Merge branch 'forward-port'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Wed, 5 Sep 2012 10:35:36 +0000 (12:35 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Wed, 5 Sep 2012 10:35:36 +0000 (12:35 +0200)
Conflicts:
planetlab/exp-tool/Makefile

578 files changed:
AUTHORS
DESIGN
FAQ [new file with mode: 0644]
INSTALL [moved from INSTALL.Linux with 88% similarity]
INSTALL.KVM
INSTALL.Libvirt
INSTALL.RHEL
INSTALL.SSL
INSTALL.XenServer
INSTALL.bridge
INSTALL.userspace
IntegrationGuide [new file with mode: 0644]
Makefile.am
NEWS
NOTICE
PORTING
README
README-gcov
SubmittingPatches
acinclude.m4
build-aux/extract-ofp-errors
build-aux/extract-ofp-msgs [new file with mode: 0755]
build-aux/sodepends.pl
build-aux/soexpand.pl
configure.ac
datapath/README
datapath/actions.c
datapath/brcompat_main.c
datapath/checksum.c
datapath/checksum.h
datapath/compat.h
datapath/datapath.c
datapath/datapath.h
datapath/dp_notify.c
datapath/dp_sysfs.h
datapath/dp_sysfs_dp.c
datapath/dp_sysfs_if.c
datapath/flow.c
datapath/flow.h
datapath/genl_exec.c
datapath/genl_exec.h
datapath/linux/Modules.mk
datapath/linux/compat/include/linux/kernel.h
datapath/linux/compat/include/linux/net.h [new file with mode: 0644]
datapath/linux/compat/include/linux/skbuff.h
datapath/linux/compat/include/linux/workqueue.h
datapath/linux/compat/workqueue.c
datapath/tunnel.c
datapath/tunnel.h
datapath/vlan.c
datapath/vlan.h
datapath/vport-capwap.c
datapath/vport-capwap.h
datapath/vport-generic.c
datapath/vport-generic.h
datapath/vport-gre.c
datapath/vport-internal_dev.c
datapath/vport-internal_dev.h
datapath/vport-netdev.c
datapath/vport-netdev.h
datapath/vport-patch.c
datapath/vport.c
datapath/vport.h
debian/automake.mk
debian/changelog
debian/control
debian/copyright.in
debian/dkms.conf.in
debian/ifupdown.sh [new file with mode: 0755]
debian/openvswitch-common.docs [new file with mode: 0644]
debian/openvswitch-common.install
debian/openvswitch-controller.init
debian/openvswitch-controller.postrm [new file with mode: 0755]
debian/openvswitch-datapath-source.copyright
debian/openvswitch-switch.README.Debian
debian/openvswitch-switch.init
debian/openvswitch-switch.install
debian/openvswitch-switch.links [new file with mode: 0644]
debian/openvswitch-switch.logrotate
debian/openvswitch-switch.postrm
debian/openvswitch-switch.template
debian/openvswitch-test.install
debian/openvswitch-test.manpages
debian/ovs-monitor-ipsec
debian/rules
include/linux/openvswitch.h
include/linux/types.h
include/openflow/automake.mk
include/openflow/nicira-ext.h
include/openflow/openflow-1.0.h
include/openflow/openflow-1.1.h
include/openflow/openflow-1.2.h
include/openflow/openflow-common.h
include/openflow/openflow.h
include/openvswitch/brcompat-netlink.h
include/openvswitch/datapath-compat.h
include/openvswitch/tunnel.h
include/openvswitch/types.h
include/sparse/arpa/inet.h
include/sparse/assert.h
include/sparse/math.h
include/sparse/netinet/in.h
include/sparse/netinet/ip6.h
include/sparse/sys/socket.h
include/sparse/sys/wait.h
lib/.gitignore
lib/aes128.c
lib/aes128.h
lib/automake.mk
lib/autopath.c
lib/autopath.h
lib/backtrace.c
lib/backtrace.h
lib/bitmap.c
lib/bitmap.h
lib/bond.c
lib/bond.h
lib/bundle.c
lib/bundle.h
lib/byte-order.h
lib/byteq.c
lib/byteq.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/coverage.c
lib/coverage.h
lib/csum.c
lib/csum.h
lib/daemon.c
lib/daemon.h
lib/dhcp.h
lib/dhparams.h
lib/dirs.c.in
lib/dirs.h
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/dynamic-string.h
lib/entropy.c
lib/entropy.h
lib/fatal-signal.c
lib/fatal-signal.h
lib/flow.c
lib/flow.h
lib/hash.c
lib/hash.h
lib/heap.c
lib/heap.h
lib/hmap.c
lib/hmap.h
lib/hmapx.c
lib/hmapx.h
lib/json.c
lib/json.h
lib/jsonrpc.c
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
lib/learning-switch.h
lib/list.c
lib/list.h
lib/lockfile.c
lib/lockfile.h
lib/mac-learning.c
lib/mac-learning.h
lib/memory-unixctl.man [new file with mode: 0644]
lib/memory.c [new file with mode: 0644]
lib/memory.h [new file with mode: 0644]
lib/meta-flow.c
lib/meta-flow.h
lib/multipath.c
lib/multipath.h
lib/netdev-bsd.c [new file with mode: 0644]
lib/netdev-dummy.c
lib/netdev-linux.c
lib/netdev-linux.h
lib/netdev-provider.h
lib/netdev-tunnel.c
lib/netdev-vport.c
lib/netdev-vport.h
lib/netdev.c
lib/netdev.h
lib/netflow.h
lib/netlink-notifier.c
lib/netlink-notifier.h
lib/netlink-protocol.h
lib/netlink-socket.c
lib/netlink-socket.h
lib/netlink.c
lib/netlink.h
lib/nx-match.c
lib/nx-match.h
lib/odp-util.c
lib/odp-util.h
lib/ofp-actions.c [new file with mode: 0644]
lib/ofp-actions.h [new file with mode: 0644]
lib/ofp-errors.c
lib/ofp-errors.h
lib/ofp-msgs.c [new file with mode: 0644]
lib/ofp-msgs.h [new file with mode: 0644]
lib/ofp-parse.c
lib/ofp-parse.h
lib/ofp-print.c
lib/ofp-print.h
lib/ofp-util.c
lib/ofp-util.def
lib/ofp-util.h
lib/ofpbuf.c
lib/ofpbuf.h
lib/ovsdb-data.c
lib/ovsdb-data.h
lib/ovsdb-error.c
lib/ovsdb-error.h
lib/ovsdb-idl-provider.h
lib/ovsdb-idl.c
lib/ovsdb-idl.h
lib/ovsdb-parser.c
lib/ovsdb-parser.h
lib/ovsdb-types.c
lib/ovsdb-types.h
lib/packets.c
lib/packets.h
lib/pcap.c
lib/pcap.h
lib/poll-loop.c
lib/poll-loop.h
lib/process.c
lib/process.h
lib/random.c
lib/random.h
lib/rconn.c
lib/rconn.h
lib/reconnect.c
lib/reconnect.h
lib/route-table-bsd.c [new file with mode: 0644]
lib/route-table.c
lib/route-table.h
lib/rtbsd.c [new file with mode: 0644]
lib/rtbsd.h [new file with mode: 0644]
lib/rtnetlink-link.c
lib/rtnetlink-link.h
lib/sat-math.h
lib/sha1.c
lib/sha1.h
lib/shash.c
lib/shash.h
lib/signals.c
lib/signals.h
lib/simap.c [new file with mode: 0644]
lib/simap.h [new file with mode: 0644]
lib/smap.c [new file with mode: 0644]
lib/smap.h [new file with mode: 0644]
lib/socket-util.c
lib/socket-util.h
lib/sort.c
lib/sort.h
lib/sset.c
lib/sset.h
lib/stp.c
lib/stp.h
lib/stream-fd.c
lib/stream-fd.h
lib/stream-nossl.c
lib/stream-provider.h
lib/stream-ssl.c
lib/stream-ssl.h
lib/stream-tcp.c
lib/stream-unix.c
lib/stream.c
lib/stream.h
lib/stress.c
lib/stress.h
lib/string.c
lib/string.h
lib/svec.c
lib/svec.h
lib/table.c
lib/table.h
lib/tag.c
lib/tag.h
lib/timer.c
lib/timer.h
lib/timeval.c
lib/timeval.h
lib/token-bucket.c [new file with mode: 0644]
lib/token-bucket.h [new file with mode: 0644]
lib/type-props.h
lib/unaligned.h
lib/unicode.c
lib/unicode.h
lib/unixctl.c
lib/unixctl.h
lib/util.c
lib/util.h
lib/uuid.c
lib/uuid.h
lib/valgrind.h
lib/vconn-provider.h
lib/vconn-stream.c
lib/vconn.c
lib/vconn.h
lib/vlan-bitmap.c
lib/vlan-bitmap.h
lib/vlandev.c
lib/vlandev.h
lib/vlog.c
lib/vlog.h
lib/vlog.man
lib/worker.c [new file with mode: 0644]
lib/worker.h [new file with mode: 0644]
m4/openvswitch.m4
manpages.mk
ofproto/automake.mk
ofproto/collectors.c
ofproto/collectors.h
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/fail-open.c
ofproto/fail-open.h
ofproto/in-band.c
ofproto/in-band.h
ofproto/names.c
ofproto/netflow.c
ofproto/netflow.h
ofproto/ofproto-dpif-governor.c
ofproto/ofproto-dpif-governor.h
ofproto/ofproto-dpif-sflow.c
ofproto/ofproto-dpif-sflow.h
ofproto/ofproto-dpif.c
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
ovsdb/column.c
ovsdb/column.h
ovsdb/condition.c
ovsdb/condition.h
ovsdb/dot2pic
ovsdb/execution.c
ovsdb/file.c
ovsdb/file.h
ovsdb/jsonrpc-server.c
ovsdb/jsonrpc-server.h
ovsdb/log.c
ovsdb/log.h
ovsdb/mutation.c
ovsdb/mutation.h
ovsdb/ovsdb-client.1.in
ovsdb/ovsdb-client.c
ovsdb/ovsdb-idlc.in
ovsdb/ovsdb-server.1.in
ovsdb/ovsdb-server.c
ovsdb/ovsdb-tool.1.in
ovsdb/ovsdb-tool.c
ovsdb/ovsdb.c
ovsdb/ovsdb.h
ovsdb/ovsdbmonitor/OVEConfig.py
ovsdb/ovsdbmonitor/OVEFetch.py
ovsdb/ovsdbmonitor/OVEFlowWindow.py
ovsdb/ovsdbmonitor/OVEStandard.py
ovsdb/ovsdbmonitor/OVEUtil.py
ovsdb/query.c
ovsdb/query.h
ovsdb/row.c
ovsdb/row.h
ovsdb/server.c
ovsdb/server.h
ovsdb/table.c
ovsdb/table.h
ovsdb/transaction.c
ovsdb/transaction.h
ovsdb/trigger.c
ovsdb/trigger.h
planetlab/exp-tool/Makefile
python/automake.mk
python/ovs/daemon.py
python/ovs/db/data.py
python/ovs/db/error.py
python/ovs/db/idl.py
python/ovs/db/parser.py
python/ovs/db/schema.py
python/ovs/db/types.py
python/ovs/dirs.py
python/ovs/dirs.py.template [new file with mode: 0644]
python/ovs/fatal_signal.py
python/ovs/json.py
python/ovs/jsonrpc.py
python/ovs/ovsuuid.py
python/ovs/poller.py
python/ovs/process.py
python/ovs/reconnect.py
python/ovs/socket_util.py
python/ovs/stream.py
python/ovs/timeval.py
python/ovs/unixctl/__init__.py [new file with mode: 0644]
python/ovs/unixctl/client.py [new file with mode: 0644]
python/ovs/unixctl/server.py [moved from python/ovs/unixctl.py with 74% similarity]
python/ovs/util.py
python/ovs/vlog.py
python/ovstest/args.py
python/ovstest/rpcserver.py
python/ovstest/tcp.py
python/ovstest/tests.py [new file with mode: 0644]
python/ovstest/udp.py
python/ovstest/util.py
python/ovstest/vswitch.py
rhel/automake.mk
rhel/etc_init.d_openvswitch
rhel/etc_logrotate.d_openvswitch
rhel/etc_sysconfig_network-scripts_ifdown-ovs
rhel/etc_sysconfig_network-scripts_ifup-ovs
rhel/openvswitch-kmod-rhel5.spec.in
rhel/openvswitch-kmod-rhel6.spec.in
rhel/openvswitch.spec.in
rhel/usr_share_openvswitch_scripts_sysconfig.template
tests/MockXenAPI.py
tests/appctl.py
tests/atlocal.in
tests/automake.mk
tests/autopath.at
tests/daemon.at
tests/file_name.at
tests/flowgen.pl
tests/interface-reconfigure.at
tests/jsonrpc.at
tests/lacp.at
tests/lcov-wrapper.in [deleted file]
tests/learn.at
tests/library.at
tests/lockfile.at
tests/odp.at
tests/ofp-actions.at [new file with mode: 0644]
tests/ofp-errors.at
tests/ofp-print.at
tests/ofproto-dpif.at
tests/ofproto-macros.at
tests/ofproto.at
tests/ovs-monitor-ipsec.at
tests/ovs-ofctl.at
tests/ovs-vsctl.at
tests/ovs-xapi-sync.at
tests/ovsdb-idl.at
tests/ovsdb-monitor.at
tests/ovsdb-server.at
tests/ovsdb-tool.at
tests/test-aes128.c
tests/test-bundle.c
tests/test-byte-order.c
tests/test-classifier.c
tests/test-csum.c
tests/test-daemon.py
tests/test-file_name.c
tests/test-flows.c
tests/test-hash.c
tests/test-heap.c
tests/test-hmap.c
tests/test-json.c
tests/test-json.py
tests/test-jsonrpc.c
tests/test-jsonrpc.py
tests/test-list.c
tests/test-lockfile.c
tests/test-multipath.c
tests/test-netflow.c
tests/test-odp.c
tests/test-ovsdb.c
tests/test-ovsdb.py
tests/test-packets.c
tests/test-random.c
tests/test-reconnect.c
tests/test-reconnect.py
tests/test-sha1.c
tests/test-stp.c
tests/test-strtok_r.c
tests/test-timeval.c
tests/test-type-props.c
tests/test-unix-socket.c
tests/test-unixctl.py
tests/test-util.c
tests/test-uuid.c
tests/test-vconn.c
tests/test-vlog.py
tests/testsuite.at
tests/unixctl-py.at
tests/uuid.at
tests/vlog.at
third-party/ofp-tcpdump.patch
utilities/.gitignore
utilities/automake.mk
utilities/bugtool/automake.mk
utilities/bugtool/ovs-bugtool-bond-show [new file with mode: 0755]
utilities/bugtool/ovs-bugtool-cfm-show
utilities/bugtool/ovs-bugtool-coverage-show [new file with mode: 0755]
utilities/bugtool/ovs-bugtool-daemons-ver
utilities/bugtool/ovs-bugtool-lacp-show
utilities/bugtool/ovs-bugtool-memory-show [new file with mode: 0755]
utilities/bugtool/ovs-bugtool-ovsdb-dump
utilities/bugtool/ovs-bugtool-tc-class-show
utilities/bugtool/ovs-bugtool-vsctl-show [new file with mode: 0755]
utilities/bugtool/ovs-bugtool.8
utilities/bugtool/ovs-bugtool.in
utilities/bugtool/plugins/kernel-info/openvswitch.xml
utilities/bugtool/plugins/network-status/openvswitch.xml
utilities/bugtool/plugins/system-configuration.xml
utilities/bugtool/plugins/system-configuration/openvswitch.xml
utilities/bugtool/plugins/system-logs/openvswitch.xml
utilities/nlmon.c
utilities/ovs-appctl.8.in
utilities/ovs-appctl.c
utilities/ovs-benchmark.c
utilities/ovs-check-dead-ifs.in [new file with mode: 0755]
utilities/ovs-controller.c
utilities/ovs-ctl.8
utilities/ovs-ctl.in
utilities/ovs-dpctl.8.in
utilities/ovs-dpctl.c
utilities/ovs-l3ping.8.in [new file with mode: 0644]
utilities/ovs-l3ping.in [new file with mode: 0644]
utilities/ovs-lib.in
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-parse-leaks.in
utilities/ovs-pcap.in
utilities/ovs-pki-cgi.in [deleted file]
utilities/ovs-pki.8.in
utilities/ovs-pki.in
utilities/ovs-save
utilities/ovs-tcpundump.in
utilities/ovs-test.in
utilities/ovs-vlan-bug-workaround.c
utilities/ovs-vlan-test.in
utilities/ovs-vsctl.8.in
utilities/ovs-vsctl.c
vswitchd/INTERNALS
vswitchd/bridge.c
vswitchd/bridge.h
vswitchd/ovs-brcompatd.c
vswitchd/ovs-vswitchd.8.in
vswitchd/ovs-vswitchd.c
vswitchd/system-stats.c
vswitchd/system-stats.h
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml
vswitchd/xenserver.c
vswitchd/xenserver.h
xenserver/README
xenserver/automake.mk
xenserver/etc_init.d_openvswitch
xenserver/etc_init.d_openvswitch-xapi-update
xenserver/etc_logrotate.d_openvswitch
xenserver/etc_profile.d_openvswitch.sh
xenserver/etc_xapi.d_plugins_openvswitch-cfg-update
xenserver/openvswitch-xen.spec.in
xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py
xenserver/opt_xensource_libexec_interface-reconfigure
xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py
xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync
xenserver/usr_share_openvswitch_scripts_sysconfig.template

diff --git a/AUTHORS b/AUTHORS
index 920f4e4..28dc742 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -8,6 +8,8 @@ Andrew Lambeth          wal@nicira.com
 Andy Southgate          andy.southgate@citrix.com
 Arun Sharma             arun.sharma@calsoftinc.com
 Ben Pfaff               blp@nicira.com
+Brian Kruger            bkruger+ovsdev@gmail.com
+Bruce Davie             bsd@nicira.com
 Bryan Phillippe         bp@toroki.com
 Casey Barker            crbarker@google.com
 Chris Wright            chrisw@sous-sol.org
@@ -20,22 +22,29 @@ David Erickson          derickso@stanford.edu
 David S. Miller         davem@davemloft.net
 Devendra Naga           devendra.aaru@gmail.com
 Dominic Curran          dominic.curran@citrix.com
+Ed Maste                emaste at freebsd.org
 Edward Tomasz NapieraÅ‚a trasz@freebsd.org
 Ethan Jackson           ethan@nicira.com
 Gaetano Catalli         gaetano.catalli@gmail.com
+Giuseppe Lettieri       g.lettieri@iet.unipi.it
 Glen Gibb               grg@stanford.edu
 Gurucharan Shetty       gshetty@nicira.com
 Hao Zheng               hzheng@nicira.com
 Ian Campbell            Ian.Campbell@citrix.com
+Isaku Yamahata          yamahata@valinux.co.jp
 Jean Tourrilhes         jt@hpl.hp.com
 Jeremy Stribling        strib@nicira.com
 Jesse Gross             jesse@nicira.com
 Joe Perches             joe@perches.com
+Joe Stringer            joe@wand.net.nz
 Jun Nakajima            jun.nakajima@intel.com
 Justin Pettit           jpettit@nicira.com
 Keith Amidon            keith@nicira.com
+Kyle Mestery            kmestery@cisco.com
+Leo Alterman            lalterman@nicira.com
 Luca Giraudo            lgiraudo@nicira.com
 Martin Casado           casado@nicira.com
+Mehak Mahajan           mmahajan@nicira.com
 Natasha Gude            natasha@nicira.com
 Neil McKee              neil.mckee@inmon.com
 Paul Fazzone            pfazzone@nicira.com
@@ -51,6 +60,7 @@ Sanjay Sane             ssane@nicira.com
 Shan Wei                davidshan@tencent.com
 Shih-Hao Li             shli@nicira.com
 Simon Horman            horms@verge.net.au
+Tadaaki Nagao           nagao@stratosphere.co.jp
 Tetsuo NAKAGAWA         nakagawa@mxc.nes.nec.co.jp
 Thomas Goirand          zigo@debian.org
 Thomas Lacroix          thomas.lacroix@citrix.com
@@ -74,7 +84,11 @@ Alan Shieh              ashieh@nicira.com
 Alban Browaeys          prahal@yahoo.com
 Alex Yip                alex@nicira.com
 Alexey I. Froloff       raorn@altlinux.org
+Amey Bhide              abhide@nicira.com
+André Ruß               andre.russ@hybris.com
 Andreas Beckmann        debian@abeckmann.de
+Atzm Watanabe           atzm@stratosphere.co.jp
+Bastian Blank           waldi@debian.org
 Ben Basler              bbasler@nicira.com
 Bob Ball                bob.ball@citrix.com
 Brad Hall               brad@nicira.com
@@ -90,16 +104,19 @@ DK Moon                 dkmoon@nicira.com
 Edwin Chiu              echiu@nicira.com
 Eivind Bulie Haanaes
 Eric Lopez              elopez@nicira.com
+Frido Roose             fr.roose@gmail.com
 Gaetano Catalli         gaetano.catalli@gmail.com
 George Shuklin          amarao@desunote.ru
 Ghanem Bahri            bahri.ghanem@gmail.com
 Giuseppe de Candia      giuseppe.decandia@gmail.com
 Gordon Good             ggood@nicira.com
+Greg Dahlman            gdahlman@hotmail.com
 Gregor Schaffrath       grsch@net.t-labs.tu-berlin.de
 Hassan Khan             hassan.khan@seecs.edu.pk
 Hector Oron             hector.oron@gmail.com
 Henrik Amren            henrik@nicira.com
 Hiroshi Tanaka          htanaka@nicira.com
+Igor Ganichev           iganichev@nicira.com
 Jacob Cherkas           jcherkas@nicira.com
 Jad Naous               jnaous@gmail.com
 Jamal Hadi Salim        hadi@cyberus.ca
@@ -111,7 +128,11 @@ Jed Daniels             openvswitch@jeddaniels.com
 Jeongkeun Lee           jklee@hp.com
 Joan Cirer              joan@ev0.net
 John Galgay             john@galgay.net
+Kevin Mancuso           kevin.mancuso@rackspace.com
+Kirill Kabardin
 Koichi Yagishita        yagishita.koichi@jrc.co.jp
+Konstantin Khorenko     khorenko@openvz.org
+Kris zhang              zhang.kris@gmail.com
 Krishna Miriyala        krishna@nicira.com
 Luca Falavigna          dktrkranz@debian.org
 Luiz Henrique Ozaki     luiz.ozaki@gmail.com
@@ -130,10 +151,15 @@ Paul Ingram             paul@nicira.com
 Paulo Cravero           pcravero@as2594.net
 Peter Balland           peter@nicira.com
 Peter Phaal             peter.phaal@inmon.com
+Prabina Pattnaik        Prabina.Pattnaik@nechclst.in
+Pratap Reddy            preddy@nicira.com
+Ralf Heiringhoff        ralf@frosty-geek.net
 Ram Jothikumar          rjothikumar@nicira.com
 Ramana Reddy            gtvrreddy@gmail.com
+Rob Sherwood            rob.sherwood@bigswitch.com
 Roger Leigh             rleigh@codelibre.net
 Rogério Vinhal Nunes
+Scott Hendricks         shendricks@nicira.com
 Sean Brady              sbrady@gtfservices.com
 Sebastian Andrzej Siewior  sebastian@breakpoint.cc
 Sébastien RICCIO        sr@swisscenter.com
@@ -143,6 +169,7 @@ Takayuki HAMA           t-hama@cb.jp.nec.com
 Teemu Koponen           koponen@nicira.com
 Timothy Chen            tchen@nicira.com
 Vishal Swarankar        vishal.swarnkar@gmail.com
+Vjekoslav Brajkovic     balkan@cs.washington.edu
 Voravit T.              voravit@kth.se
 YAMAMOTO Takashi        yamamoto@valinux.co.jp
 Yongqiang Liu           liuyq7809@gmail.com
diff --git a/DESIGN b/DESIGN
index e43878b..f3e9309 100644 (file)
--- a/DESIGN
+++ b/DESIGN
@@ -17,8 +17,14 @@ given controller receives OpenFlow asynchronous messages.  This
 section describes how all of these features interact.
 
 First, a service controller never receives any asynchronous messages
-unless it explicitly configures a miss_send_len greater than zero with
-an OFPT_SET_CONFIG message.
+unless it changes its miss_send_len from the service controller
+default of zero in one of the following ways:
+
+    - Sending an OFPT_SET_CONFIG message with nonzero miss_send_len.
+
+    - Sending any NXT_SET_ASYNC_CONFIG message: as a side effect, this
+      message changes the miss_send_len to
+      OFP_DEFAULT_MISS_SEND_LEN (128) for service controllers.
 
 Second, OFPT_FLOW_REMOVED and NXT_FLOW_REMOVED messages are generated
 only if the flow that was removed had the OFPFF_SEND_FLOW_REM flag
@@ -146,6 +152,206 @@ sends flow_removed message   ---    ---     ---      %       %
     receive the generated messages.)
 
 
+VLAN Matching
+=============
+
+The 802.1Q VLAN header causes more trouble than any other 4 bytes in
+networking.  More specifically, three versions of OpenFlow and Open
+vSwitch have among them four different ways to match the contents and
+presence of the VLAN header.  The following table describes how each
+version works.
+
+       Match        NXM        OF1.0        OF1.1         OF1.2
+       -----  ---------  -----------  -----------  ------------
+         [1]  0000/0000  ????/1,??/?  ????/1,??/?  0000/0000,--
+         [2]  0000/ffff  ffff/0,??/?  ffff/0,??/?  0000/ffff,--
+         [3]  1xxx/1fff  0xxx/0,??/1  0xxx/0,??/1  1xxx/ffff,--
+         [4]  z000/f000  ????/1,0y/0  fffe/0,0y/0  1000/1000,0y
+         [5]  zxxx/ffff  0xxx/0,0y/0  0xxx/0,0y/0  1xxx/ffff,0y
+         [6]  0000/0fff    <none>       <none>        <none>
+         [7]  0000/f000    <none>       <none>        <none>
+         [8]  0000/efff    <none>       <none>        <none>
+         [9]  1001/1001    <none>       <none>     1001/1001,--
+        [10]  3000/3000    <none>       <none>        <none>
+
+Each column is interpreted as follows.
+
+    - Match: See the list below.
+
+    - NXM: xxxx/yyyy means NXM_OF_VLAN_TCI_W with value xxxx and mask
+      yyyy.  A mask of 0000 is equivalent to omitting
+      NXM_OF_VLAN_TCI(_W), a mask of ffff is equivalent to
+      NXM_OF_VLAN_TCI.
+
+    - OF1.0 and OF1.1: wwww/x,yy/z means dl_vlan wwww, OFPFW_DL_VLAN
+      x, dl_vlan_pcp yy, and OFPFW_DL_VLAN_PCP z.  ? means that the
+      given nibble is ignored (and conventionally 0 for wwww or z,
+      conventionally 1 for x or z).  <none> means that the given match
+      is not supported.
+
+    - OF1.2: xxxx/yyyy,zz means OXM_OF_VLAN_VID_W with value xxxx and
+      mask yyyy, and OXM_OF_VLAN_PCP (which is not maskable) with
+      value zz.  A mask of 0000 is equivalent to omitting
+      OXM_OF_VLAN_VID(_W), a mask of ffff is equivalent to
+      OXM_OF_VLAN_VID.  -- means that OXM_OF_VLAN_PCP is omitted.
+      <none> means that the given match is not supported.
+
+The matches are:
+
+ [1] Matches any packet, that is, one without an 802.1Q header or with
+     an 802.1Q header with any TCI value.
+
+ [2] Matches only packets without an 802.1Q header.
+
+     NXM: Any match with (vlan_tci == 0) and (vlan_tci_mask & 0x1000)
+     != 0 is equivalent to the one listed in the table.
+
+     OF1.0: The spec doesn't define behavior if dl_vlan is set to
+     0xffff and OFPFW_DL_VLAN_PCP is not set.
+
+     OF1.1: The spec says explicitly to ignore dl_vlan_pcp when
+     dl_vlan is set to 0xffff.
+
+     OF1.2: The spec doesn't say what should happen if (vlan_vid == 0)
+     and (vlan_vid_mask & 0x1000) != 0 but (vlan_vid_mask != 0x1000),
+     but it would be straightforward to also interpret as [2].
+
+ [3] Matches only packets that have an 802.1Q header with VID xxx (and
+     any PCP).
+
+ [4] Matches only packets that have an 802.1Q header with PCP y (and
+     any VID).
+
+     NXM: z is ((y << 1) | 1).
+
+     OF1.0: The spec isn't very clear, but OVS implements it this way.
+
+     OF1.2: Presumably other masks such that (vlan_vid_mask & 0x1fff)
+     == 0x1000 would also work, but the spec doesn't define their
+     behavior.
+
+ [5] Matches only packets that have an 802.1Q header with VID xxx and
+     PCP y.
+
+     NXM: z is ((y << 1) | 1).
+
+     OF1.2: Presumably other masks such that (vlan_vid_mask & 0x1fff)
+     == 0x1fff would also work.
+
+ [6] Matches packets with no 802.1Q header or with an 802.1Q header
+     with a VID of 0.  Only possible with NXM.
+
+ [7] Matches packets with no 802.1Q header or with an 802.1Q header
+     with a PCP of 0.  Only possible with NXM.
+
+ [8] Matches packets with no 802.1Q header or with an 802.1Q header
+     with both VID and PCP of 0.  Only possible with NXM.
+
+ [9] Matches only packets that have an 802.1Q header with an
+     odd-numbered VID (and any PCP).  Only possible with NXM and
+     OF1.2.  (This is just an example; one can match on any desired
+     VID bit pattern.)
+
+[10] Matches only packets that have an 802.1Q header with an
+     odd-numbered PCP (and any VID).  Only possible with NXM.  (This
+     is just an example; one can match on any desired VID bit
+     pattern.)
+
+Additional notes:
+
+    - OF1.2: The top three bits of OXM_OF_VLAN_VID are fixed to zero,
+      so bits 13, 14, and 15 in the masks listed in the table may be
+      set to arbitrary values, as long as the corresponding value bits
+      are also zero.  The suggested ffff mask for [2], [3], and [5]
+      allows a shorter OXM representation (the mask is omitted) than
+      the minimal 1fff mask.
+
+
+Flow Cookies
+============
+
+OpenFlow 1.0 and later versions have the concept of a "flow cookie",
+which is a 64-bit integer value attached to each flow.  The treatment
+of the flow cookie has varied greatly across OpenFlow versions,
+however.
+
+In OpenFlow 1.0:
+
+        - OFPFC_ADD set the cookie in the flow that it added.
+
+        - OFPFC_MODIFY and OFPFC_MODIFY_STRICT updated the cookie for
+          the flow or flows that it modified.
+
+        - OFPST_FLOW messages included the flow cookie.
+
+        - OFPT_FLOW_REMOVED messages reported the cookie of the flow
+          that was removed.
+
+OpenFlow 1.1 made the following changes:
+
+        - Flow mod operations OFPFC_MODIFY, OFPFC_MODIFY_STRICT,
+          OFPFC_DELETE, and OFPFC_DELETE_STRICT, plus flow stats
+          requests and aggregate stats requests, gained the ability to
+          match on flow cookies with an arbitrary mask.
+
+        - OFPFC_MODIFY and OFPFC_MODIFY_STRICT were changed to add a
+          new flow, in the case of no match, only if the flow table
+          modification operation did not match on the cookie field.
+          (In OpenFlow 1.0, modify operations always added a new flow
+          when there was no match.)
+
+        - OFPFC_MODIFY and OFPFC_MODIFY_STRICT no longer updated flow
+          cookies.
+
+OpenFlow 1.2 made the following changes:
+
+        - OFPC_MODIFY and OFPFC_MODIFY_STRICT were changed to never
+          add a new flow, regardless of whether the flow cookie was
+          used for matching.
+
+Open vSwitch support for OpenFlow 1.0 implements the OpenFlow 1.0
+behavior with the following extensions:
+
+        - An NXM extension field NXM_NX_COOKIE(_W) allows the NXM
+          versions of OFPFC_MODIFY, OFPFC_MODIFY_STRICT, OFPFC_DELETE,
+          and OFPFC_DELETE_STRICT flow_mods, plus flow stats requests
+          and aggregate stats requests, to match on flow cookies with
+          arbitrary masks.  This is much like the equivalent OpenFlow
+          1.1 feature.
+
+        - Like OpenFlow 1.1, OFPC_MODIFY and OFPFC_MODIFY_STRICT add a
+          new flow if there is no match and the mask is zero (or not
+          given).
+
+        - The "cookie" field in OFPT_FLOW_MOD and NXT_FLOW_MOD messages
+          is used as the cookie value for OFPFC_ADD commands, as
+          described in OpenFlow 1.0.  For OFPFC_MODIFY and
+          OFPFC_MODIFY_STRICT commands, the "cookie" field is used as a
+          new cookie for flows that match unless it is UINT64_MAX, in
+          which case the flow's cookie is not updated.
+
+        - NXT_PACKET_IN (the Nicira extended version of
+          OFPT_PACKET_IN) reports the cookie of the rule that
+          generated the packet, or all-1-bits if no rule generated the
+          packet.  (Older versions of OVS used all-0-bits instead of
+          all-1-bits.)
+
+The following table shows the handling of different protocols when
+receiving OFPFC_MODIFY and OFPFC_MODIFY_STRICT messages.  A mask of 0
+indicates either an explicit mask of zero or an implicit one by not
+specifying the NXM_NX_COOKIE(_W) field.
+
+                Match   Update   Add on miss   Add on miss
+                cookie  cookie     mask!=0       mask==0
+                ======  ======   ===========   ===========
+OpenFlow 1.0      no     yes        <always add on miss>
+OpenFlow 1.1     yes      no          no           yes
+OpenFlow 1.2     yes      no          no            no
+NXM              yes     yes*         no           yes
+
+* Updates the flow's cookie unless the "cookie" field is UINT64_MAX.
+
+
 Multiple Table Support
 ======================
 
@@ -521,6 +727,39 @@ The following are explicitly *not* supported by in-band control:
      gateway.
 
 
+Action Reproduction
+===================
+
+It seems likely that many controllers, at least at startup, use the
+OpenFlow "flow statistics" request to obtain existing flows, then
+compare the flows' actions against the actions that they expect to
+find.  Before version 1.8.0, Open vSwitch always returned exact,
+byte-for-byte copies of the actions that had been added to the flow
+table.  The current version of Open vSwitch does not always do this in
+some exceptional cases.  This section lists the exceptions that
+controller authors must keep in mind if they compare actual actions
+against desired actions in a bytewise fashion:
+
+        - Open vSwitch zeros padding bytes in action structures,
+          regardless of their values when the flows were added.
+
+        - Open vSwitch "normalizes" the instructions in OpenFlow 1.1
+          (and later) in the following way:
+
+              * OVS sorts the instructions into the following order:
+                Apply-Actions, Clear-Actions, Write-Actions,
+                Write-Metadata, Goto-Table.
+
+              * OVS drops Apply-Actions instructions that have empty
+                action lists.
+
+              * OVS drops Write-Actions instructions that have empty
+                action sets.
+
+Please report other discrepancies, if you notice any, so that we can
+fix or document them.
+
+
 Suggestions
 ===========
 
diff --git a/FAQ b/FAQ
new file mode 100644 (file)
index 0000000..ae904f1
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,747 @@
+                 Open vSwitch <http://openvswitch.org>
+
+Frequently Asked Questions
+==========================
+
+General
+-------
+
+Q: What is Open vSwitch?
+
+A: Open vSwitch is a production quality open source software switch
+   designed to be used as a vswitch in virtualized server environments.  A
+   vswitch forwards traffic between different VMs on the same physical host
+   and also forwards traffic between VMs and the physical network.  Open
+   vSwitch supports standard management interfaces (e.g. sFlow, NetFlow,
+   RSPAN, CLI), and is open to programmatic extension and control using
+   OpenFlow and the OVSDB management protocol.
+
+   Open vSwitch as designed to be compatible with modern switching
+   chipsets.  This means that it can be ported to existing high-fanout
+   switches allowing the same flexible control of the physical
+   infrastructure as the virtual infrastructure.  It also means that
+   Open vSwitch will be able to take advantage of on-NIC switching
+   chipsets as their functionality matures.
+
+Q: What virtualization platforms can use Open vSwitch?
+
+A: Open vSwitch can currently run on any Linux-based virtualization
+   platform (kernel 2.6.18 and newer), including: KVM, VirtualBox, Xen,
+   Xen Cloud Platform, XenServer. As of Linux 3.3 it is part of the
+   mainline kernel.  The bulk of the code is written in platform-
+   independent C and is easily ported to other environments.  We welcome
+   inquires about integrating Open vSwitch with other virtualization
+   platforms.
+
+Q: How can I try Open vSwitch?
+
+A: The Open vSwitch source code can be built on a Linux system.  You can
+   build and experiment with Open vSwitch on any Linux machine.
+   Packages for various Linux distributions are available on many
+   platforms, including: Debian, Ubuntu, Fedora.
+
+   You may also download and run a virtualization platform that already
+   has Open vSwitch integrated.  For example, download a recent ISO for
+   XenServer or Xen Cloud Platform.  Be aware that the version
+   integrated with a particular platform may not be the most recent Open
+   vSwitch release.
+
+Q: Does Open vSwitch only work on Linux?
+
+A: No, Open vSwitch has been ported to a number of different operating
+   systems and hardware platforms.  Most of the development work occurs
+   on Linux, but the code should be portable to any POSIX system.  We've
+   seen Open vSwitch ported to a number of different platforms,
+   including FreeBSD, Windows, and even non-POSIX embedded systems.
+
+   By definition, the Open vSwitch Linux kernel module only works on
+   Linux and will provide the highest performance.  However, a userspace
+   datapath is available that should be very portable.
+
+Q: What's involved with porting Open vSwitch to a new platform or
+   switching ASIC?
+
+A: The PORTING document describes how one would go about porting Open
+   vSwitch to a new operating system or hardware platform.
+
+Q: Why would I use Open vSwitch instead of the Linux bridge?
+
+A: Open vSwitch is specially designed to make it easier to manage VM
+   network configuration and monitor state spread across many physical
+   hosts in dynamic virtualized environments.  Please see WHY-OVS for a
+   more detailed description of how Open vSwitch relates to the Linux
+   Bridge.
+
+Q: How is Open vSwitch related to distributed virtual switches like the
+   VMware vNetwork distributed switch or the Cisco Nexus 1000V?
+
+A: Distributed vswitch applications (e.g., VMware vNetwork distributed
+   switch, Cisco Nexus 1000V) provide a centralized way to configure and
+   monitor the network state of VMs that are spread across many physical
+   hosts.  Open vSwitch is not a distributed vswitch itself, rather it
+   runs on each physical host and supports remote management in a way
+   that makes it easier for developers of virtualization/cloud
+   management platforms to offer distributed vswitch capabilities.
+
+   To aid in distribution, Open vSwitch provides two open protocols that
+   are specially designed for remote management in virtualized network
+   environments: OpenFlow, which exposes flow-based forwarding state,
+   and the OVSDB management protocol, which exposes switch port state.
+   In addition to the switch implementation itself, Open vSwitch
+   includes tools (ovs-controller, ovs-ofctl, ovs-vsctl) that developers
+   can script and extend to provide distributed vswitch capabilities
+   that are closely integrated with their virtualization management
+   platform.
+
+Q: Why doesn't Open vSwitch support distribution?
+
+A: Open vSwitch is intended to be a useful component for building
+   flexible network infrastructure. There are many different approaches
+   to distribution which balance trade-offs between simplicity,
+   scalability, hardware compatibility, convergence times, logical
+   forwarding model, etc. The goal of Open vSwitch is to be able to
+   support all as a primitive building block rather than choose a
+   particular point in the distributed design space.
+
+Q: How can I contribute to the Open vSwitch Community?
+
+A: You can start by joining the mailing lists and helping to answer
+   questions.  You can also suggest improvements to documentation.  If
+   you have a feature or bug you would like to work on, send a mail to
+   one of the mailing lists:
+
+       http://openvswitch.org/mlists/
+
+
+
+Releases
+--------
+
+Q: What does it mean for an Open vSwitch release to be LTS (long-term
+   support)?
+
+A: All official releases have been through a comprehensive testing
+   process and are suitable for production use.  Planned releases will
+   occur several times a year.  If a significant bug is identified in an
+   LTS release, we will provide an updated release that includes the
+   fix.  Releases that are not LTS may not be fixed and may just be
+   supplanted by the next major release.  The current LTS release is
+   1.4.x.
+
+Q: What features are not available in the Open vSwitch kernel datapath
+   that ships as part of the upstream Linux kernel?
+
+A: The kernel module in upstream Linux 3.3 and later does not include
+   the following features:
+
+       - Bridge compatibility, that is, support for the ovs-brcompatd
+         daemon that (if you enable it) lets "brctl" and other Linux
+         bridge tools transparently work with Open vSwitch instead.
+
+         We do not expect bridge compatibility to ever be available in
+         upstream Linux.  If you need bridge compatibility, use the
+         kernel module from the Open vSwitch distribution instead of the
+         upstream Linux kernel module.
+
+       - Tunnel virtual ports, that is, interfaces with type "gre",
+         "ipsec_gre", "capwap".  It is possible to create tunnels in
+         Linux and attach them to Open vSwitch as system devices.
+         However, they cannot be dynamically created through the OVSDB
+         protocol or set the tunnel ids as a flow action.
+
+         Work is in progress in adding these features to the upstream
+         Linux version of the Open vSwitch kernel module.  For now, if
+         you need these features, use the kernel module from the Open
+         vSwitch distribution instead of the upstream Linux kernel
+         module.
+
+       - Patch virtual ports, that is, interfaces with type "patch".
+         You can use Linux "veth" devices as a substitute.
+
+         We don't have any plans to add patch ports upstream.
+
+Q: What features are not available when using the userspace datapath?
+
+A: Tunnel and patch virtual ports are not supported, as described in the
+   previous answer.  It is also not possible to use queue-related
+   actions.  On Linux kernels before 2.6.39, maximum-sized VLAN packets
+   may not be transmitted.
+
+
+Basic Configuration
+-------------------
+
+Q: How do I configure a port as an access port?
+
+A: Add "tag=VLAN" to your "ovs-vsctl add-port" command.  For example,
+   the following commands configure br0 with eth0 as a trunk port (the
+   default) and tap0 as an access port for VLAN 9:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl add-port br0 eth0
+       ovs-vsctl add-port br0 tap0 tag=9
+
+   If you want to configure an already added port as an access port,
+   use "ovs-vsctl set", e.g.:
+
+       ovs-vsctl set port tap0 tag=9
+
+Q: How do I configure a port as a SPAN port, that is, enable mirroring
+   of all traffic to that port?
+
+A: The following commands configure br0 with eth0 and tap0 as trunk
+   ports.  All traffic coming in or going out on eth0 or tap0 is also
+   mirrored to tap1; any traffic arriving on tap1 is dropped:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl add-port br0 eth0
+       ovs-vsctl add-port br0 tap0
+       ovs-vsctl add-port br0 tap1 \
+           -- --id=@p get port tap1 \
+          -- --id=@m create mirror name=m0 select-all=true output-port=@p \
+          -- set bridge br0 mirrors=@m
+
+   To later disable mirroring, run:
+
+       ovs-vsctl clear bridge br0 mirrors
+
+Q: How do I configure a VLAN as an RSPAN VLAN, that is, enable
+   mirroring of all traffic to that VLAN?
+
+A: The following commands configure br0 with eth0 as a trunk port and
+   tap0 as an access port for VLAN 10.  All traffic coming in or going
+   out on tap0, as well as traffic coming in or going out on eth0 in
+   VLAN 10, is also mirrored to VLAN 15 on eth0.  The original tag for
+   VLAN 10, in cases where one is present, is dropped as part of
+   mirroring:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl add-port br0 eth0
+       ovs-vsctl add-port br0 tap0 tag=10
+       ovs-vsctl \
+          -- --id=@m create mirror name=m0 select-all=true select-vlan=10 \
+                                    output-vlan=15 \
+          -- set bridge br0 mirrors=@m
+
+   To later disable mirroring, run:
+
+       ovs-vsctl clear bridge br0 mirrors
+
+   Mirroring to a VLAN can disrupt a network that contains unmanaged
+   switches.  See ovs-vswitchd.conf.db(5) for details.  Mirroring to a
+   GRE tunnel has fewer caveats than mirroring to a VLAN and should
+   generally be preferred.
+
+Q: Can I mirror more than one input VLAN to an RSPAN VLAN?
+
+A: Yes, but mirroring to a VLAN strips the original VLAN tag in favor
+   of the specified output-vlan.  This loss of information may make
+   the mirrored traffic too hard to interpret.
+
+   To mirror multiple VLANs, use the commands above, but specify a
+   comma-separated list of VLANs as the value for select-vlan.  To
+   mirror every VLAN, use the commands above, but omit select-vlan and
+   its value entirely.
+
+   When a packet arrives on a VLAN that is used as a mirror output
+   VLAN, the mirror is disregarded.  Instead, in standalone mode, OVS
+   floods the packet across all the ports for which the mirror output
+   VLAN is configured.  (If an OpenFlow controller is in use, then it
+   can override this behavior through the flow table.)  If OVS is used
+   as an intermediate switch, rather than an edge switch, this ensures
+   that the RSPAN traffic is distributed through the network.
+
+   Mirroring to a VLAN can disrupt a network that contains unmanaged
+   switches.  See ovs-vswitchd.conf.db(5) for details.  Mirroring to a
+   GRE tunnel has fewer caveats than mirroring to a VLAN and should
+   generally be preferred.
+
+Q: How do I configure mirroring of all traffic to a GRE tunnel?
+
+A: The following commands configure br0 with eth0 and tap0 as trunk
+   ports.  All traffic coming in or going out on eth0 or tap0 is also
+   mirrored to gre0, a GRE tunnel to the remote host 192.168.1.10; any
+   traffic arriving on gre0 is dropped:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl add-port br0 eth0
+       ovs-vsctl add-port br0 tap0
+       ovs-vsctl add-port br0 gre0 \
+           -- set interface gre0 type=gre options:remote_ip=192.168.1.10 \
+           -- --id=@p get port gre0 \
+          -- --id=@m create mirror name=m0 select-all=true output-port=@p \
+          -- set bridge br0 mirrors=@m
+
+   To later disable mirroring and destroy the GRE tunnel:
+
+       ovs-vsctl clear bridge br0 mirrors
+       ovs-vcstl del-port br0 gre0
+
+Q: Does Open vSwitch support ERSPAN?
+
+A: No.  ERSPAN is an undocumented proprietary protocol.  As an
+   alternative, Open vSwitch supports mirroring to a GRE tunnel (see
+   above).
+
+
+Configuration Problems
+----------------------
+
+Q: I created a bridge and added my Ethernet port to it, using commands
+   like these:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl add-port br0 eth0
+
+   and as soon as I ran the "add-port" command I lost all connectivity
+   through eth0.  Help!
+
+A: A physical Ethernet device that is part of an Open vSwitch bridge
+   should not have an IP address.  If one does, then that IP address
+   will not be fully functional.
+
+   You can restore functionality by moving the IP address to an Open
+   vSwitch "internal" device, such as the network device named after
+   the bridge itself.  For example, assuming that eth0's IP address is
+   192.168.128.5, you could run the commands below to fix up the
+   situation:
+
+       ifconfig eth0 0.0.0.0
+       ifconfig br0 192.168.128.5
+
+   (If your only connection to the machine running OVS is through the
+   IP address in question, then you would want to run all of these
+   commands on a single command line, or put them into a script.)  If
+   there were any additional routes assigned to eth0, then you would
+   also want to use commands to adjust these routes to go through br0.
+
+   If you use DHCP to obtain an IP address, then you should kill the
+   DHCP client that was listening on the physical Ethernet interface
+   (e.g. eth0) and start one listening on the internal interface
+   (e.g. br0).  You might still need to manually clear the IP address
+   from the physical interface (e.g. with "ifconfig eth0 0.0.0.0").
+
+   There is no compelling reason why Open vSwitch must work this way.
+   However, this is the way that the Linux kernel bridge module has
+   always worked, so it's a model that those accustomed to Linux
+   bridging are already used to.  Also, the model that most people
+   expect is not implementable without kernel changes on all the
+   versions of Linux that Open vSwitch supports.
+
+   By the way, this issue is not specific to physical Ethernet
+   devices.  It applies to all network devices except Open vswitch
+   "internal" devices.
+
+Q: I created a bridge and added a couple of Ethernet ports to it,
+   using commands like these:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl add-port br0 eth0
+       ovs-vsctl add-port br0 eth1
+
+   and now my network seems to have melted: connectivity is unreliable
+   (even connectivity that doesn't go through Open vSwitch), all the
+   LEDs on my physical switches are blinking, wireshark shows
+   duplicated packets, and CPU usage is very high.
+
+A: More than likely, you've looped your network.  Probably, eth0 and
+   eth1 are connected to the same physical Ethernet switch.  This
+   yields a scenario where OVS receives a broadcast packet on eth0 and
+   sends it out on eth1, then the physical switch connected to eth1
+   sends the packet back on eth0, and so on forever.  More complicated
+   scenarios, involving a loop through multiple switches, are possible
+   too.
+
+   The solution depends on what you are trying to do:
+
+       - If you added eth0 and eth1 to get higher bandwidth or higher
+         reliability between OVS and your physical Ethernet switch,
+         use a bond.  The following commands create br0 and then add
+         eth0 and eth1 as a bond:
+
+             ovs-vsctl add-br br0
+             ovs-vsctl add-bond br0 bond0 eth0 eth1
+
+         Bonds have tons of configuration options.  Please read the
+         documentation on the Port table in ovs-vswitchd.conf.db(5)
+         for all the details.
+
+       - Perhaps you don't actually need eth0 and eth1 to be on the
+         same bridge.  For example, if you simply want to be able to
+         connect each of them to virtual machines, then you can put
+         each of them on a bridge of its own:
+
+             ovs-vsctl add-br br0
+             ovs-vsctl add-port br0 eth0
+
+             ovs-vsctl add-br br1
+             ovs-vsctl add-port br1 eth1
+
+         and then connect VMs to br0 and br1.  (A potential
+         disadvantage is that traffic cannot directly pass between br0
+         and br1.  Instead, it will go out eth0 and come back in eth1,
+         or vice versa.)
+
+       - If you have a redundant or complex network topology and you
+         want to prevent loops, turn on spanning tree protocol (STP).
+         The following commands create br0, enable STP, and add eth0
+         and eth1 to the bridge.  The order is important because you
+         don't want have to have a loop in your network even
+         transiently:
+
+             ovs-vsctl add-br br0
+             ovs-vsctl set bridge br0 stp_enable=true
+             ovs-vsctl add-port br0 eth0
+             ovs-vsctl add-port br0 eth1
+
+         The Open vSwitch implementation of STP is not well tested.
+         Please report any bugs you observe, but if you'd rather avoid
+         acting as a beta tester then another option might be your
+         best shot.
+
+Q: I can't seem to use Open vSwitch in a wireless network.
+
+A: Wireless base stations generally only allow packets with the source
+   MAC address of NIC that completed the initial handshake.
+   Therefore, without MAC rewriting, only a single device can
+   communicate over a single wireless link.
+
+   This isn't specific to Open vSwitch, it's enforced by the access
+   point, so the same problems will show up with the Linux bridge or
+   any other way to do bridging.
+
+Q: Is there any documentation on the database tables and fields?
+
+A: Yes.  ovs-vswitchd.conf.db(5) is a comprehensive reference.
+
+
+VLANs
+-----
+
+Q: What's a VLAN?
+
+A: At the simplest level, a VLAN (short for "virtual LAN") is a way to
+   partition a single switch into multiple switches.  Suppose, for
+   example, that you have two groups of machines, group A and group B.
+   You want the machines in group A to be able to talk to each other,
+   and you want the machine in group B to be able to talk to each
+   other, but you don't want the machines in group A to be able to
+   talk to the machines in group B.  You can do this with two
+   switches, by plugging the machines in group A into one switch and
+   the machines in group B into the other switch.
+
+   If you only have one switch, then you can use VLANs to do the same
+   thing, by configuring the ports for machines in group A as VLAN
+   "access ports" for one VLAN and the ports for group B as "access
+   ports" for a different VLAN.  The switch will only forward packets
+   between ports that are assigned to the same VLAN, so this
+   effectively subdivides your single switch into two independent
+   switches, one for each group of machines.
+
+   So far we haven't said anything about VLAN headers.  With access
+   ports, like we've described so far, no VLAN header is present in
+   the Ethernet frame.  This means that the machines (or switches)
+   connected to access ports need not be aware that VLANs are
+   involved, just like in the case where we use two different physical
+   switches.
+
+   Now suppose that you have a whole bunch of switches in your
+   network, instead of just one, and that some machines in group A are
+   connected directly to both switches 1 and 2.  To allow these
+   machines to talk to each other, you could add an access port for
+   group A's VLAN to switch 1 and another to switch 2, and then
+   connect an Ethernet cable between those ports.  That works fine,
+   but it doesn't scale well as the number of switches and the number
+   of VLANs increases, because you use up a lot of valuable switch
+   ports just connecting together your VLANs.
+
+   This is where VLAN headers come in.  Instead of using one cable and
+   two ports per VLAN to connect a pair of switches, we configure a
+   port on each switch as a VLAN "trunk port".  Packets sent and
+   received on a trunk port carry a VLAN header that says what VLAN
+   the packet belongs to, so that only two ports total are required to
+   connect the switches, regardless of the number of VLANs in use.
+   Normally, only switches (either physical or virtual) are connected
+   to a trunk port, not individual hosts, because individual hosts
+   don't expect to see a VLAN header in the traffic that they receive.
+
+   None of the above discussion says anything about particular VLAN
+   numbers.  This is because VLAN numbers are completely arbitrary.
+   One must only ensure that a given VLAN is numbered consistently
+   throughout a network and that different VLANs are given different
+   numbers.  (That said, VLAN 0 is usually synonymous with a packet
+   that has no VLAN header, and VLAN 4095 is reserved.)
+
+Q: VLANs don't work.
+
+A: Many drivers in Linux kernels before version 3.3 had VLAN-related
+   bugs.  If you are having problems with VLANs that you suspect to be
+   driver related, then you have several options:
+
+       - Upgrade to Linux 3.3 or later.
+
+       - Build and install a fixed version of the particular driver
+         that is causing trouble, if one is available.
+
+       - Use a NIC whose driver does not have VLAN problems.
+
+       - Use "VLAN splinters", a feature in Open vSwitch 1.4 and later
+         that works around bugs in kernel drivers.  To enable VLAN
+         splinters on interface eth0, use the command:
+
+           ovs-vsctl set interface eth0 other-config:enable-vlan-splinters=true
+
+         For VLAN splinters to be effective, Open vSwitch must know
+         which VLANs are in use.  See the "VLAN splinters" section in
+         the Interface table in ovs-vswitchd.conf.db(5) for details on
+         how Open vSwitch infers in-use VLANs.
+
+         VLAN splinters increase memory use and reduce performance, so
+         use them only if needed.
+
+       - Apply the "vlan workaround" patch from the XenServer kernel
+         patch queue, build Open vSwitch against this patched kernel,
+         and then use ovs-vlan-bug-workaround(8) to enable the VLAN
+         workaround for each interface whose driver is buggy.
+
+         (This is a nontrivial exercise, so this option is included
+         only for completeness.)
+
+   It is not always easy to tell whether a Linux kernel driver has
+   buggy VLAN support.  The ovs-vlan-test(8) and ovs-test(8) utilities
+   can help you test.  See their manpages for details.  Of the two
+   utilities, ovs-test(8) is newer and more thorough, but
+   ovs-vlan-test(8) may be easier to use.
+
+Q: VLANs still don't work.  I've tested the driver so I know that it's OK.
+
+A: Do you have VLANs enabled on the physical switch that OVS is
+   attached to?  Make sure that the port is configured to trunk the
+   VLAN or VLANs that you are using with OVS.
+
+Q: Outgoing VLAN-tagged traffic goes through OVS to my physical switch
+   and to its destination host, but OVS seems to drop incoming return
+   traffic.
+
+A: It's possible that you have the VLAN configured on your physical
+   switch as the "native" VLAN.  In this mode, the switch treats
+   incoming packets either tagged with the native VLAN or untagged as
+   part of the native VLAN.  It may also send outgoing packets in the
+   native VLAN without a VLAN tag.
+
+   If this is the case, you have two choices:
+
+       - Change the physical switch port configuration to tag packets
+         it forwards to OVS with the native VLAN instead of forwarding
+         them untagged.
+
+       - Change the OVS configuration for the physical port to a
+         native VLAN mode.  For example, the following sets up a
+         bridge with port eth0 in "native-tagged" mode in VLAN 9:
+
+             ovs-vsctl add-br br0
+             ovs-vsctl add-port br0 eth0 tag=9 vlan_mode=native-tagged
+
+         In this situation, "native-untagged" mode will probably work
+         equally well.  Refer to the documentation for the Port table
+         in ovs-vswitchd.conf.db(5) for more information.
+
+Q: Can I configure an IP address on a VLAN?
+
+A: Yes.  Use an "internal port" configured as an access port.  For
+   example, the following configures IP address 192.168.0.7 on VLAN 9.
+   That is, OVS will forward packets from eth0 to 192.168.0.7 only if
+   they have an 802.1Q header with VLAN 9.  Conversely, traffic
+   forwarded from 192.168.0.7 to eth0 will be tagged with an 802.1Q
+   header with VLAN 9:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl add-port br0 eth0
+       ovs-vsctl add-port br0 vlan9 tag=9 -- set interface vlan9 type=internal
+       ifconfig vlan9 192.168.0.7
+
+Q: My OpenFlow controller doesn't see the VLANs that I expect.
+
+A: The configuration for VLANs in the Open vSwitch database (e.g. via
+   ovs-vsctl) only affects traffic that goes through Open vSwitch's
+   implementation of the OpenFlow "normal switching" action.  By
+   default, when Open vSwitch isn't connected to a controller and
+   nothing has been manually configured in the flow table, all traffic
+   goes through the "normal switching" action.  But, if you set up
+   OpenFlow flows on your own, through a controller or using ovs-ofctl
+   or through other means, then you have to implement VLAN handling
+   yourself.
+
+   You can use "normal switching" as a component of your OpenFlow
+   actions, e.g. by putting "normal" into the lists of actions on
+   ovs-ofctl or by outputting to OFPP_NORMAL from an OpenFlow
+   controller.  This will only be suitable for some situations,
+   though.
+
+Q: I configured ports on a bridge as access ports with different VLAN
+   tags, like this:
+
+       ovs-vsctl add-br br0
+       ovs-vsctl set-controller br0 tcp:192.168.0.10:6633
+       ovs-vsctl add-port br0 eth0
+       ovs-vsctl add-port br0 tap0 tag=9
+       ovs-vsctl add-port br0 tap1 tag=10
+
+   but the VMs running behind tap0 and tap1 can still communicate,
+   that is, they are not isolated from each other even though they are
+   on different VLANs.
+
+A: Do you have a controller configured on br0 (as the commands above
+   do)?  If so, then this is a variant on the previous question, "My
+   OpenFlow controller doesn't see the VLANs that I expect," and you
+   can refer to the answer there for more information.
+
+
+Controllers
+-----------
+
+Q: What versions of OpenFlow does Open vSwitch support?
+
+A: Open vSwitch supports OpenFlow 1.0.  It also includes a number of
+   extensions that bring many of the features from later versions of
+   OpenFlow.  Work is underway to provide support for later versions and
+   can be tracked here:
+
+       http://openvswitch.org/development/openflow-1-x-plan/
+
+Q: I'm getting "error type 45250 code 0".  What's that?
+
+A: This is a Open vSwitch extension to OpenFlow error codes.  Open
+   vSwitch uses this extension when it must report an error to an
+   OpenFlow controller but no standard OpenFlow error code is
+   suitable.
+
+   Open vSwitch logs the errors that it sends to controllers, so the
+   easiest thing to do is probably to look at the ovs-vswitchd log to
+   find out what the error was.
+
+   If you want to dissect the extended error message yourself, the
+   format is documented in include/openflow/nicira-ext.h in the Open
+   vSwitch source distribution.  The extended error codes are
+   documented in lib/ofp-errors.h.
+
+Q1: Some of the traffic that I'd expect my OpenFlow controller to see
+    doesn't actually appear through the OpenFlow connection, even
+    though I know that it's going through.
+Q2: Some of the OpenFlow flows that my controller sets up don't seem
+    to apply to certain traffic, especially traffic between OVS and
+    the controller itself.
+
+A: By default, Open vSwitch assumes that OpenFlow controllers are
+   connected "in-band", that is, that the controllers are actually
+   part of the network that is being controlled.  In in-band mode,
+   Open vSwitch sets up special "hidden" flows to make sure that
+   traffic can make it back and forth between OVS and the controllers.
+   These hidden flows are higher priority than any flows that can be
+   set up through OpenFlow, and they are not visible through normal
+   OpenFlow flow table dumps.
+
+   Usually, the hidden flows are desirable and helpful, but
+   occasionally they can cause unexpected behavior.  You can view the
+   full OpenFlow flow table, including hidden flows, on bridge br0
+   with the command:
+
+       ovs-appctl bridge/dump-flows br0
+
+   to help you debug.  The hidden flows are those with priorities
+   greater than 65535 (the maximum priority that can be set with
+   OpenFlow).
+
+   The DESIGN file at the top level of the Open vSwitch source
+   distribution describes the in-band model in detail.
+
+   If your controllers are not actually in-band (e.g. they are on
+   localhost via 127.0.0.1, or on a separate network), then you should
+   configure your controllers in "out-of-band" mode.  If you have one
+   controller on bridge br0, then you can configure out-of-band mode
+   on it with:
+
+       ovs-vsctl set controller br0 connection-mode=out-of-band
+
+Q: I configured all my controllers for out-of-band control mode but
+   "ovs-appctl bridge/dump-flows" still shows some hidden flows.
+
+A: You probably have a remote manager configured (e.g. with "ovs-vsctl
+   set-manager").  By default, Open vSwitch assumes that managers need
+   in-band rules set up on every bridge.  You can disable these rules
+   on bridge br0 with:
+
+       ovs-vsctl set bridge br0 other-config:disable-in-band=true
+
+   This actually disables in-band control entirely for the bridge, as
+   if all the bridge's controllers were configured for out-of-band
+   control.
+
+Q: My OpenFlow controller doesn't see the VLANs that I expect.
+
+A: See answer under "VLANs", above.
+
+Q: I ran "ovs-ofctl add-flow br0 nw_dst=192.168.0.1,actions=drop"
+   but I got a funny message like this:
+
+       ofp_util|INFO|normalization changed ofp_match, details:
+       ofp_util|INFO| pre: nw_dst=192.168.0.1
+       ofp_util|INFO|post:
+
+   and when I ran "ovs-ofctl dump-flows br0" I saw that my nw_dst
+   match had disappeared, so that the flow ends up matching every
+   packet.
+
+A: The term "normalization" in the log message means that a flow
+   cannot match on an L3 field without saying what L3 protocol is in
+   use.  The "ovs-ofctl" command above didn't specify an L3 protocol,
+   so the L3 field match was dropped.
+
+   In this case, the L3 protocol could be IP or ARP.  A correct
+   command for each possibility is, respectively:
+
+       ovs-ofctl add-flow br0 ip,nw_dst=192.168.0.1,actions=drop
+
+   and 
+
+       ovs-ofctl add-flow br0 arp,nw_dst=192.168.0.1,actions=drop
+
+   Similarly, a flow cannot match on an L4 field without saying what
+   L4 protocol is in use.  For example, the flow match "tp_src=1234"
+   is, by itself, meaningless and will be ignored.  Instead, to match
+   TCP source port 1234, write "tcp,tp_src=1234", or to match UDP
+   source port 1234, write "udp,tp_src=1234".
+
+Q: How can I figure out the OpenFlow port number for a given port?
+
+A: The OFPT_FEATURES_REQUEST message requests an OpenFlow switch to
+   respond with an OFPT_FEATURES_REPLY that, among other information,
+   includes a mapping between OpenFlow port names and numbers.  From a
+   command prompt, "ovs-ofctl show br0" makes such a request and
+   prints the response for switch br0.
+
+   The Interface table in the Open vSwitch database also maps OpenFlow
+   port names to numbers.  To print the OpenFlow port number
+   associated with interface eth0, run:
+
+       ovs-vsctl get Interface eth0 ofport
+
+   You can print the entire mapping with:
+
+       ovs-vsctl -- --columns=name,ofport list Interface
+
+   but the output mixes together interfaces from all bridges in the
+   database, so it may be confusing if more than one bridge exists.
+
+   In the Open vSwitch database, ofport value -1 means that the
+   interface could not be created due to an error.  (The Open vSwitch
+   log should indicate the reason.)  ofport value [] (the empty set)
+   means that the interface hasn't been created yet.  The latter is
+   normally an intermittent condition (unless ovs-vswitchd is not
+   running).
+
+Contact 
+-------
+
+bugs@openvswitch.org
+http://openvswitch.org/
similarity index 88%
rename from INSTALL.Linux
rename to INSTALL
index 0d3c310..671c4a4 100644 (file)
+++ b/INSTALL
@@ -1,9 +1,9 @@
-                 How to Install Open vSwitch on Linux
-                 ====================================
+           How to Install Open vSwitch on Linux and FreeBSD
+           ================================================
 
 This document describes how to build and install Open vSwitch on a
-generic Linux host.  If you want to install Open vSwitch on a Citrix
-XenServer, see INSTALL.XenServer instead.
+generic Linux or FreeBSD host.  If you want to install Open vSwitch on
+a Citrix XenServer, see INSTALL.XenServer instead.
 
 This version of Open vSwitch may be built manually with "configure"
 and "make", as described below.  You may also build Debian packages by
@@ -29,9 +29,9 @@ you will need the following software:
       libssl is installed, then Open vSwitch will automatically build
       with support for it.
 
-To compile the kernel module, you must also install the following.  If
-you cannot build or install the kernel module, you may use the
-userspace-only implementation, at a cost in performance.  The
+To compile the kernel module on Linux, you must also install the
+following.  If you cannot build or install the kernel module, you may
+use the userspace-only implementation, at a cost in performance.  The
 userspace implementation may also lack some features.  Refer to
 INSTALL.userspace for more information.
 
@@ -100,6 +100,14 @@ you have the following:
 
     - Python 2.x, for x >= 4.
 
+If you are going to extensively modify Open vSwitch, please consider
+installing the following to obtain better warnings:
+
+    - "sparse" version 0.4.4 or later
+      (http://www.kernel.org/pub/software/devel/sparse/dist/).
+
+    - GNU make.
+
 Installation Requirements
 -------------------------
 
@@ -111,14 +119,14 @@ following software:
     - libssl compatible with the libssl used for build, if OpenSSL was
       used for the build.
 
-    - The Linux kernel version configured as part of the build.
+    - On Linux, the same kernel version configured as part of the build.
 
-    - For optional support of ingress policing, the "tc" program from
-      iproute2 (part of all major distributions and available at
+    - For optional support of ingress policing on Linux, the "tc" program
+      from iproute2 (part of all major distributions and available at
       http://www.linux-foundation.org/en/Net:Iproute2).
 
-You should ensure that /dev/urandom exists.  To support TAP devices,
-you must also ensure that /dev/net/tun exists.
+On Linux you should ensure that /dev/urandom exists.  To support TAP
+devices, you must also ensure that /dev/net/tun exists.
 
 To run the ovsdbmonitor tool, the machine must also have the following
 software:
@@ -136,13 +144,13 @@ 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
-==============================================
+Building and Installing Open vSwitch for Linux or FreeBSD
+=========================================================
 
 Once you have installed all the prerequisites listed above in the Base
 Prerequisites section, follow the procedure below to build.
 
-1. If you pulled the sources directly from an Open vSwitch Git tree, 
+1. If you pulled the sources directly from an Open vSwitch Git tree,
    run boot.sh in the top source directory:
 
       % ./boot.sh
@@ -183,10 +191,16 @@ Prerequisites section, follow the procedure below to build.
    additional environment variables.  For a full list, invoke
    configure with the --help option.
 
-3. Run make in the top source directory: 
+3. Run make in the top source directory:
 
       % make
 
+   On FreeBSD you may need to use GNU make (gmake) or NetBSD make
+   (bmake) instead of the native make.
+
+   For improved warnings if you installed "sparse" (see
+   "Prerequisites"), add C=1 to the "make" command line.
+
 4. Become root by running "su" or another program.
 
 5. Run "make install" to install the executables and manpages into the
@@ -311,7 +325,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".
+        vSwitch for Linux or FreeBSD".
 
       - If you want to preserve the contents of your database, back it
         up first, then use "ovsdb-tool convert" to upgrade it, e.g.:
@@ -319,7 +333,7 @@ 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" above.
+   Installing Open vSwitch for Linux or FreeBSD" above.
 
 Bug Reporting
 -------------
index 61620e5..18a7bd4 100644 (file)
@@ -3,12 +3,12 @@
 
 This document describes how to use Open vSwitch with the Kernel-based
 Virtual Machine (KVM). This document assumes that you have read and
-followed INSTALL.Linux to get Open vSwitch setup on your Linux system.
+followed INSTALL to get Open vSwitch setup on your Linux system.
 
 Setup
 -----
 
-First, follow the setup instructions in INSTALL.Linux to get a working
+First, follow the setup instructions in INSTALL to get a working
 Open vSwitch installation.
 
 KVM uses tunctl to handle various bridging modes, which you can 
@@ -43,7 +43,7 @@ switch='br0'
 ovs-vsctl del-port ${switch} $1
 --------------------------------------------------------------------
 
-At the end of INSTALL.Linux, it describes basic usage of creating 
+At the end of INSTALL, it describes basic usage of creating
 bridges and ports. If you haven't already, create a bridge named
 br0 with the following command:
 
index 4f31831..1bc45d5 100644 (file)
@@ -4,7 +4,7 @@
 This document describes how to use Open vSwitch with Libvirt 0.9.11 or
 later. The Open vSwitch support in Libvirt 0.9.11 eliminates the need to
 use OVS Linux Bridge compatibility layer (brcompatd) and interface up/down
-scripts. This document assumes that you followed INSTALL.Linux or installed
+scripts. This document assumes that you followed INSTALL or installed
 Open vSwitch from distribution packaging such as a .deb or .rpm. The Open
 vSwitch support is included by default in Libvirt 0.9.11. Consult
 www.libvirt.org for instructions on how to build the latest Libvirt, if your
index 039a857..ff79c89 100644 (file)
@@ -3,7 +3,7 @@
 
 This document describes how to build and install Open vSwitch on a Red
 Hat Enterprise Linux (RHEL) host.  If you want to install Open vSwitch
-on a generic Linux host, see INSTALL.Linux instead.
+on a generic Linux host, see INSTALL instead.
 
 We have tested these instructions with RHEL 5.6 and RHEL 6.0.
 
index f322b41..8eb0c49 100644 (file)
@@ -7,8 +7,8 @@ with OpenSSL.  SSL support ensures integrity and confidentiality of
 the OpenFlow connections, increasing network security.
 
 This file explains how to configure an Open vSwitch to connect to an
-OpenFlow controller over SSL.  Refer to INSTALL.Linux for instructions
-on building Open vSwitch with SSL support.
+OpenFlow controller over SSL.  Refer to INSTALL for instructions on
+building Open vSwitch with SSL support.
 
 Open vSwitch uses TLS version 1.0 or later (TLSv1), as specified by
 RFC 2246, which is very similar to SSL version 3.0.  TLSv1 was
@@ -306,10 +306,6 @@ After you have added all of these configuration keys, you may specify
 "tcp:" connection methods are still allowed even after SSL has been
 configured, so for security you should use only "ssl:" connections.
 
-Unlike most Open vSwitch settings, the SSL settings are read only
-once, at ovs-vswitchd startup time.  For changes to take effect,
-ovs-vswitchd must be killed and restarted.
-
 Reporting Bugs
 --------------
 
index e45d5bc..7a4dd76 100644 (file)
@@ -3,7 +3,7 @@
 
 This document describes how to build and install Open vSwitch on a
 Citrix XenServer host.  If you want to install Open vSwitch on a
-generic Linux host, see INSTALL.Linux instead.
+generic Linux or BSD host, see INSTALL instead.
 
 These instructions have been tested with XenServer 5.6 FP1.
 
@@ -19,8 +19,8 @@ RPMs for Citrix XenServer is the DDK VM available from Citrix.
    ./configure; make dist" in the Git tree.  You cannot run this in
    the DDK VM, because it lacks tools that are necessary to bootstrap
    the Open vSwitch distribution.  Instead, you must run this on a
-   machine that has the tools listed in INSTALL.Linux as prerequisites
-   for building from a Git tree.
+   machine that has the tools listed in INSTALL as prerequisites for
+   building from a Git tree.
 
 2. Copy the distribution tarball into /usr/src/redhat/SOURCES inside
    the DDK VM.
index dec09d8..af20bff 100644 (file)
@@ -25,7 +25,7 @@ that the Open vSwitch kernel modules are loaded before the Linux
 kernel bridge module.
 
 1. Build, install, and start up the Open vSwitch kernel modules and
-   userspace programs as described in INSTALL.Linux.
+   userspace programs as described in INSTALL.
 
    It is important to run "make install", because some Open vSwitch
    programs expect to find files in locations selected at installation
index 6e6fcd4..296f077 100644 (file)
@@ -17,7 +17,7 @@ Building and Installing
 -----------------------
 
 The requirements and procedure for building, installing, and
-configuring Open vSwitch are the same as those given in INSTALL.Linux.
+configuring Open vSwitch are the same as those given in INSTALL.
 You may omit configuring, building, and installing the kernel module,
 and the related requirements.
 
@@ -31,6 +31,10 @@ 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.
+
 Using the Userspace Datapath with ovs-vswitchd
 ----------------------------------------------
 
@@ -47,6 +51,19 @@ 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.
 
+Firewall Rules
+--------------
+
+On Linux, when a physical interface is in use by the userspace
+datapath, packets received on the interface still also pass into the
+kernel TCP/IP stack.  This can cause surprising and incorrect
+behavior.  You can use "iptables" to avoid this behavior, by using it
+to drop received packets.  For example, to drop packets received on
+eth0:
+
+    iptables -A INPUT -i eth0 -j DROP
+    iptables -A FORWARD -i eth0 -j DROP
+
 Bug Reporting
 -------------
 
diff --git a/IntegrationGuide b/IntegrationGuide
new file mode 100644 (file)
index 0000000..976936b
--- /dev/null
@@ -0,0 +1,169 @@
+                 Integration Guide for Centralized Control
+                 =========================================
+
+This document describes how to integrate Open vSwitch onto a new
+platform to expose the state of the switch and attached devices for
+centralized control.  (If you are looking to port the switching
+components of Open vSwitch to a new platform, please see the PORTING
+document.)  The focus of this guide is on hypervisors, but many of the
+interfaces are useful for hardware switches, as well.  The XenServer
+integration is the most mature implementation, so most of the examples
+are drawn from it.
+
+The externally visible interface to this integration is
+platform-agnostic.  We encourage anyone who integrates Open vSwitch to
+use the same interface, because keeping a uniform interface means that
+controllers require less customization for individual platforms (and
+perhaps no customization at all).
+
+Integration centers around the Open vSwitch database and mostly involves
+the 'external_ids' columns in several of the tables.  These columns are
+not interpreted by Open vSwitch itself.  Instead, they provide
+information to a controller that permits it to associate a database
+record with a more meaningful entity.  In contrast, the 'other_config'
+column is used to configure behavior of the switch.  The main job of the
+integrator, then, is to ensure that these values are correctly populated
+and maintained.
+
+An integrator sets the columns in the database by talking to the
+ovsdb-server daemon.  A few of the columns can be set during startup by
+calling the ovs-ctl tool from inside the startup scripts.  The
+'xenserver/etc_init.d_openvswitch' script provides examples of its use,
+and the ovs-ctl(8) manpage contains complete documentation.  At runtime,
+ovs-vsctl can be be used to set columns in the database.  The script
+'xenserver/etc_xensource_scripts_vif' contains examples of its use, and
+ovs-vsctl(8) manpage contains complete documentation.
+
+Python and C bindings to the database are provided if deeper integration
+with a program are needed.  The XenServer ovs-xapi-sync daemon
+('xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync') provides an
+example of using the Python bindings.  More information on the python
+bindings is available at 'python/ovs/db/idl.py'.  Information on the C
+bindings is available at 'lib/ovsdb-idl.h'.
+
+The following diagram shows how integration scripts fit into the Open vSwitch
+architecture:
+
+                +----------------------------------------+
+                |           Controller Cluster           +
+                +----------------------------------------+
+                                    |
+                                    |
+       +----------------------------------------------------------+
+       |                            |                             |
+       |             +--------------+---------------+             |
+       |             |                              |             |
+       |   +-------------------+           +------------------+   |
+       |   |   ovsdb-server    |-----------|   ovs-vswitchd   |   |
+       |   +-------------------+           +------------------+   |
+       |             |                              |             |
+       |  +---------------------+                   |             |
+       |  | Integration scripts |                   |             |
+       |  | (ex: ovs-xapi-sync) |                   |             |
+       |  +---------------------+                   |             |
+       |                                            |   Userspace |
+       |----------------------------------------------------------|
+       |                                            |      Kernel |
+       |                                            |             |
+       |                                 +---------------------+  |
+       |                                 |  OVS Kernel Module  |  |
+       |                                 +---------------------+  |
+       +----------------------------------------------------------+
+
+
+A description of the most relevant fields for integration follows.  By
+setting these values, controllers are able to understand the network and
+manage it more dynamically and precisely.  For more details about the
+database and each individual column, please refer to the
+ovs-vswitchd.conf.db(5) manpage.
+
+
+Open_vSwitch table
+------------------
+The Open_vSwitch table describes the switch as a whole.  The
+'system_type' and 'system_version' columns identify the platform to the
+controller.  The 'external_ids:system-id' key uniquely identifies the
+physical host.  In XenServer, the system-id will likely be the same as
+the UUID returned by 'xe host-list'. This key allows controllers to
+distinguish between multiple hypervisors.
+
+Most of this configuration can be done with the ovs-ctl command at
+startup.  For example:
+
+  ovs-ctl --system-type="XenServer" --system-version="6.0.0-50762p" \
+          --system-id="${UUID}" "${other_options}" start
+
+Alternatively, the ovs-vsctl command may be used to set a particular
+value at runtime.  For example:
+
+  ovs-vsctl set open_vswitch . external-ids:system-id='"${UUID}"'
+
+The 'other_config:enable-statistics' key may be set to "true" to have OVS
+populate the database with statistics (e.g., number of CPUs, memory,
+system load) for the controller's use.
+
+
+Bridge table
+------------
+The Bridge table describes individual bridges within an Open vSwitch
+instance.  The 'external-ids:bridge-id' key uniquely identifies a
+particular bridge.  In XenServer, this will likely be the same as the
+UUID returned by 'xe network-list' for that particular bridge.
+
+For example, to set the identifier for bridge "br0", the following
+command can be used:
+
+  ovs-vsctl set Bridge br0 external-ids:bridge-id='"${UUID}"'
+
+The MAC address of the bridge may be manually configured by setting it
+with the "other_config:hwaddr" key.  For example:
+
+  ovs-vsctl set Bridge br0 other_config:hwaddr="12:34:56:78:90:ab"
+
+
+Interface table
+---------------
+The Interface table describes an interface under the control of Open
+vSwitch.  The 'external_ids' column contains keys that are used to
+provide additional information about the interface:
+
+    attached-mac
+
+        This field contains the MAC address of the device attached to
+        the interface.  On a hypervisor, this is the MAC address of the
+        interface as seen inside a VM.  It does not necessarily
+        correlate to the host-side MAC address.  For example, on
+        XenServer, the MAC address on a VIF in the hypervisor is always
+        FE:FF:FF:FF:FF:FF, but inside the VM a normal MAC address is
+        seen.
+
+    iface-id
+
+        This field uniquely identifies the interface.  In hypervisors,
+        this allows the controller to follow VM network interfaces as
+        VMs migrate.  A well-chosen identifier should also allow an
+        administrator or a controller to associate the interface with
+        the corresponding object in the VM management system.  For
+        example, the Open vSwitch integration with XenServer by default
+        uses the XenServer assigned UUID for a VIF record as the
+        iface-id.
+
+    iface-status
+
+        In a hypervisor, there are situations where there are multiple
+        interface choices for a single virtual ethernet interface inside
+        a VM.  Valid values are "active" and "inactive".  A complete
+        description is available in the ovs-vswitchd.conf.db(5) manpage.
+
+    vm-id
+
+        This field uniquely identifies the VM to which this interface
+        belongs.  A single VM may have multiple interfaces attached to
+        it.
+
+As in the previous tables, the ovs-vsctl command may be used to
+configure the values.  For example, to set the 'iface-id' on eth0, the
+following command can be used:
+
+  ovs-vsctl set Interface eth0 external-ids:iface-id='"${UUID}"'
+
index d7eadec..932f495 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -20,10 +20,18 @@ AM_CFLAGS += $(WARNING_FLAGS)
 if NDEBUG
 AM_CPPFLAGS += -DNDEBUG
 AM_CFLAGS += -fomit-frame-pointer
-else
-AM_LDFLAGS = -export-dynamic
 endif
 
+# PYTHONDONTWRITEBYTECODE=yes keeps Python from creating .pyc and .pyo
+# files.  Creating .py[co] works OK for any given version of Open
+# vSwitch, but it causes trouble if you switch from a version with
+# foo/__init__.py into an (older) version with plain foo.py, since
+# foo/__init__.pyc will cause Python to ignore foo.py.
+run_python = \
+       PYTHONDONTWRITEBYTECODE=yes \
+       PYTHONPATH=$(top_srcdir)/python:$$PYTHONPATH \
+       $(PYTHON)
+
 ALL_LOCAL =
 BUILT_SOURCES =
 CLEANFILES =
@@ -33,14 +41,16 @@ PYCOV_CLEAN_FILES = build-aux/check-structs,cover
 EXTRA_DIST = \
        CodingStyle \
        DESIGN \
+       FAQ \
+       INSTALL \
        INSTALL.KVM \
        INSTALL.Libvirt \
-       INSTALL.Linux \
        INSTALL.RHEL \
        INSTALL.SSL \
        INSTALL.XenServer \
        INSTALL.bridge \
        INSTALL.userspace \
+       IntegrationGuide \
        NOTICE \
        PORTING \
        README-gcov \
@@ -98,6 +108,7 @@ SUFFIXES += .in
            sed \
                -e 's,[@]PKIDIR[@],$(PKIDIR),g' \
                 -e 's,[@]LOGDIR[@],$(LOGDIR),g' \
+                -e 's,[@]DBDIR[@],$(DBDIR),g' \
                 -e 's,[@]PERL[@],$(PERL),g' \
                 -e 's,[@]PYTHON[@],$(PYTHON),g' \
                 -e 's,[@]RUNDIR[@],$(RUNDIR),g' \
@@ -123,6 +134,11 @@ CLEAN_LOCAL += clean-pycov
 
 # If we're checked out from a Git repository, make sure that every
 # file that is in Git is distributed.
+#
+# We only enable this check when GNU make is in use because the
+# Makefile in datapath/linux, needed to get the list of files to
+# distribute, requires GNU make extensions.
+if GNU_MAKE
 ALL_LOCAL += dist-hook-git
 dist-hook-git: distfiles
        @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1; then \
@@ -148,6 +164,8 @@ distfiles: Makefile
          sed -e "s|^$$srcdirstrip/||;t" \
              -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t" | sort -u > $@
 CLEANFILES += distfiles
+endif
+.PHONY: dist-hook-git
 
 # Check that "struct vlog_ratelimit" is always declared "static".
 ALL_LOCAL += rate-limit-check
@@ -159,6 +177,7 @@ rate-limit-check:
            echo "'struct vlog_rate_limit' must always be 'static'"; \
            exit 1; \
         fi
+.PHONY: rate-limit-check
 
 if HAVE_GROFF
 ALL_LOCAL += manpage-check
diff --git a/NEWS b/NEWS
index 723c256..872f8d0 100644 (file)
--- a/NEWS
+++ b/NEWS
-post-v1.6.0
+post-v1.8.0
+------------------------
+    - FreeBSD is now a supported platform, thanks to code contributions from
+      Gaetano Catalli, Ed Maste, and Giuseppe Lettieri.
+    - ovs-bugtool: New --ovs option to report only OVS related information.
+    - New %t and %T log escapes to identify the subprogram within a
+      cooperating group of processes or threads that emitted a log message.
+      The default log patterns now include this information.
+    - OpenFlow:
+      - Allow bitwise masking for SHA and THA fields in ARP, SLL and TLL
+        fields in IPv6 neighbor discovery messages, and IPv6 flow label.
+    - ovs-dpctl:
+      - Support requesting the port number with the "port_no" option in
+        the "add-if" command.
+    - ovs-pki: The "online PKI" features have been removed, along with
+      the ovs-pki-cgi program that facilitated it, because of some
+      alarmist insecurity claims.  We do not believe that these claims
+      are true, but because we do not know of any users for this
+      feature it seems better on balance to remove it.  (The ovs-pki-cgi
+      program was not included in distribution packaging.)
+    - Stable bond mode is deprecated and will be removed no earlier than
+      February 2013.  Please email dev@openvswitch.org with concerns.
+    - The autopath action is deprecated and will be removed no earlier than
+      February 2013.  Please email dev@openvswitch.org with concerns.
+    - The null interface type is deprecated and will be removed no earlier
+      than February 2013.  Please email dev@openvswitch.org with concerns.
+
+
+v1.8.0 - xx xxx xxxx
+------------------------
+    - New FAQ.  Please send updates and additions!
+    - Authors of controllers, please read the new section titled "Action
+      Reproduction" in DESIGN, which describes an Open vSwitch change in
+      behavior in corner cases that may affect some controllers.
+    - ovs-l3ping:
+        - A new test utility that can create L3 tunnel between two Open
+          vSwitches and detect connectivity issues.
+    - ovs-ofctl:
+        - New --sort and --rsort options for "dump-flows" command.
+        - "mod-port" command can now control all OpenFlow config flags.
+    - OpenFlow:
+      - Allow general bitwise masking for IPv4 and IPv6 addresses in
+        IPv4, IPv6, and ARP packets.  (Previously, only CIDR masks
+        were allowed.)
+      - Allow support for arbitrary Ethernet masks.  (Previously, only
+        the multicast bit in the destination address could be individually
+       masked.)
+      - New field OXM_OF_METADATA, to align with OpenFlow 1.1.
+      - The OFPST_QUEUE request now reports an error if a specified port or
+        queue does not exist, or for requests for a specific queue on all
+        ports, if the specified queue does not exist on any port.  (Previous
+        versions generally reported an empty set of results.)
+      - New "flow monitor" feature to allow controllers to be notified of
+        flow table changes as they happen.
+    - Additional protocols are not mirrored and dropped when forward-bpdu is
+      false.  For a full list, see the ovs-vswitchd.conf.db man page.
+    - Open vSwitch now sends RARP packets in situations where it previously
+      sent a custom protocol, making it consistent with behavior of QEMU and
+      VMware.
+    - All Open vSwitch programs and log files now show timestamps in UTC,
+      instead the local timezone, by default.
+
+
+v1.7.0 - 30 Jul 2012
 ------------------------
     - kernel modules are renamed. openvswitch_mod.ko is now
       openvswitch.ko and brcompat_mod.ko is now brcompat.ko.
     - Increased the number of NXM registers to 8.
-    - Added ability to configure dscp setting for manager and controller
+    - Added ability to configure DSCP setting for manager and controller
       connections.  By default, these connections have a DSCP value of
       Internetwork Control (0xc0).
     - Added the granular link health statistics, 'cfm_health', to an
       interface.
     - OpenFlow:
         - Added support to mask nd_target for ICMPv6 neighbor discovery flows.
+        - Added support for OpenFlow 1.3 port description (OFPMP_PORT_DESC)
+          multipart messages.
+    - ovs-ofctl:
+        - Added the "dump-ports-desc" command to retrieve port
+          information using the new port description multipart messages.
     - ovs-test:
         - Added support for spawning ovs-test server from the client.
         - Now ovs-test is able to automatically create test bridges and ports.
     - "ovs-dpctl dump-flows" now prints observed TCP flags in TCP flows.
+    - Tripled flow setup performance.
     - The "coverage/log" command previously available through ovs-appctl
       has been replaced by "coverage/show".  The new command replies with
       coverage counter values, instead of logging them.
 
 
-v1.6.0 - xx xxx xxxx
+v1.6.1 - 25 Jun 2012
+------------------------
+    - Allow OFPP_CONTROLLER as the in_port for packet-out messages.
+
+
+v1.6.0 - 24 Feb 2012
 ------------------------
+    *** Internal only release ***
     - bonding
         - LACP bonds no longer fall back to balance-slb when negotiations fail.
           Instead they drop traffic.
@@ -41,8 +116,6 @@ v1.6.0 - xx xxx xxxx
         - Added "fin_timeout" support to "learn" action.
         - New Nicira action NXAST_CONTROLLER that offers additional features
           over output to OFPP_CONTROLLER.
-    - The default MAC learning timeout has been increased from 60 seconds
-      to 300 seconds.  The MAC learning timeout is now configurable.
     - When QoS settings for an interface do not configure a default queue
       (queue 0), Open vSwitch now uses a default configuration for that
       queue, instead of dropping all packets as in previous versions.
@@ -69,7 +142,7 @@ v1.6.0 - xx xxx xxxx
     - CFM module CCM broadcasts can now be tagged with an 802.1p priority.
 
 
-v1.5.0 - xx xxx xxxx
+v1.5.0 - 01 Jun 2012
 ------------------------
     - OpenFlow:
         - Added support for querying, modifying, and deleting flows
@@ -83,6 +156,8 @@ v1.5.0 - xx xxx xxxx
           {=}, {!=}, {<}, {>}, {<=}, and {>=}.
     - ovsdb-tool now uses the typical database and schema installation
       directories as defaults.
+    - The default MAC learning timeout has been increased from 60 seconds
+      to 300 seconds.  The MAC learning timeout is now configurable.
 
 
 v1.4.0 - 30 Jan 2012
diff --git a/NOTICE b/NOTICE
index d2aa9aa..dafd25f 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -2,7 +2,10 @@ This file is included in compliance with the Apache 2.0 license,
 available at http://www.apache.org/licenses/LICENSE-2.0.html
 
 Open vSwitch
-Copyright (c) 2007, 2008, 2009, 2010, 2011 Nicira Networks.
+Copyright (c) 2007, 2008, 2009, 2010, 2011 Nicira, Inc.
+
+Open vSwitch BSD port
+Copyright (c) 2011 Gaetano Catalli
 
 Apache Portable Runtime
 Copyright 2008 The Apache Software Foundation.
diff --git a/PORTING b/PORTING
index 5281d50..ffde296 100644 (file)
--- a/PORTING
+++ b/PORTING
@@ -245,6 +245,17 @@ ovs_be64 as fixed-width types in network byte order.  Each of the
 latter is equivalent to the one of the former, but the difference in
 name makes the intended use obvious.
 
+The default "fail-mode" for Open vSwitch bridges is "standalone",
+meaning that, when the OpenFlow controllers cannot be contacted, Open
+vSwitch acts as a regular MAC-learning switch.  This works well in
+virtualization environments where there is normally just one uplink
+(either a single physical interface or a bond).  In a more general
+environment, it can create loops.  So, if you are porting to a
+general-purpose switch platform, you should consider changing the
+default "fail-mode" to "secure", which does not behave this way.  See
+documentation for the "fail-mode" column in the Bridge table in
+ovs-vswitchd.conf.db(5) for more information.
+
 lib/entropy.c assumes that it can obtain high-quality random number
 seeds at startup by reading from /dev/urandom.  You will need to
 modify it if this is not true on your platform.
diff --git a/README b/README
index 251a92e..7c680d4 100644 (file)
--- a/README
+++ b/README
@@ -88,7 +88,9 @@ Open vSwitch also provides some tools:
 What other documentation is available?
 --------------------------------------
 
-To install Open vSwitch on a regular Linux machine, read INSTALL.Linux.
+To install Open vSwitch on a regular Linux machine, read INSTALL.
+
+For answers to common questions, read FAQ.
 
 To use Open vSwitch as a drop-in replacement for the Linux bridge,
 read INSTALL.bridge.
@@ -99,7 +101,7 @@ or resource pool, read INSTALL.XenServer.
 To build RPMs for installing Open vSwitch on a Red Hat Enterprise
 Linux host, read INSTALL.RHEL.
 
-To use Open vSwitch with KVM on Linux, read INSTALL.Linux, then
+To use Open vSwitch with KVM on Linux, read INSTALL, then
 INSTALL.KVM.
 
 To use Open vSwitch with Libvirt, read INSTALL.Libvirt.
index 01c49cc..2fe9f3a 100644 (file)
@@ -16,9 +16,3 @@ code-coverage related options:
   --enable-coverage=yes
 
     Build with gcov code coverage support.
-
-    If you enable coverage and you have the "lcov" and "genhtml"
-    programs in PATH, then you may run "make check-lcov" to produce a
-    directory "tests/coverage.html" in the build directory with an
-    analysis of the test suite's coverage.  lcov is available at
-    http://ltp.sourceforge.net/coverage/lcov.php
index 8beb7d8..cb585d0 100644 (file)
@@ -1,7 +1,7 @@
 How to Submit Patches for Open vSwitch
 ======================================
 
-Send changes to Open vSwitch as patches to discuss@openvswitch.org.
+Send changes to Open vSwitch as patches to dev@openvswitch.org.
 One patch per email, please.  More details are included below.
 
 If you are using Git, then "git format-patch" takes care of most of
index 69bb772..d502b00 100644 (file)
@@ -1,6 +1,6 @@
 # -*- autoconf -*-
 
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -247,6 +247,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_warn_if_lro],
                   [OVS_DEFINE([HAVE_SKB_WARN_LRO])])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [consume_skb])
+  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_frag_page])
 
   OVS_GREP_IFELSE([$KSRC/include/linux/string.h], [kmemdup], [],
                   [OVS_GREP_IFELSE([$KSRC/include/linux/slab.h], [kmemdup])])
@@ -287,6 +288,17 @@ AC_DEFUN([OVS_CHECK_IF_PACKET],
                 [Define to 1 if net/if_packet.h is available.])
    fi])
 
+dnl Checks for net/if_dl.h.
+AC_DEFUN([OVS_CHECK_IF_DL],
+  [AC_CHECK_HEADER([net/if_dl.h],
+                   [HAVE_IF_DL=yes],
+                   [HAVE_IF_DL=no])
+   AM_CONDITIONAL([HAVE_IF_DL], [test "$HAVE_IF_DL" = yes])
+   if test "$HAVE_IF_DL" = yes; then
+      AC_DEFINE([HAVE_IF_DL], [1],
+                [Define to 1 if net/if_dl.h is available.])
+   fi])
+
 dnl Checks for buggy strtok_r.
 dnl
 dnl Some versions of glibc 2.7 has a bug in strtok_r when compiling
@@ -393,7 +405,7 @@ dnl OVS_MAKE_HAS_IF([if-true], [if-false])
 dnl
 dnl Checks whether make has the GNU make $(if condition,then,else) extension.
 dnl Runs 'if-true' if so, 'if-false' otherwise.
-AC_DEFUN([OVS_MAKE_HAS_IF],
+AC_DEFUN([OVS_CHECK_MAKE_IF],
   [AC_CACHE_CHECK(
      [whether ${MAKE-make} has GNU make \$(if) extension],
      [ovs_cv_gnu_make_if],
@@ -413,8 +425,28 @@ EOF
         ovs_cv_gnu_make_if=yes
       else
         ovs_cv_gnu_make_if=no
-      fi])
-   AS_IF([test $ovs_cv_gnu_make_if = yes], [$1], [$2])])
+      fi])])
+
+dnl OVS_CHECK_GNU_MAKE
+dnl
+dnl Checks whether make is GNU make (because Linux kernel Makefiles
+dnl only work with GNU make).
+AC_DEFUN([OVS_CHECK_GNU_MAKE],
+  [AC_CACHE_CHECK(
+     [whether ${MAKE-make} is GNU make],
+     [ovs_cv_gnu_make],
+     [rm -f conftest.out
+      AS_ECHO(["$as_me:$LINENO: invoking ${MAKE-make} --version:"]) >&AS_MESSAGE_LOG_FD 2>&1
+      ${MAKE-make} --version >conftest.out 2>&1
+      cat conftest.out >&AS_MESSAGE_LOG_FD 2>&1
+      result=`cat conftest.out`
+      rm -f conftest.mk conftest.out
+
+      case $result in # (
+        GNU*) ovs_cv_gnu_make=yes ;; # (
+        *) ovs_cv_gnu_make=no ;;
+      esac])
+   AM_CONDITIONAL([GNU_MAKE], [test $ovs_cv_gnu_make = yes])])
 
 dnl OVS_CHECK_SPARSE_TARGET
 dnl
@@ -438,8 +470,10 @@ AC_DEFUN([OVS_CHECK_SPARSE_TARGET],
 dnl OVS_ENABLE_SPARSE
 AC_DEFUN([OVS_ENABLE_SPARSE],
   [AC_REQUIRE([OVS_CHECK_SPARSE_TARGET])
-   OVS_MAKE_HAS_IF(
-     [AC_CONFIG_COMMANDS_PRE(
-        [: ${SPARSE=sparse}
-         AC_SUBST([SPARSE])
-         CC='$(if $(C),REAL_CC="'"$CC"'" CHECK="$(SPARSE) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS)" cgcc $(CGCCFLAGS),'"$CC"')'])])])
+   AC_REQUIRE([OVS_CHECK_MAKE_IF])
+   : ${SPARSE=sparse}
+   AC_SUBST([SPARSE])
+   AC_CONFIG_COMMANDS_PRE(
+     [if test $ovs_cv_gnu_make_if = yes; then
+        CC='$(if $(C),REAL_CC="'"$CC"'" CHECK="$(SPARSE) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS)" cgcc $(CGCCFLAGS),'"$CC"')'
+      fi])])
index efaf103..e513b7f 100755 (executable)
@@ -234,6 +234,7 @@ def extract_ofp_errors(filenames):
                               "NX1.0+": ("OF1.0", "OF1.1", "OF1.2"),
                               "NX1.0":  ("OF1.0",),
                               "NX1.1":  ("OF1.1",),
+                              "NX1.1+": ("OF1.1",),
                               "NX1.2":  ("OF1.2",)}
                 if targets not in target_map:
                     fatal("%s: unknown error domain" % targets)
@@ -342,7 +343,7 @@ static enum ofperr
 }"""
 
         print """
-const struct ofperr_domain %s = {
+static const struct ofperr_domain %s = {
     "%s",
     %d,
     %s_decode,
diff --git a/build-aux/extract-ofp-msgs b/build-aux/extract-ofp-msgs
new file mode 100755 (executable)
index 0000000..ad1a8a8
--- /dev/null
@@ -0,0 +1,352 @@
+#! /usr/bin/python
+
+import sys
+import os.path
+import re
+
+line = ""
+
+OFP10_VERSION = 0x01
+OFP11_VERSION = 0x02
+OFP12_VERSION = 0x03
+OFP13_VERSION = 0x04
+
+NX_VENDOR_ID = 0x00002320
+
+OFPT_VENDOR = 4
+OFPT10_STATS_REQUEST = 16
+OFPT10_STATS_REPLY = 17
+OFPT11_STATS_REQUEST = 18
+OFPT11_STATS_REPLY = 19
+OFPST_VENDOR = 0xffff
+
+version_map = {"1.0":     (OFP10_VERSION, OFP10_VERSION),
+               "1.1":     (OFP11_VERSION, OFP11_VERSION),
+               "1.2":     (OFP12_VERSION, OFP12_VERSION),
+               "1.3":     (OFP13_VERSION, OFP13_VERSION),
+               "1.0+":    (OFP10_VERSION, OFP13_VERSION),
+               "1.1+":    (OFP11_VERSION, OFP13_VERSION),
+               "1.2+":    (OFP12_VERSION, OFP13_VERSION),
+               "1.3+":    (OFP13_VERSION, OFP13_VERSION),
+               "1.0-1.1": (OFP10_VERSION, OFP11_VERSION),
+               "1.0-1.2": (OFP10_VERSION, OFP12_VERSION)}
+
+def get_line():
+    global line
+    global line_number
+    line = input_file.readline()
+    line_number += 1
+    if line == "":
+        fatal("unexpected end of input")
+
+n_errors = 0
+def error(msg):
+    global n_errors
+    sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
+    n_errors += 1
+
+def fatal(msg):
+    error(msg)
+    sys.exit(1)
+
+def usage():
+    argv0 = os.path.basename(sys.argv[0])
+    print '''\
+%(argv0)s, for extracting OpenFlow message types from header files
+usage: %(argv0)s INPUT OUTPUT
+  where INPUT is the name of the input header file
+    and OUTPUT is the output file name.
+Despite OUTPUT, the output is written to stdout, and the OUTPUT argument
+only controls #line directives in the output.\
+''' % {"argv0": argv0}
+    sys.exit(0)
+
+def make_sizeof(s):
+    m = re.match(r'(.*) up to (.*)', s)
+    if m:
+        struct, member = m.groups()
+        return "offsetof(%s, %s)" % (struct, member)
+    else:
+        return "sizeof(%s)" % s
+
+def extract_ofp_msgs(output_file_name):
+    raw_types = []
+
+    all_hdrs = {}
+    all_raws = {}
+    all_raws_order = []
+
+    while True:
+        get_line()
+        if re.match('enum ofpraw', line):
+            break
+
+    while True:
+        get_line()
+        first_line_number = line_number
+        here = '%s:%d' % (file_name, line_number)
+        if (line.startswith('/*')
+            or line.startswith(' *')
+            or not line
+            or line.isspace()):
+            continue
+        elif re.match('}', line):
+            break
+
+        if not line.lstrip().startswith('/*'):
+            fatal("unexpected syntax between ofpraw types")
+
+        comment = line.lstrip()[2:].strip()
+        while not comment.endswith('*/'):
+            get_line()
+            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(r'([A-Z]+) ([-.+\d]+) \((\d+)\): ([^.]+)\.$', comment)
+        if not m:
+            fatal("unexpected syntax between messages")
+        type_, versions, number, contents = m.groups()
+        number = int(number)
+
+        get_line()
+        m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_,
+                     line)
+        if not m:
+            fatal("syntax error expecting OFPRAW_ enum")
+        vinfix, name = m.groups()
+        rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name)
+
+        min_version, max_version = version_map[versions]
+
+        human_name = '%s_%s' % (type_, name)
+        if type_.endswith('ST'):
+            if rawname.endswith('_REQUEST'):
+                human_name = human_name[:-8] + " request"
+            elif rawname.endswith('_REPLY'):
+                human_name = human_name[:-6] + " reply"
+            else:
+                fatal("%s messages are statistics but %s doesn't end "
+                      "in _REQUEST or _REPLY" % (type_, rawname))
+
+        these_hdrs = []
+        for version in range(min_version, max_version + 1):
+            if type_ == 'OFPT':
+                if number == OFPT_VENDOR:
+                    fatal("OFPT (%d) is used for vendor extensions"
+                          % number)
+                elif (version == OFP10_VERSION
+                      and (number == OFPT10_STATS_REQUEST
+                           or number == OFPT10_STATS_REPLY)):
+                    fatal("OFPT 1.0 (%d) is used for stats messages"
+                          % number)
+                elif (version != OFP10_VERSION
+                      and (number == OFPT11_STATS_REQUEST
+                           or number == OFPT11_STATS_REPLY)):
+                    fatal("OFPT 1.1+ (%d) is used for stats messages"
+                          % number)
+                hdrs = (version, number, 0, 0, 0)
+            elif type_ == 'OFPST' and name.endswith('_REQUEST'):
+                if version == OFP10_VERSION:
+                    hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0)
+                else:
+                    hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0)
+            elif type_ == 'OFPST' and name.endswith('_REPLY'):
+                if version == OFP10_VERSION:
+                    hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
+                else:
+                    hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
+            elif type_ == 'NXT':
+                hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
+            elif type_ == 'NXST' and name.endswith('_REQUEST'):
+                if version == OFP10_VERSION:
+                    hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
+                            NX_VENDOR_ID, number)
+                else:
+                    hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
+                            NX_VENDOR_ID, number)
+            elif type_ == 'NXST' and name.endswith('_REPLY'):
+                if version == OFP10_VERSION:
+                    hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
+                            NX_VENDOR_ID, number)
+                else:
+                    hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
+                            NX_VENDOR_ID, number)
+            else:
+                fatal("type '%s' unknown" % type_)
+
+            if hdrs in all_hdrs:
+                error("Duplicate message definition for %s." % str(hdrs))
+                sys.stderr.write("%s: Here is the location "
+                                 "of the previous definition.\n"
+                                 % (all_hdrs[hdrs]))
+            all_hdrs[hdrs] = here
+            these_hdrs.append(hdrs)
+
+        extra_multiple = '0'
+        if contents == 'void':
+            min_body = '0'
+        else:
+            min_body_elem = []
+            for c in [s.strip() for s in contents.split(",")]:
+                if c.endswith('[]'):
+                    if extra_multiple == '0':
+                        extra_multiple = make_sizeof(c[:-2])
+                    else:
+                        error("Cannot have multiple [] elements")
+                else:
+                    min_body_elem.append(c)
+
+            if min_body_elem:
+                min_body = " + ".join([make_sizeof(s)
+                                       for s in min_body_elem])
+            else:
+                if extra_multiple == '0':
+                    error("Must specify contents (use 'void' if empty)")
+                min_body = 0
+
+        if rawname in all_raws:
+            fatal("%s: Duplicate name" % rawname)
+
+        all_raws[rawname] = {"hdrs": these_hdrs,
+                             "min_version": min_version,
+                             "max_version": max_version,
+                             "min_body": min_body,
+                             "extra_multiple": extra_multiple,
+                             "type": type_,
+                             "human_name": human_name,
+                             "line": first_line_number}
+        all_raws_order.append(rawname)
+
+        continue
+
+    while True:
+        get_line()
+        if re.match('enum ofptype', line):
+            break
+
+    while True:
+        get_line()
+        if re.match(r'\s*/?\*', line) or line.isspace():
+            continue
+        elif re.match('}', line):
+            break
+
+        if not re.match(r'\s*OFPTYPE_.*/\*', line):
+            fatal("unexpected syntax between OFPTYPE_ definitions")
+
+        syntax = line.strip()
+        while not syntax.endswith('*/'):
+            get_line()
+            if not line.strip().startswith('*'):
+                fatal("unexpected syntax within OFPTYPE_ definition")
+            syntax += ' %s' % line.strip().lstrip('* \t')
+            syntax = syntax.strip()
+
+        m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax)
+        if not m:
+            fatal("syntax error in OFPTYPE_ definition")
+
+        ofptype, raws_ = m.groups()
+        raws = [s.rstrip('.') for s in raws_.split()]
+        for raw in raws:
+            if not re.match('OFPRAW_[A-Z0-9_]+$', raw):
+                fatal("%s: invalid OFPRAW_* name syntax" % raw)
+            if raw not in all_raws:
+                fatal("%s: not a declared OFPRAW_* name" % raw)
+            if "ofptype" in all_raws[raw]:
+                fatal("%s: already part of %s"
+                      % (raw, all_raws[raw]["ofptype"]))
+            all_raws[raw]["ofptype"] = ofptype
+
+    input_file.close()
+
+    if n_errors:
+        sys.exit(1)
+
+    output = []
+    output.append("/* Generated automatically; do not modify!     "
+                  "-*- buffer-read-only: t -*- */")
+    output.append("")
+
+    for raw in all_raws_order:
+        r = all_raws[raw]
+        output.append("static struct raw_instance %s_instances[] = {"
+                      % raw.lower())
+        for hdrs in r['hdrs']:
+            output.append("    { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 },"
+                          % (hdrs + (raw,)))
+                
+        output.append("};")
+
+    output.append("")
+
+    output.append("static struct raw_info raw_infos[] = {")
+    for raw in all_raws_order:
+        r = all_raws[raw]
+        if "ofptype" not in r:
+            error("%s: no defined OFPTYPE_" % raw)
+            continue
+        output.append("    {")
+        output.append("        %s_instances," % raw.lower())
+        output.append("        %d, %d," % (r["min_version"], r["max_version"]))
+        output.append("#line %s \"%s\"" % (r["line"], file_name))
+        output.append("        %s," % r["min_body"])
+        output.append("#line %s \"%s\"" % (r["line"], file_name))
+        output.append("        %s," % r["extra_multiple"])
+        output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name))
+        output.append("        %s," % r["ofptype"])
+        output.append("        \"%s\"," % r["human_name"])
+        output.append("    },")
+
+        if r['type'].endswith("ST"):
+            for hdrs in r['hdrs']:
+                op_hdrs = list(hdrs)
+                if hdrs[0] == OFP10_VERSION:
+                    if hdrs[1] == OFPT10_STATS_REQUEST:
+                        op_hdrs[1] = OFPT10_STATS_REPLY
+                    elif hdrs[1] == OFPT10_STATS_REPLY:
+                        op_hdrs[1] = OFPT10_STATS_REQUEST
+                    else:
+                        assert False
+                else:
+                    if hdrs[1] == OFPT11_STATS_REQUEST:
+                        op_hdrs[1] = OFPT11_STATS_REPLY
+                    elif hdrs[1] == OFPT11_STATS_REPLY:
+                        op_hdrs[1] = OFPT11_STATS_REQUEST
+                    else:
+                        assert False
+                if tuple(op_hdrs) not in all_hdrs:
+                    if r["human_name"].endswith("request"):
+                        fatal("%s has no corresponding reply"
+                              % r["human_name"])
+                    else:
+                        fatal("%s has no corresponding request"
+                              % r["human_name"])
+    output.append("};")
+
+    if n_errors:
+        sys.exit(1)
+
+    return output
+
+
+if __name__ == '__main__':
+    if '--help' in sys.argv:
+        usage()
+    elif len(sys.argv) != 3:
+        sys.stderr.write("exactly one non-option arguments required; "
+                         "use --help for help\n")
+        sys.exit(1)
+    else:
+        global file_name
+        global input_file
+        global line_number
+        file_name = sys.argv[1]
+        input_file = open(file_name)
+        line_number = 0
+
+        for line in extract_ofp_msgs(sys.argv[2]):
+            print line
+        
index 3cc4c2e..333d037 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2008, 2011 Nicira Networks.
+# Copyright (c) 2008, 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.
index 924295c..2162564 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2008 Nicira Networks.
+# Copyright (c) 2008 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
index a2fcfba..9bdffea 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 AC_PREREQ(2.64)
-AC_INIT(openvswitch, 1.6.90, ovs-bugs@openvswitch.org)
+AC_INIT(openvswitch, 1.8.90, ovs-bugs@openvswitch.org)
 AC_CONFIG_SRCDIR([datapath/datapath.c])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
@@ -41,6 +41,7 @@ AC_SYS_LARGEFILE
 AC_SEARCH_LIBS([pow], [m])
 AC_SEARCH_LIBS([clock_gettime], [rt])
 AC_SEARCH_LIBS([timer_create], [rt])
+AC_SEARCH_LIBS([pcap_open_live], [pcap])
 
 OVS_CHECK_COVERAGE
 OVS_CHECK_NDEBUG
@@ -52,6 +53,7 @@ OVS_CHECK_PYUIC4
 OVS_CHECK_OVSDBMONITOR
 OVS_CHECK_DOT
 OVS_CHECK_IF_PACKET
+OVS_CHECK_IF_DL
 OVS_CHECK_STRTOK_R
 AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec],
   [], [], [[#include <sys/stat.h>]])
@@ -60,6 +62,7 @@ AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h])
 
 OVS_CHECK_PKIDIR
 OVS_CHECK_RUNDIR
+OVS_CHECK_DBDIR
 OVS_CHECK_BACKTRACE
 OVS_CHECK_MALLOC_HOOKS
 OVS_CHECK_VALGRIND
@@ -68,6 +71,7 @@ OVS_CHECK_LINKER_SECTIONS
 OVS_CHECK_XENSERVER_VERSION
 OVS_CHECK_GROFF
 OVS_CHECK_BRCOMPAT
+OVS_CHECK_GNU_MAKE
 
 OVS_ENABLE_OPTION([-Wall])
 OVS_ENABLE_OPTION([-Wno-sign-compare])
index b8a048b..8fa2dd1 100644 (file)
@@ -118,7 +118,7 @@ essentially like this, ignoring metadata:
 Naively, to add VLAN support, it makes sense to add a new "vlan" flow
 key attribute to contain the VLAN tag, then continue to decode the
 encapsulated headers beyond the VLAN tag using the existing field
-definitions.  With this change, an TCP packet in VLAN 10 would have a
+definitions.  With this change, a TCP packet in VLAN 10 would have a
 flow key much like this:
 
     eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...)
index 2903801..208f260 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index e7f741b..1671b90 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -569,7 +569,7 @@ module_init(brc_init);
 module_exit(brc_cleanup);
 
 MODULE_DESCRIPTION("Open vSwitch bridge compatibility");
-MODULE_AUTHOR("Nicira Networks");
+MODULE_AUTHOR("Nicira, Inc.");
 MODULE_LICENSE("GPL");
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
index 2a84e9a..5146c65 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 5ea2a52..2f2ffee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 71fa06c..3113b96 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 2fe6fdd..c83ce16 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -39,7 +39,6 @@
 #include <linux/version.h>
 #include <linux/ethtool.h>
 #include <linux/wait.h>
-#include <asm/system.h>
 #include <asm/div64.h>
 #include <linux/highmem.h>
 #include <linux/netfilter_bridge.h>
@@ -62,8 +61,8 @@
 #include "vport-internal_dev.h"
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) || \
-    LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
-#error Kernels before 2.6.18 or after 3.3 are not supported by this version of Open vSwitch.
+    LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+#error Kernels before 2.6.18 or after 3.5 are not supported by this version of Open vSwitch.
 #endif
 
 #define REHASH_FLOW_INTERVAL (10 * 60 * HZ)
@@ -405,14 +404,15 @@ static int queue_gso_packets(struct net *net, int dp_ifindex,
                             struct sk_buff *skb,
                             const struct dp_upcall_info *upcall_info)
 {
+       unsigned short gso_type = skb_shinfo(skb)->gso_type;
        struct dp_upcall_info later_info;
        struct sw_flow_key later_key;
        struct sk_buff *segs, *nskb;
        int err;
 
        segs = skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM);
-       if (IS_ERR(skb))
-               return PTR_ERR(skb);
+       if (IS_ERR(segs))
+               return PTR_ERR(segs);
 
        /* Queue all of the segments. */
        skb = segs;
@@ -421,7 +421,7 @@ static int queue_gso_packets(struct net *net, int dp_ifindex,
                if (err)
                        break;
 
-               if (skb == segs && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) {
+               if (skb == segs && gso_type & SKB_GSO_UDP) {
                        /* The initial flow key extracted by ovs_flow_extract()
                         * in this case is for a first fragment, so we need to
                         * properly mark later fragments.
@@ -558,6 +558,19 @@ static int validate_sample(const struct nlattr *attr,
        return validate_actions(actions, key, depth + 1);
 }
 
+static int validate_tp_port(const struct sw_flow_key *flow_key)
+{
+       if (flow_key->eth.type == htons(ETH_P_IP)) {
+               if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst)
+                       return 0;
+       } else if (flow_key->eth.type == htons(ETH_P_IPV6)) {
+               if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst)
+                       return 0;
+       }
+
+       return -EINVAL;
+}
+
 static int validate_set(const struct nlattr *a,
                        const struct sw_flow_key *flow_key)
 {
@@ -584,7 +597,7 @@ static int validate_set(const struct nlattr *a,
                if (flow_key->eth.type != htons(ETH_P_IP))
                        return -EINVAL;
 
-               if (!flow_key->ipv4.addr.src || !flow_key->ipv4.addr.dst)
+               if (!flow_key->ip.proto)
                        return -EINVAL;
 
                ipv4_key = nla_data(ovs_key);
@@ -600,18 +613,13 @@ static int validate_set(const struct nlattr *a,
                if (flow_key->ip.proto != IPPROTO_TCP)
                        return -EINVAL;
 
-               if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst)
-                       return -EINVAL;
-
-               break;
+               return validate_tp_port(flow_key);
 
        case OVS_KEY_ATTR_UDP:
                if (flow_key->ip.proto != IPPROTO_UDP)
                        return -EINVAL;
 
-               if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst)
-                       return -EINVAL;
-               break;
+               return validate_tp_port(flow_key);
 
        default:
                return -EINVAL;
@@ -1399,6 +1407,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        dp->ifobj.kset = NULL;
        kobject_init(&dp->ifobj, &dp_ktype);
 
+       ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
+
        /* Allocate table. */
        err = -ENOMEM;
        rcu_assign_pointer(dp->table, ovs_flow_tbl_alloc(TBL_MIN_BUCKETS));
@@ -1410,7 +1420,6 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
                err = -ENOMEM;
                goto err_destroy_table;
        }
-       ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
 
        dp->ports = kmalloc(DP_VPORT_HASH_BUCKETS * sizeof(struct hlist_head),
                            GFP_KERNEL);
@@ -1465,6 +1474,7 @@ err_destroy_percpu:
 err_destroy_table:
        ovs_flow_tbl_destroy(genl_dereference(dp->table));
 err_free_dp:
+       release_net(ovs_dp_get_net(dp));
        kfree(dp);
 err_unlock_rtnl:
        rtnl_unlock();
@@ -1891,10 +1901,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq,
                                         OVS_VPORT_CMD_NEW);
        if (IS_ERR(reply)) {
-               err = PTR_ERR(reply);
                netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
-                               ovs_dp_vport_multicast_group.id, err);
-               return 0;
+                               ovs_dp_vport_multicast_group.id, PTR_ERR(reply));
+               goto exit_unlock;
        }
 
        genl_notify(reply, genl_info_net(info), info->snd_pid,
index 18c8598..affbf0e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 48023a9..13085d6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -41,18 +41,19 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
        case NETDEV_UNREGISTER:
                if (!ovs_is_internal_dev(dev)) {
                        struct sk_buff *notify;
+                       struct datapath *dp = vport->dp;
 
                        notify = ovs_vport_cmd_build_info(vport, 0, 0,
                                                          OVS_VPORT_CMD_DEL);
                        ovs_dp_detach_port(vport);
                        if (IS_ERR(notify)) {
-                               netlink_set_err(GENL_SOCK(ovs_dp_get_net(vport->dp)), 0,
+                               netlink_set_err(GENL_SOCK(ovs_dp_get_net(dp)), 0,
                                                ovs_dp_vport_multicast_group.id,
                                                PTR_ERR(notify));
                                break;
                        }
 
-                       genlmsg_multicast_netns(ovs_dp_get_net(vport->dp), notify, 0,
+                       genlmsg_multicast_netns(ovs_dp_get_net(dp), notify, 0,
                                                ovs_dp_vport_multicast_group.id,
                                                GFP_KERNEL);
                }
index c981285..526b0a1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index ec673ce..3ecacd7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 436ee49..219a260 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 9f93550..d07337c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 5261fa8..5be481e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index c963489..ba8dfc0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 251aa0a..834de8e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 819247b..8ce6115 100644 (file)
@@ -37,6 +37,7 @@ openvswitch_headers += \
        linux/compat/include/linux/lockdep.h \
        linux/compat/include/linux/log2.h \
        linux/compat/include/linux/mutex.h \
+       linux/compat/include/linux/net.h \
        linux/compat/include/linux/netdevice.h \
        linux/compat/include/linux/netfilter_bridge.h \
        linux/compat/include/linux/netfilter_ipv4.h \
index bd6e9a4..812f213 100644 (file)
@@ -7,6 +7,7 @@
 #endif
 
 #include <linux/version.h>
+#include <linux/bug.h>
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
 #undef pr_emerg
 #define pr_emerg(fmt, ...) \
diff --git a/datapath/linux/compat/include/linux/net.h b/datapath/linux/compat/include/linux/net.h
new file mode 100644 (file)
index 0000000..5665e2e
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __LINUX_NET_WRAPPER_H
+#define __LINUX_NET_WRAPPER_H 1
+
+#include_next <linux/net.h>
+
+#ifndef net_ratelimited_function
+#define net_ratelimited_function(function, ...)                        \
+do {                                                           \
+       if (net_ratelimit())                                    \
+               function(__VA_ARGS__);                          \
+} while (0)
+
+#define net_emerg_ratelimited(fmt, ...)                                \
+       net_ratelimited_function(pr_emerg, fmt, ##__VA_ARGS__)
+#define net_alert_ratelimited(fmt, ...)                                \
+       net_ratelimited_function(pr_alert, fmt, ##__VA_ARGS__)
+#define net_crit_ratelimited(fmt, ...)                         \
+       net_ratelimited_function(pr_crit, fmt, ##__VA_ARGS__)
+#define net_err_ratelimited(fmt, ...)                          \
+       net_ratelimited_function(pr_err, fmt, ##__VA_ARGS__)
+#define net_notice_ratelimited(fmt, ...)                       \
+       net_ratelimited_function(pr_notice, fmt, ##__VA_ARGS__)
+#define net_warn_ratelimited(fmt, ...)                         \
+       net_ratelimited_function(pr_warn, fmt, ##__VA_ARGS__)
+#define net_info_ratelimited(fmt, ...)                         \
+       net_ratelimited_function(pr_info, fmt, ##__VA_ARGS__)
+#define net_dbg_ratelimited(fmt, ...)                          \
+       net_ratelimited_function(pr_debug, fmt, ##__VA_ARGS__)
+#endif
+
+#endif
index 01e524e..7fbe9d8 100644 (file)
@@ -238,7 +238,7 @@ static inline bool skb_warn_if_lro(const struct sk_buff *skb)
 #define consume_skb kfree_skb
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
+#ifndef HAVE_SKB_FRAG_PAGE
 static inline struct page *skb_frag_page(const skb_frag_t *frag)
 {
        return frag->page;
index 919afe3..cb48863 100644 (file)
@@ -1,23 +1,19 @@
 #ifndef __LINUX_WORKQUEUE_WRAPPER_H
 #define __LINUX_WORKQUEUE_WRAPPER_H 1
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
-#include_next <linux/workqueue.h>
-static inline int __init ovs_workqueues_init(void) { return 0; }
-static inline void  ovs_workqueues_exit(void) {}
-
-#else
 #include <linux/timer.h>
 
 int __init ovs_workqueues_init(void);
 void ovs_workqueues_exit(void);
 
-
 /* Older kernels have an implementation of work queues with some very bad
  * characteristics when trying to cancel work (potential deadlocks, use after
  * free, etc.  Therefore we implement simple ovs specific work queue using
  * single worker thread. work-queue API are kept similar for compatibility.
+ * It seems it is useful even on newer kernel. As it can avoid system wide
+ * freeze in event of softlockup due to workq blocked on genl_lock.
  */
+
 struct work_struct;
 
 typedef void (*work_func_t)(struct work_struct *work);
@@ -29,6 +25,9 @@ struct work_struct {
        atomic_long_t data;
        struct list_head entry;
        work_func_t func;
+#ifdef CONFIG_LOCKDEP
+       struct lockdep_map lockdep_map;
+#endif
 };
 
 #define WORK_DATA_INIT()        ATOMIC_LONG_INIT(0)
@@ -68,6 +67,6 @@ int cancel_delayed_work_sync(struct delayed_work *dwork);
                (_work)->func = (_func);                        \
        } while (0)
 
-#endif /* kernel version < 2.6.23 */
+extern void flush_scheduled_work(void);
 
 #endif
index 883665b..9934f1a 100644 (file)
@@ -23,8 +23,6 @@
 #include <linux/lockdep.h>
 #include <linux/idr.h>
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
-
 static spinlock_t wq_lock;
 static struct list_head workq;
 static wait_queue_head_t more_work;
@@ -210,4 +208,3 @@ void  ovs_workqueues_exit(void)
        BUG_ON(!list_empty(&workq));
        kthread_stop(workq_thread);
 }
-#endif
index d406dbc..d651c11 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -1002,12 +1002,15 @@ unlock:
 static struct rtable *__find_route(const struct tnl_mutable_config *mutable,
                                   u8 ipproto, u8 tos)
 {
+       /* Tunnel configuration keeps DSCP part of TOS bits, But Linux
+        * router expect RT_TOS bits only. */
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
        struct flowi fl = { .nl_u = { .ip4_u = {
                                        .daddr = mutable->key.daddr,
                                        .saddr = mutable->key.saddr,
-                                       .tos = tos } },
-                           .proto = ipproto };
+                                       .tos   = RT_TOS(tos) } },
+                                       .proto = ipproto };
        struct rtable *rt;
 
        if (unlikely(ip_route_output_key(port_key_get_net(&mutable->key), &rt, &fl)))
@@ -1017,7 +1020,7 @@ static struct rtable *__find_route(const struct tnl_mutable_config *mutable,
 #else
        struct flowi4 fl = { .daddr = mutable->key.daddr,
                             .saddr = mutable->key.saddr,
-                            .flowi4_tos = tos,
+                            .flowi4_tos = RT_TOS(tos),
                             .flowi4_proto = ipproto };
 
        return ip_route_output_key(port_key_get_net(&mutable->key), &fl);
@@ -1034,7 +1037,7 @@ static struct rtable *find_route(struct vport *vport,
        *cache = NULL;
        tos = RT_TOS(tos);
 
-       if (likely(tos == mutable->tos &&
+       if (likely(tos == RT_TOS(mutable->tos) &&
            check_cache_valid(cur_cache, mutable))) {
                *cache = cur_cache;
                return cur_cache->rt;
@@ -1045,7 +1048,7 @@ static struct rtable *find_route(struct vport *vport,
                if (IS_ERR(rt))
                        return NULL;
 
-               if (likely(tos == mutable->tos))
+               if (likely(tos == RT_TOS(mutable->tos)))
                        *cache = build_cache(vport, mutable, rt);
 
                return rt;
@@ -1219,8 +1222,6 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
        else
                tos = mutable->tos;
 
-       tos = INET_ECN_encapsulate(tos, inner_tos);
-
        /* Route lookup */
        rt = find_route(vport, mutable, tos, &cache);
        if (unlikely(!rt))
@@ -1228,6 +1229,8 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
        if (unlikely(!cache))
                unattached_dst = &rt_dst(rt);
 
+       tos = INET_ECN_encapsulate(tos, inner_tos);
+
        /* Reset SKB */
        nf_reset(skb);
        secpath_reset(skb);
@@ -1402,7 +1405,8 @@ static int tnl_set_config(struct net *net, struct nlattr *options,
 
        if (a[OVS_TUNNEL_ATTR_TOS]) {
                mutable->tos = nla_get_u8(a[OVS_TUNNEL_ATTR_TOS]);
-               if (mutable->tos != RT_TOS(mutable->tos))
+               /* Reject ToS config with ECN bits set. */
+               if (mutable->tos & INET_ECN_MASK)
                        return -EINVAL;
        }
 
index 33eb63c..1924017 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 8b4e05d..104ed55 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index d6e9b32..5d3573b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index e5b7afb..05a099d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  * Distributed under the terms of the GNU GPL version 2.
  *
  * Significant portions of this file may be copied from parts of the Linux
index b33cc94..5c0a6f8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index b10f28a..09b0b7c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index ff55b85..4a295c7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 3bb55f0..ab89c5b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 9647a61..4dc2eb4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -25,6 +25,9 @@
 #include <linux/skbuff.h>
 #include <linux/version.h>
 
+#include <net/dst.h>
+#include <net/xfrm.h>
+
 #include "checksum.h"
 #include "datapath.h"
 #include "vlan.h"
@@ -289,6 +292,11 @@ static int internal_dev_recv(struct vport *vport, struct sk_buff *skb)
 #endif
 
        len = skb->len;
+
+       skb_dst_drop(skb);
+       nf_reset(skb);
+       secpath_reset(skb);
+
        skb->dev = netdev;
        skb->pkt_type = PACKET_HOST;
        skb->protocol = eth_type_trans(skb, netdev);
index 3454447..9a7d30e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 05d7696..0098554 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -273,9 +273,9 @@ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
        ovs_vport_receive(vport, skb);
 }
 
-static unsigned packet_length(const struct sk_buff *skb)
+static unsigned int packet_length(const struct sk_buff *skb)
 {
-       unsigned length = skb->len - ETH_HLEN;
+       unsigned int length = skb->len - ETH_HLEN;
 
        if (skb->protocol == htons(ETH_P_8021Q))
                length -= VLAN_HLEN;
@@ -303,9 +303,9 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
        int len;
 
        if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) {
-               if (net_ratelimit())
-                       pr_warn("%s: dropped over-mtu packet: %d > %d\n",
-                               ovs_dp_name(vport->dp), packet_length(skb), mtu);
+               net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
+                                    ovs_dp_name(vport->dp),
+                                    packet_length(skb), mtu);
                goto error;
        }
 
index 721810b..c9cf5e5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index ea46a8d..d9e8970 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index b75a866..172261a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 2aafde0..b0cdeae 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
index 4425750..b6cb12e 100644 (file)
@@ -12,6 +12,7 @@ EXTRA_DIST += \
        debian/openvswitch-brcompat.postinst \
        debian/openvswitch-brcompat.postrm \
        debian/openvswitch-common.dirs \
+       debian/openvswitch-common.docs \
        debian/openvswitch-common.install \
        debian/openvswitch-common.manpages \
        debian/openvswitch-controller.README.Debian \
@@ -21,6 +22,7 @@ EXTRA_DIST += \
        debian/openvswitch-controller.install \
        debian/openvswitch-controller.manpages \
        debian/openvswitch-controller.postinst \
+       debian/openvswitch-controller.postrm \
        debian/openvswitch-datapath-module-_KVERS_.postinst.modules.in \
        debian/openvswitch-datapath-dkms.postinst \
        debian/openvswitch-datapath-dkms.prerm \
@@ -43,6 +45,7 @@ EXTRA_DIST += \
        debian/openvswitch-switch.postinst \
        debian/openvswitch-switch.postrm \
        debian/openvswitch-switch.template \
+       debian/openvswitch-switch.links \
        debian/openvswitch-test.dirs \
        debian/openvswitch-test.install \
        debian/openvswitch-test.manpages \
@@ -53,6 +56,7 @@ EXTRA_DIST += \
        debian/python-openvswitch.install \
        debian/rules \
        debian/rules.modules \
+       debian/ifupdown.sh \
        debian/source/format
 
 check-debian-changelog-version:
index a691190..7b8bedb 100644 (file)
@@ -1,9 +1,82 @@
-openvswitch (1.6.90-1) unstable; urgency=low
+openvswitch (1.8.90-1) unstable; urgency=low
+   [ Open vSwitch team ]
+   * New upstream version
+    - Nothing yet!  Try NEWS...
+
+ -- Open vSwitch team <dev@openvswitch.org>  Mon, 07 May 2012 14:14:52 +0900
+
+openvswitch (1.8.0-1) unstable; urgency=low
+   [ Open vSwitch team ]
+   * New upstream version
+    - New FAQ.  Please send updates and additions!
+    - Authors of controllers, please read the new section titled "Action
+      Reproduction" in DESIGN, which describes an Open vSwitch change in
+      behavior in corner cases that may affect some controllers.
+    - ovs-l3ping:
+        - A new test utility that can create L3 tunnel between two Open
+          vSwitches and detect connectivity issues.
+    - ovs-ofctl:
+        - New --sort and --rsort options for "dump-flows" command.
+        - "mod-port" command can now control all OpenFlow config flags.
+    - OpenFlow:
+      - Allow general bitwise masking for IPv4 and IPv6 addresses in
+        IPv4, IPv6, and ARP packets.  (Previously, only CIDR masks
+        were allowed.)
+      - Allow support for arbitrary Ethernet masks.  (Previously, only
+        the multicast bit in the destination address could be individually
+        masked.)
+      - New field OXM_OF_METADATA, to align with OpenFlow 1.1.
+      - The OFPST_QUEUE request now reports an error if a specified port or
+        queue does not exist, or for requests for a specific queue on all
+        ports, if the specified queue does not exist on any port.  (Previous
+        versions generally reported an empty set of results.)
+      - New "flow monitor" feature to allow controllers to be notified of
+        flow table changes as they happen.
+    - Additional protocols are not mirrored and dropped when forward-bpdu is
+      false.  For a full list, see the ovs-vswitchd.conf.db man page.
+    - Open vSwitch now sends RARP packets in situations where it previously
+      sent a custom protocol, making it consistent with behavior of QEMU and
+      VMware.
+    - All Open vSwitch programs and log files now show timestamps in UTC,
+      instead the local timezone, by default.
+
+ -- Open vSwitch team <dev@openvswitch.org>  Mon, 16 Jul 2012 16:44:52 +0900
+
+openvswitch (1.7.0-1) unstable; urgency=low
   [ Open vSwitch team ]
   * New upstream version
-    - Nothing yet!  Try NEWS...
+    - kernel modules are renamed. openvswitch_mod.ko is now
+      openvswitch.ko and brcompat_mod.ko is now brcompat.ko.
+    - Increased the number of NXM registers to 8.
+    - Added ability to configure dscp setting for manager and controller
+      connections.  By default, these connections have a DSCP value of
+      Internetwork Control (0xc0).
+    - Added the granular link health statistics, 'cfm_health', to an
+      interface.
+    - OpenFlow:
+        - Added support to mask nd_target for ICMPv6 neighbor discovery flows.
+        - Added support for OpenFlow 1.3 port description (OFPMP_PORT_DESC)
+          multipart messages.
+    - ovs-ofctl:
+        - Added the "dump-ports-desc" command to retrieve port
+          information using the new port description multipart messages.
+    - ovs-test:
+        - Added support for spawning ovs-test server from the client.
+        - Now ovs-test is able to automatically create test bridges and ports.
+    - "ovs-dpctl dump-flows" now prints observed TCP flags in TCP flows.
+    - Tripled flow setup performance.
+    - The "coverage/log" command previously available through ovs-appctl
+      has been replaced by "coverage/show".  The new command replies with
+      coverage counter values, instead of logging them.
+
+ -- Open vSwitch team <dev@openvswitch.org>  Mon, 30 Jul 2012 17:23:57 +0900
+
+openvswitch (1.6.1-1) unstable; urgency=low
+  [ Open vSwitch team ]
+  * New upstream version
+    - Allow OFPP_CONTROLLER as the in_port for packet-out messages.
 
- -- Open vSwitch team <dev@openvswitch.org>  Fri, 24 Feb 2012 11:12:52 +0900
+ -- Open vSwitch team <dev@openvswitch.org>  Mon, 25 Jun 2012 13:52:17 +0900
 
 openvswitch (1.6.0-1) unstable; urgency=low
   [ Open vSwitch team ]
@@ -66,7 +139,91 @@ openvswitch (1.5.0-1) unstable; urgency=low
     - ovsdb-tool now uses the typical database and schema installation
       directories as defaults.
 
- -- Open vSwitch team <dev@openvswitch.org>  Wed, 15 Feb 2012 11:12:48 +0900
+ -- Open vSwitch team <dev@openvswitch.org>  Fri, 01 June 2012 13:06:00 +0900
+
+openvswitch (1.4.0+git20120426-1) unstable; urgency=low
+
+  * New upstream release fixing the following bugs:
+    o Broken log rotation.
+    o Use-after-free error when ports disappear.
+    o Minor memory leaks.
+    o Testsuite failures on big-endian architectures.
+
+ -- Ben Pfaff <pfaffben@debian.org>  Thu, 26 Apr 2012 13:46:55 -0700
+
+openvswitch (1.4.0+git20120321-1) unstable; urgency=low
+
+  * New upstream version including:
+    o Features:
+      - ovs-vsctl: Allow "fake bridges" to be created for VLAN 0.
+      - vswitchd: Make the MAC entry aging time configurable.
+      - mac-learning: Increase MAC learning timeout to 300 seconds.
+    o Bug fixes:
+      - netdev-linux: Fix use-after-free when netdev_dump_queues() deletes
+        queues.
+      - netlink-socket: Increase Netlink socket receive buffer size.
+      - ofproto: Fix code that keeps track of MTU.
+      - ovs-monitor-ipsec: Detect correctly IPSEC configuration changes
+      - bond: Incorrectly reported an error in appctl.
+      - socket-util: Unlink Unix domain sockets that bind but fail to connect.
+      - bridge: Remove unwanted ports at time of ofproto creation.
+      - dpif-linux: Make dpif_linux_port_query_by_name() query only one
+        datapath.
+      - ofproto-dpif: Cleanup STP on ports when disabled on their bridge.
+      - configure: Try to extract kernel source directory from build Makefile.
+      - vswitchd: Always configure a default queue for QoS.
+      - ofproto-dpif: Don't output to in_port even if in_port is OFPP_LOCAL.
+      - sflow_agent: Use snprintf() in place of sprintf().
+    o Packaging:
+      - Move PKI directory to FHS-compliant location.  Closes: #661090.
+        Thanks to Andreas Beckmann <debian@abeckmann.de> for reporting
+        this bug.
+      - Use a different way to avoid failing install without kernel module.
+      - Avoid unit test failure when doing "unofficial" builds.
+      - Bump standards-version to 3.9.3.
+      - Remove some useless files from the dkms package.
+      - Clean .pyc files in "clean" target.
+      - Remove po-debconf build dependency.
+      - Build-depend on python-all to pull in all Python versions.
+      - Add missing ${python:Depends} to openvswitch-test package.
+      - Improve long descriptions so as to better describe the packages.
+      - Bump debhelper compat level to 8 and make build-depends consistent.
+      - Fix exit status of openvswitch-switch init script "status" command.
+      - Use provided kernel source dir instead of host kernel version.
+      - Do not run "make" if "configure" fails during DKMS build.
+      - Look in /lib/modules instead of /usr/src for DKMS kernel sources.
+      - Fix dependencies for openvswitch-datapath-dkms package.
+      - Don't install Python modules for obsolete Python versions.
+      - Add dependency on ${misc:Depends} to openvswitch-test
+    o Documentation improvements:
+      - ovsdb-doc: Use minus sign in negative numbers in nroff output.
+      - ovsdb-doc: Convert '-' preceding a number as a minus sign, not a
+        hyphen.
+      - ovsdb-doc: Put NAME section into generated manpage.
+      - Fix typo in manpage.
+      - vswitchd: Document behavior of 802.1p priorities with VLAN splinters.
+      - netdev: Fix typo in error message.
+      - INSTALL.Linux: minor typo
+
+  * Many thanks to Thomas Goirand <zigo@debian.org> for contributing many
+    of the packaging fixes listed above.
+
+  * This version fixes most of the lintian errors described in bug
+    #663051, but a few remain, so this upload does not close that bug.  I
+    believe that this upload should be suitable for downgrading that bug's
+    severity.
+
+ -- Ben Pfaff <pfaffben@debian.org>  Wed, 21 Mar 2012 10:00:28 -0700
+
+openvswitch (1.4.0-2+nmu1) unstable; urgency=low
+
+  * Non maintainer upload.
+  * Removes all patches in debian/patches, because they are patching stuff
+  inconditionally in debian/*, and this should be applied by default.
+  * Uses the correct ${kernel_source_dir} in debian/dkms.conf.in, so that
+  the kernel module builds as expected (Closes: #659685).
+
+ -- Thomas Goirand <zigo@debian.org>  Thu, 08 Mar 2012 08:46:24 +0000
 
 openvswitch (1.4.0-2) unstable; urgency=low
 
index 6c509fd..15ca3eb 100644 (file)
@@ -66,7 +66,7 @@ Description: Open vSwitch common components
 Package: openvswitch-switch
 Architecture: linux-any
 Suggests: openvswitch-datapath-module
-Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, openvswitch-common (= ${binary:Version}), module-init-tools, procps, uuid-runtime
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, openvswitch-common (= ${binary:Version}), module-init-tools, procps, uuid-runtime, netbase
 Description: Open vSwitch switch implementations
  Open vSwitch is a production quality, multilayer, software-based, Ethernet
  virtual switch. It is designed to enable massive network automation through
index c81db07..16e5559 100644 (file)
@@ -8,10 +8,11 @@ Upstream Authors (from AUTHORS):
 
 Upstream Copyright Holders:
 
-       Copyright (c) 2007, 2008, 2009, 2010, 2011 Nicira Networks.
+       Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
        Copyright (c) 2010 Jean Tourrilhes - HP-Labs.
        Copyright (c) 2008,2009,2010 Citrix Systems, Inc.
        and authors listed above.
+       Copyright (c) 2011 Gaetano Catalli
 
 License:
 
@@ -53,10 +54,7 @@ License:
   GNU Lesser General Public License version 2.1.
 
        utilities/bugtool/ovs-bugtool
-       utilities/bugtool/ovs-bugtool-cfm-show
-       utilities/bugtool/ovs-bugtool-lacp-show
-       utilities/bugtool/ovs-bugtool-tc-class-show
-       utilities/bugtool/ovs-bugtool-daemons-ver
+       utilities/bugtool/ovs-bugtool-*
        utilities/bugtool/plugins/kernel-info/openvswitch.xml
        utilities/bugtool/plugins/network-status/openvswitch.xml
        utilities/bugtool/plugins/system-configuration.xml
index eaf0541..0d3db76 100644 (file)
@@ -7,3 +7,4 @@ BUILT_MODULE_LOCATION[0]=datapath/linux/
 BUILT_MODULE_LOCATION[1]=datapath/linux/
 DEST_MODULE_LOCATION[0]=/kernel/drivers/net/openvswitch/
 DEST_MODULE_LOCATION[1]=/kernel/drivers/net/openvswitch/
+AUTOINSTALL=yes
diff --git a/debian/ifupdown.sh b/debian/ifupdown.sh
new file mode 100755 (executable)
index 0000000..bd6aec8
--- /dev/null
@@ -0,0 +1,87 @@
+#! /bin/sh
+
+# Copyright (c) 2012 Nicira, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Have a look at /usr/share/doc/openvswitch-switch/README.Debian
+# for more information about configuring the /etc/network/interfaces.
+
+if [ -z "${IF_OVS_TYPE}" ]; then
+    exit 0
+fi
+
+ovs_vsctl() {
+    ovs-vsctl --timeout=5 "$@"
+}
+
+if (ovs_vsctl --version) > /dev/null 2>&1; then :; else
+    exit 0
+fi
+
+if [ "${MODE}" = "start" ]; then
+    eval OVS_EXTRA=\"${IF_OVS_EXTRA}\"
+
+    case "${IF_OVS_TYPE}" in
+        OVSBridge)
+                ovs_vsctl -- --may-exist add-br "${IFACE}" ${IF_OVS_OPTIONS}\
+                         ${OVS_EXTRA+-- $OVS_EXTRA}
+
+                if [ ! -z "${IF_OVS_PORTS}" ]; then
+                    ifup --allow="${IFACE}" ${IF_OVS_PORTS}
+                fi
+                ;;
+        OVSPort)
+                ovs_vsctl -- --may-exist add-port "${IF_OVS_BRIDGE}"\
+                    "${IFACE}" ${IF_OVS_OPTIONS} \
+                    ${OVS_EXTRA+-- $OVS_EXTRA}
+
+                ifconfig "${IFACE}" up
+                ;;
+        OVSIntPort)
+                ovs_vsctl -- --may-exist add-port "${IF_OVS_BRIDGE}"\
+                    "${IFACE}" ${IF_OVS_OPTIONS} -- set Interface "${IFACE}"\
+                    type=internal ${OVS_EXTRA+-- $OVS_EXTRA}
+
+                ifconfig "${IFACE}" up
+                ;;
+        OVSBond)
+                ovs_vsctl -- --fake-iface add-bond "${IF_OVS_BRIDGE}"\
+                    "${IFACE}" ${IF_OVS_BONDS} ${IF_OVS_OPTIONS} \
+                    ${OVS_EXTRA+-- $OVS_EXTRA}
+
+                ifconfig "${IFACE}" up
+                ;;
+        *)
+                exit 0
+                ;;
+    esac
+elif [ "${MODE}" = "stop" ]; then
+    case "${IF_OVS_TYPE}" in
+        OVSBridge)
+                if [ ! -z "${IF_OVS_PORTS}" ]; then
+                    ifdown --allow="${IFACE}" ${IF_OVS_PORTS}
+                fi
+
+                ovs_vsctl -- --if-exists del-br "${IFACE}"
+                ;;
+        OVSPort|OVSIntPort|OVSBond)
+                ovs_vsctl -- --if-exists del-port "${IF_OVS_BRIDGE}" "${IFACE}"
+                ;;
+        *)
+                exit 0
+                ;;
+    esac
+fi
+
+exit 0
diff --git a/debian/openvswitch-common.docs b/debian/openvswitch-common.docs
new file mode 100644 (file)
index 0000000..d6360a1
--- /dev/null
@@ -0,0 +1 @@
+FAQ
index 1805582..3b1fd93 100644 (file)
@@ -6,7 +6,4 @@ usr/bin/ovs-pki
 usr/bin/ovsdb-client
 usr/sbin/ovs-bugtool
 usr/share/openvswitch/bugtool-plugins
-usr/share/openvswitch/scripts/ovs-bugtool-cfm-show
-usr/share/openvswitch/scripts/ovs-bugtool-lacp-show
-usr/share/openvswitch/scripts/ovs-bugtool-tc-class-show
-usr/share/openvswitch/scripts/ovs-bugtool-daemons-ver
+usr/share/openvswitch/scripts/ovs-bugtool-*
index cfcd0a3..b073dde 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright (c) 2011 Nicira Networks Inc.
+# Copyright (c) 2011 Nicira, Inc.
 # Copyright (c) 2007, 2009 Javier Fernandez-Sanguino <jfs@debian.org>
 #
 # This is free software; you may redistribute it and/or modify
diff --git a/debian/openvswitch-controller.postrm b/debian/openvswitch-controller.postrm
new file mode 100755 (executable)
index 0000000..42cb523
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+# postrm script for openvswitch-controller
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+#        * <postrm> `remove'
+#        * <postrm> `purge'
+#        * <old-postrm> `upgrade' <new-version>
+#        * <new-postrm> `failed-upgrade' <old-version>
+#        * <new-postrm> `abort-install'
+#        * <new-postrm> `abort-install' <old-version>
+#        * <new-postrm> `abort-upgrade' <old-version>
+#        * <disappearer's-postrm> `disappear' <overwriter>
+#          <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+    purge)
+        if cd /etc/openvswitch-controller; then
+            rm -f cacert.pem cert.pem privkey.pem req.pem
+            rm -f tmp-privkey.pem tmp-cert.pem tmp-req.pem
+        fi
+        ;;
+
+    remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+        ;;
+
+    *)
+        echo "postrm called with unknown argument \`$1'" >&2
+        exit 1
+        ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
index 32cba23..8151de1 100644 (file)
@@ -1,10 +1,10 @@
 Upstream Authors: 
 
-    Nicira Networks
+    Nicira, Inc.
 
 Copyright: 
 
-    Copyright (C) 2008 Nicira Networks
+    Copyright (C) 2008 Nicira, Inc.
 
 License:
 
index ef92d3e..8617e77 100644 (file)
@@ -1,10 +1,6 @@
 README.Debian for openvswitch-switch
 ---------------------------------
 
-* The switch must be configured before it can be used.  Edit
-  /etc/default/openvswitch-switch, then start the switch manually with
-  "/etc/init.d/openvswitch-switch start".
-
 * To use the Linux kernel-based switch implementation, you will need
   to build and install the Open vSwitch kernel module.  To do so, install
   the openvswitch-datapath-source package, then follow the instructions
@@ -13,4 +9,121 @@ README.Debian for openvswitch-switch
 * This package does not yet support the userspace datapath-based
   switch implementation.
 
- -- Ben Pfaff <blp@nicira.com>, Mon, 30 Aug 2010 09:51:19 -0700
+ -- Ben Pfaff <blp@nicira.com>, Fri,  6 Jul 2012 15:12:38 -0700
+
+Debian network scripts integration
+----------------------------------
+This package lets a user to optionally configure Open vSwitch bridges
+and ports from /etc/network/interfaces. Please refer to the interfaces(5)
+manpage for more details regarding /etc/network/interfaces.
+
+The stanzas that configure the OVS bridges should begin with "allow-ovs"
+followed by name of the bridge. Here is an example.
+allow-ovs br0
+
+The stanzas that configure the OVS ports should begin with
+"allow-${bridge-name}" followed by name of the port. Here is an example.
+allow-br0 eth0
+
+The following OVS specific "command" options are supported:
+
+    - ovs_type: This can either be OVSBridge, OVSPort, OVSIntPort or OVSBond
+      depending on whether you configure a bridge, port, an internal port or
+      a bond. This is a required option.
+
+    - ovs_ports: This option specifies all the ports that belong to a bridge.
+
+    - ovs_bridge: This options specifies a bridge to which a port belongs.
+      This is a required option for a port.
+
+    - ovs_bonds: This option specifies the list of physical interfaces to be
+      bonded together.
+
+    - ovs_options: This option lets you add extra arguments to a ovs-vsctl
+      command. See examples.
+
+    - ovs_extra: This option lets you run additional ovs-vsctl commands,
+      separated by "--" (double dash). Variables can be part of the "ovs_extra"
+      option. You can provide all the standard environmental variables
+      described in the interfaces(5) man page. You can also pass shell
+      commands.
+
+More implementation specific details can be seen in the examples.
+
+Examples:
+--------
+ex 1: A standalone bridge.
+
+allow-ovs br0
+iface br0 inet static
+    address 192.168.1.1
+    netmask 255.255.255.0
+    ovs_type OVSBridge
+
+ex 2: A bridge with one port.
+
+allow-ovs br0
+iface br0 inet dhcp
+    ovs_type OVSBridge
+    ovs_ports eth0
+
+allow-br0 eth0
+iface eth0 inet manual
+    ovs_bridge br0
+    ovs_type OVSPort
+
+ex 3: A bridge with multiple physical ports.
+
+allow-ovs br0
+iface br0 inet dhcp
+    ovs_type OVSBridge
+    ovs_ports eth0 eth1
+
+allow-br0 eth0
+iface eth0 inet manual
+    ovs_bridge br0
+    ovs_type OVSPort
+
+allow-br0 eth1
+iface eth1 inet manual
+    ovs_bridge br0
+    ovs_type OVSPort
+
+ex 4: A bridge with an OVS internal port.
+
+allow-ovs br1
+iface br1 inet static
+    address 192.168.1.1
+    netmask 255.255.255.0
+    ovs_type OVSBridge
+    ovs_ports vlan100
+
+allow-br1 vlan100
+iface vlan100 inet manual
+    ovs_bridge br1
+    ovs_type OVSIntPort
+    ovs_options tag=100
+    ovs_extra set interface ${IFACE} external-ids:iface-id=$(hostname -s)
+
+ex 5: Bonding.
+
+allow-ovs br2
+iface br2 inet static
+    address 192.170.1.1
+    netmask 255.255.255.0
+    ovs_type OVSBridge
+    ovs_ports bond0
+
+allow-br2 bond0
+iface bond0 inet manual
+    ovs_bridge br2
+    ovs_type OVSBond
+    ovs_bonds eth2 eth3
+    ovs_options bond_mode=balance-tcp lacp=active
+
+ex 6: Create and destroy bridges.
+
+ifup --allow=ovs $list_of_bridges
+ifdown --allow=ovs $list_of_bridges
+
+-- Gurucharan Shetty <gshetty@nicira.com>, Fri, 04 May 2012 12:58:19 -0700
index aebf21e..f650f87 100755 (executable)
@@ -1,6 +1,6 @@
 #! /bin/sh
 #
-# Copyright (C) 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -70,9 +70,8 @@ start () {
     if test X"$FORCE_COREFILES" != X; then
        set "$@" --force-corefiles="$FORCE_COREFILES"
     fi
+    set "$@" $OVS_CTL_OPTS
     "$@" || exit $?
-
-    ovs_ctl --protocol=gre enable-protocol
 }
 
 stop () {
index a7006ac..4d7a15b 100644 (file)
@@ -6,7 +6,9 @@ usr/bin/ovs-vsctl
 usr/bin/ovsdb-tool
 usr/sbin/ovs-vswitchd
 usr/sbin/ovsdb-server
+usr/share/openvswitch/scripts/ovs-check-dead-ifs
 usr/share/openvswitch/scripts/ovs-ctl
 usr/share/openvswitch/scripts/ovs-lib
 usr/share/openvswitch/scripts/ovs-save
 usr/share/openvswitch/vswitch.ovsschema
+debian/ifupdown.sh usr/share/openvswitch/scripts
diff --git a/debian/openvswitch-switch.links b/debian/openvswitch-switch.links
new file mode 100644 (file)
index 0000000..0d6d1e0
--- /dev/null
@@ -0,0 +1,2 @@
+usr/share/openvswitch/scripts/ifupdown.sh etc/network/if-pre-up.d/openvswitch
+usr/share/openvswitch/scripts/ifupdown.sh etc/network/if-post-down.d/openvswitch
index 6620a09..8b04240 100644 (file)
@@ -7,11 +7,8 @@
     rotate 30
     postrotate
     # Tell Open vSwitch daemons to reopen their log files
-    if [ -e /var/run/openvswitch/ovs-vswitchd.pid ]; then
-        ovs-appctl -t ovs-vswitchd vlog/reopen
-    fi
-    if [ -e /var/run/openvswitch/ovsdb-server.pid ]; then
-        ovs-appctl -t ovsdb-server vlog/reopen
-    fi
+    for pidfile in `cd /var/run/openvswitch && echo *.pid`; do
+        ovs-appctl -t "${pidfile%%.pid}" vlog/reopen
+    done
     endscript
 }
index baf37c8..88bf9fc 100755 (executable)
@@ -26,6 +26,7 @@ case "$1" in
         rm -f /etc/default/openvswitch-switch
         rm -f /var/log/openvswitch/ovs-vswitchd.log* || true
         rm -f /var/log/openvswitch/ovsdb-server.log* || true
+        rm -f /etc/openvswitch/system-id.conf
         ;;
 
     remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
index 46816ea..afa5dd3 100644 (file)
@@ -6,3 +6,7 @@
 # BRCOMPAT: If 'yes' and the openvswitch-brcompat package is installed, then
 # Linux bridge compatibility will be enabled.
 # BRCOMPAT=no
+
+# OVS_CTL_OPTS: Extra options to pass to ovs-ctl.  This is, for example,
+# a suitable place to specify --ovs-vswitchd-wrapper=valgrind.
+# OVS_CTL_OPTS=
index a36c828..a2ea413 100644 (file)
@@ -1,2 +1,3 @@
 usr/share/openvswitch/python/ovstest usr/lib/python2.6/dist-packages/
 usr/bin/ovs-test
+usr/bin/ovs-l3ping
index 683c978..1a7f6d4 100644 (file)
@@ -1 +1,2 @@
 _debian/utilities/ovs-test.8
+_debian/utilities/ovs-l3ping.8
index 5024277..ffaa979 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/python
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@ import ovs.util
 import ovs.daemon
 import ovs.db.idl
 import ovs.unixctl
+import ovs.unixctl.server
 import ovs.vlog
 
 vlog = ovs.vlog.Vlog("ovs-monitor-ipsec")
@@ -414,7 +415,7 @@ def main():
     ovs.daemon.daemonize()
 
     ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None)
-    error, unixctl_server = ovs.unixctl.UnixctlServer.create(None)
+    error, unixctl_server = ovs.unixctl.server.UnixctlServer.create(None)
     if error:
         ovs.util.ovs_fatal(error, "could not create unixctl server", vlog)
 
index 4a0e8b5..51a2e51 100755 (executable)
@@ -111,6 +111,7 @@ install-arch: build-arch
        $(MAKE) -C _debian DESTDIR=$(CURDIR)/debian/tmp install
        cp debian/openvswitch-switch.template debian/openvswitch-switch/usr/share/openvswitch/switch/default.template
        dh_install -s
+       dh_link -s
 
 # Must not depend on anything. This is to be called by
 # binary-arch/binary-indep
index 0578b5f..f5c9cca 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
  *
  * This file is offered under your choice of two licenses: Apache 2.0 or GNU
  * GPL 2.0 or later.  The permission statements for each of these licenses is
index fb05744..b88fb1c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 4b7bc07..38e8eef 100644 (file)
@@ -10,7 +10,7 @@ if HAVE_PYTHON
 SUFFIXES += .h .hstamp
 
 .h.hstamp:
-       $(PYTHON) $(srcdir)/build-aux/check-structs -I$(srcdir)/include $<
+       $(run_python) $(srcdir)/build-aux/check-structs -I$(srcdir)/include $<
        touch $@
 
 HSTAMP_FILES = \
index ccd6273..75bf6db 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
 #include "openflow/openflow.h"
 #include "openvswitch/types.h"
 
-/* The following vendor extensions, proposed by Nicira Networks, are not yet
+/* The following vendor extensions, proposed by Nicira, are not yet
  * standardized, so they are not included in openflow.h.  Some of them may be
  * suitable for standardization; others we never expect to standardize. */
 
@@ -73,66 +73,26 @@ struct nx_vendor_error {
 struct nicira_header {
     struct ofp_header header;
     ovs_be32 vendor;            /* NX_VENDOR_ID. */
-    ovs_be32 subtype;           /* One of NXT_* below. */
+    ovs_be32 subtype;           /* See the NXT numbers in ofp-msgs.h. */
 };
 OFP_ASSERT(sizeof(struct nicira_header) == 16);
 
-/* Values for the 'subtype' member of struct nicira_header. */
-enum nicira_type {
-    /* No longer used. */
-    NXT_STATUS_REQUEST__OBSOLETE = 0,
-    NXT_STATUS_REPLY__OBSOLETE = 1,
-    NXT_ACT_SET_CONFIG__OBSOLETE = 2,
-    NXT_ACT_GET_CONFIG__OBSOLETE = 3,
-    NXT_COMMAND_REQUEST__OBSOLETE = 4,
-    NXT_COMMAND_REPLY__OBSOLETE = 5,
-    NXT_FLOW_END_CONFIG__OBSOLETE = 6,
-    NXT_FLOW_END__OBSOLETE = 7,
-    NXT_MGMT__OBSOLETE = 8,
-    NXT_TUN_ID_FROM_COOKIE__OBSOLETE = 9,
-
-    /* Controller role support.  The request body is struct nx_role_request.
-     * The reply echos the request. */
-    NXT_ROLE_REQUEST = 10,
-    NXT_ROLE_REPLY = 11,
-
-    /* Flexible flow specification (aka NXM = Nicira Extended Match). */
-    NXT_SET_FLOW_FORMAT = 12,   /* Set flow format. */
-    NXT_FLOW_MOD = 13,          /* Analogous to OFPT_FLOW_MOD. */
-    NXT_FLOW_REMOVED = 14,      /* Analogous to OFPT_FLOW_REMOVED. */
-
-    /* Use the upper 8 bits of the 'command' member in struct ofp_flow_mod to
-     * designate the table to which a flow is to be added?  See the big comment
-     * on struct nx_flow_mod_table_id for more information. */
-    NXT_FLOW_MOD_TABLE_ID = 15,
-
-    /* Alternative PACKET_IN message formats. */
-    NXT_SET_PACKET_IN_FORMAT = 16, /* Set Packet In format. */
-    NXT_PACKET_IN = 17,            /* Nicira Packet In. */
-
-    /* Are the idle_age and hard_age members in struct nx_flow_stats supported?
-     * If so, the switch does not reply to this message (which consists only of
-     * a "struct nicira_header").  If not, the switch sends an error reply. */
-    NXT_FLOW_AGE = 18,
-
-    NXT_SET_ASYNC_CONFIG = 19,  /* struct nx_async_config. */
-    NXT_SET_CONTROLLER_ID = 20, /* struct nx_controller_id. */
-};
-
-/* Header for Nicira vendor stats request and reply messages. */
-struct nicira_stats_msg {
-    struct ofp_vendor_stats_msg vsm; /* Vendor NX_VENDOR_ID. */
+/* Header for Nicira vendor stats request and reply messages in OpenFlow
+ * 1.0. */
+struct nicira10_stats_msg {
+    struct ofp10_vendor_stats_msg vsm; /* Vendor NX_VENDOR_ID. */
     ovs_be32 subtype;           /* One of NXST_* below. */
     uint8_t pad[4];             /* Align to 64-bits. */
 };
-OFP_ASSERT(sizeof(struct nicira_stats_msg) == 24);
+OFP_ASSERT(sizeof(struct nicira10_stats_msg) == 24);
 
-/* Values for the 'subtype' member of struct nicira_stats_msg. */
-enum nicira_stats_type {
-    /* Flexible flow specification (aka NXM = Nicira Extended Match). */
-    NXST_FLOW,                  /* Analogous to OFPST_FLOW. */
-    NXST_AGGREGATE              /* Analogous to OFPST_AGGREGATE. */
+/* Header for Nicira vendor stats request and reply messages in OpenFlow
+ * 1.1. */
+struct nicira11_stats_msg {
+    struct ofp11_vendor_stats_msg vsm; /* Vendor NX_VENDOR_ID. */
+    ovs_be32 subtype;           /* One of NXST_* below. */
 };
+OFP_ASSERT(sizeof(struct nicira11_stats_msg) == 24);
 
 /* Fields to use when hashing flows. */
 enum nx_hash_fields {
@@ -158,7 +118,7 @@ enum nx_hash_fields {
  * instead of having the switch decide which table is most appropriate as
  * required by OpenFlow 1.0.  By default, the extension is disabled.
  *
- * When this feature is enabled, Open vSwitch treats struct ofp_flow_mod's
+ * When this feature is enabled, Open vSwitch treats struct ofp10_flow_mod's
  * 16-bit 'command' member as two separate fields.  The upper 8 bits are used
  * as the table ID, the lower 8 bits specify the command as usual.  A table ID
  * of 0xff is treated like a wildcarded table ID.
@@ -192,13 +152,10 @@ enum nx_hash_fields {
  *      table match, then none is modified or deleted.
  */
 struct nx_flow_mod_table_id {
-    struct ofp_header header;
-    ovs_be32 vendor;            /* NX_VENDOR_ID. */
-    ovs_be32 subtype;           /* NXT_FLOW_MOD_TABLE_ID. */
     uint8_t set;                /* Nonzero to enable, zero to disable. */
     uint8_t pad[7];
 };
-OFP_ASSERT(sizeof(struct nx_flow_mod_table_id) == 24);
+OFP_ASSERT(sizeof(struct nx_flow_mod_table_id) == 8);
 
 enum nx_packet_in_format {
     NXPIF_OPENFLOW10 = 0,       /* Standard OpenFlow 1.0 compatible. */
@@ -207,37 +164,44 @@ enum nx_packet_in_format {
 
 /* NXT_SET_PACKET_IN_FORMAT request. */
 struct nx_set_packet_in_format {
-    struct nicira_header nxh;
     ovs_be32 format;            /* One of NXPIF_*. */
 };
-OFP_ASSERT(sizeof(struct nx_set_packet_in_format) == 20);
+OFP_ASSERT(sizeof(struct nx_set_packet_in_format) == 4);
 
 /* NXT_PACKET_IN (analogous to OFPT_PACKET_IN).
  *
- * The NXT_PACKET_IN format is intended to model the OpenFlow-1.2 PACKET_IN
- * with some minor tweaks.  Most notably NXT_PACKET_IN includes the cookie of
- * the rule which triggered the NXT_PACKET_IN message, and the match fields are
- * in NXM format.
+ * NXT_PACKET_IN is similar to the OpenFlow 1.2 OFPT_PACKET_IN.  The
+ * differences are:
+ *
+ *     - NXT_PACKET_IN includes the cookie of the rule that triggered the
+ *       message.  (OpenFlow 1.3 OFPT_PACKET_IN also includes the cookie.)
+ *
+ *     - The metadata fields use NXM (instead of OXM) field numbers.
+ *
+ * Open vSwitch 1.9.0 and later omits metadata fields that are zero (as allowed
+ * by OpenFlow 1.2).  Earlier versions included all implemented metadata
+ * fields.
+ *
+ * Open vSwitch does not include non-metadata in the nx_match, because by
+ * definition that information can be found in the packet itself.  The format
+ * and the standards allow this, however, so controllers should be prepared to
+ * tolerate future changes.
  *
- * The match fields in the NXT_PACKET_IN are intended to contain flow
- * processing metadata collected at the time the NXT_PACKET_IN message was
- * triggered.  It is minimally required to contain the NXM_OF_IN_PORT of the
- * packet, but may include other NXM headers such as flow registers.  The match
- * fields are allowed to contain non-metadata (e.g. NXM_OF_ETH_SRC etc).
- * However, this information can typically be found in the packet directly, so
- * it may be redundant.
+ * The NXM format is convenient for reporting metadata values, but it is
+ * important not to interpret the format as matching against a flow, because it
+ * does not.  Nothing is being matched; arbitrary metadata masks would not be
+ * meaningful.
  *
  * Whereas in most cases a controller can expect to only get back NXM fields
  * that it set up itself (e.g. flow dumps will ordinarily report only NXM
  * fields from flows that the controller added), NXT_PACKET_IN messages might
  * contain fields that the controller does not understand, because the switch
  * might support fields (new registers, new protocols, etc.) that the
- * controller does not. Â The controller must prepared to tolerate these.
+ * controller does not.  The controller must prepared to tolerate these.
  *
  * The 'cookie' and 'table_id' fields have no meaning when 'reason' is
  * OFPR_NO_MATCH.  In this case they should be set to 0. */
 struct nx_packet_in {
-    struct nicira_header nxh;
     ovs_be32 buffer_id;       /* ID assigned by datapath. */
     ovs_be16 total_len;       /* Full length of frame. */
     uint8_t reason;           /* Reason packet is sent (one of OFPR_*). */
@@ -255,11 +219,11 @@ struct nx_packet_in {
      * The padding bytes preceding the Ethernet frame ensure that the IP
      * header (if any) following the Ethernet header is 32-bit aligned. */
 
-    /* uint8_t nxm_fields[...]; */ /* Match. */
+    /* uint8_t nxm_fields[...]; */ /* NXM headers. */
     /* uint8_t pad[2]; */          /* Align to 64 bit + 16 bit. */
     /* uint8_t data[0]; */         /* Ethernet frame. */
 };
-OFP_ASSERT(sizeof(struct nx_packet_in) == 40);
+OFP_ASSERT(sizeof(struct nx_packet_in) == 24);
 
 /* Configures the "role" of the sending controller.  The default role is:
  *
@@ -281,10 +245,9 @@ OFP_ASSERT(sizeof(struct nx_packet_in) == 40);
  *      messages, but they do receive OFPT_PORT_STATUS messages.
  */
 struct nx_role_request {
-    struct nicira_header nxh;
     ovs_be32 role;              /* One of NX_ROLE_*. */
 };
-OFP_ASSERT(sizeof(struct nx_role_request) == 20);
+OFP_ASSERT(sizeof(struct nx_role_request) == 4);
 
 enum nx_role {
     NX_ROLE_OTHER,              /* Default role, full access. */
@@ -305,14 +268,16 @@ enum nx_role {
  * with value 1<<2 == 4 in port_status_mask[1] determines whether the
  * controller will receive OFPT_PORT_STATUS messages with reason OFPPR_MODIFY
  * (value 2) when the controller has a "slave" role.
+ *
+ * As a side effect, for service controllers, this message changes the
+ * miss_send_len from default of zero to OFP_DEFAULT_MISS_SEND_LEN (128).
  */
 struct nx_async_config {
-    struct nicira_header nxh;
     ovs_be32 packet_in_mask[2];    /* Bitmasks of OFPR_* values. */
     ovs_be32 port_status_mask[2];  /* Bitmasks of OFPRR_* values. */
     ovs_be32 flow_removed_mask[2]; /* Bitmasks of OFPPR_* values. */
 };
-OFP_ASSERT(sizeof(struct nx_async_config) == 40);
+OFP_ASSERT(sizeof(struct nx_async_config) == 24);
 \f
 /* Nicira vendor flow actions. */
 
@@ -328,7 +293,7 @@ enum nx_action_subtype {
     NXAST_NOTE,                 /* struct nx_action_note */
     NXAST_SET_TUNNEL64,         /* struct nx_action_set_tunnel64 */
     NXAST_MULTIPATH,            /* struct nx_action_multipath */
-    NXAST_AUTOPATH,             /* struct nx_action_autopath */
+    NXAST_AUTOPATH__DEPRECATED, /* struct nx_action_autopath */
     NXAST_BUNDLE,               /* struct nx_action_bundle */
     NXAST_BUNDLE_LOAD,          /* struct nx_action_bundle */
     NXAST_RESUBMIT_TABLE,       /* struct nx_action_resubmit */
@@ -338,6 +303,7 @@ enum nx_action_subtype {
     NXAST_DEC_TTL,              /* struct nx_action_header */
     NXAST_FIN_TIMEOUT,          /* struct nx_action_fin_timeout */
     NXAST_CONTROLLER,           /* struct nx_action_controller */
+    NXAST_DEC_TTL_CNT_IDS,      /* struct nx_action_cnt_ids */
 };
 
 /* Header for Nicira-defined actions. */
@@ -426,7 +392,7 @@ OFP_ASSERT(sizeof(struct nx_action_set_tunnel) == 16);
  * Sets the encapsulating tunnel ID to a 64-bit value. */
 struct nx_action_set_tunnel64 {
     ovs_be16 type;                  /* OFPAT_VENDOR. */
-    ovs_be16 len;                   /* Length is 16. */
+    ovs_be16 len;                   /* Length is 24. */
     ovs_be32 vendor;                /* NX_VENDOR_ID. */
     ovs_be16 subtype;               /* NXAST_SET_TUNNEL64. */
     uint8_t pad[6];
@@ -540,7 +506,7 @@ OFP_ASSERT(sizeof(struct nx_action_pop_queue) == 16);
  */
 struct nx_action_reg_move {
     ovs_be16 type;                  /* OFPAT_VENDOR. */
-    ovs_be16 len;                   /* Length is 16. */
+    ovs_be16 len;                   /* Length is 24. */
     ovs_be32 vendor;                /* NX_VENDOR_ID. */
     ovs_be16 subtype;               /* NXAST_REG_MOVE. */
     ovs_be16 n_bits;                /* Number of bits. */
@@ -579,7 +545,7 @@ OFP_ASSERT(sizeof(struct nx_action_reg_move) == 24);
  */
 struct nx_action_reg_load {
     ovs_be16 type;                  /* OFPAT_VENDOR. */
-    ovs_be16 len;                   /* Length is 16. */
+    ovs_be16 len;                   /* Length is 24. */
     ovs_be32 vendor;                /* NX_VENDOR_ID. */
     ovs_be16 subtype;               /* NXAST_REG_LOAD. */
     ovs_be16 ofs_nbits;             /* (ofs << 6) | (n_bits - 1). */
@@ -1003,7 +969,7 @@ OFP_ASSERT(sizeof(struct nx_action_fin_timeout) == 16);
  */
 struct nx_action_autopath {
     ovs_be16 type;              /* OFPAT_VENDOR. */
-    ovs_be16 len;               /* Length is 20. */
+    ovs_be16 len;               /* Length is 24. */
     ovs_be32 vendor;            /* NX_VENDOR_ID. */
     ovs_be16 subtype;           /* NXAST_AUTOPATH. */
 
@@ -1104,6 +1070,35 @@ enum nx_bd_algorithm {
      * Uses the 'fields' and 'basis' parameters. */
     NX_BD_ALG_HRW /* Highest Random Weight. */
 };
+
+\f
+/* Action structure for NXAST_DEC_TTL_CNT_IDS.
+ *
+ * If the packet is not IPv4 or IPv6, does nothing.  For IPv4 or IPv6, if the
+ * TTL or hop limit is at least 2, decrements it by 1.  Otherwise, if TTL or
+ * hop limit is 0 or 1, sends a packet-in to the controllers with each of the
+ * 'n_controllers' controller IDs specified in 'cnt_ids'.
+ *
+ * (This differs from NXAST_DEC_TTL in that for NXAST_DEC_TTL the packet-in is
+ * sent only to controllers with id 0.)
+ */
+struct nx_action_cnt_ids {
+    ovs_be16 type;              /* OFPAT_VENDOR. */
+    ovs_be16 len;               /* Length including slaves. */
+    ovs_be32 vendor;            /* NX_VENDOR_ID. */
+    ovs_be16 subtype;           /* NXAST_DEC_TTL_CNT_IDS. */
+
+    ovs_be16 n_controllers;     /* Number of controllers. */
+    uint8_t zeros[4];           /* Must be zero. */
+
+    /* Followed by 1 or more controller ids.
+     *
+     * uint16_t cnt_ids[];        // Controller ids.
+     * uint8_t pad[];           // Must be 0 to 8-byte align cnt_ids[].
+     */
+};
+OFP_ASSERT(sizeof(struct nx_action_cnt_ids) == 16);
+
 \f
 /* Action structure for NXAST_OUTPUT_REG.
  *
@@ -1149,11 +1144,11 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 \f
 /* Flexible flow specifications (aka NXM = Nicira Extended Match).
  *
- * OpenFlow 1.0 has "struct ofp_match" for specifying flow matches.  This
+ * OpenFlow 1.0 has "struct ofp10_match" for specifying flow matches.  This
  * structure is fixed-length and hence difficult to extend.  This section
  * describes a more flexible, variable-length flow match, called "nx_match" for
  * short, that is also supported by Open vSwitch.  This section also defines a
- * replacement for each OpenFlow message that includes struct ofp_match.
+ * replacement for each OpenFlow message that includes struct ofp10_match.
  *
  *
  * Format
@@ -1211,7 +1206,7 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
  *     matches bit J in nxm_value.  A 0-bit in nxm_mask causes the
  *     corresponding bits in nxm_value and the field's value to be ignored.
  *     (The sense of the nxm_mask bits is the opposite of that used by the
- *     "wildcards" member of struct ofp_match.)
+ *     "wildcards" member of struct ofp10_match.)
  *
  *     When nxm_hasmask is 1, nxm_length is always even.
  *
@@ -1368,13 +1363,13 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
  *
  * Format: 48-bit Ethernet MAC address.
  *
- * Masking: The nxm_mask patterns 01:00:00:00:00:00 and FE:FF:FF:FF:FF:FF must
- *   be supported for NXM_OF_ETH_DST_W (as well as the trivial patterns that
- *   are all-0-bits or all-1-bits).  Support for other patterns and for masking
- *   of NXM_OF_ETH_SRC is optional. */
+ * Masking: Fully maskable, in versions 1.8 and later. Earlier versions only
+ *   supported the following masks for NXM_OF_ETH_DST_W: 00:00:00:00:00:00,
+ *   fe:ff:ff:ff:ff:ff, 01:00:00:00:00:00, ff:ff:ff:ff:ff:ff. */
 #define NXM_OF_ETH_DST    NXM_HEADER  (0x0000,  1, 6)
 #define NXM_OF_ETH_DST_W  NXM_HEADER_W(0x0000,  1, 6)
 #define NXM_OF_ETH_SRC    NXM_HEADER  (0x0000,  2, 6)
+#define NXM_OF_ETH_SRC_W  NXM_HEADER_W(0x0000,  2, 6)
 
 /* Packet's Ethernet type.
  *
@@ -1461,7 +1456,8 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
  *
  * Format: 32-bit integer in network byte order.
  *
- * Masking: Only CIDR masks are allowed, that is, masks that consist of N
+ * Masking: Fully maskable, in Open vSwitch 1.8 and later.  In earlier
+ *   versions, only CIDR masks are allowed, that is, masks that consist of N
  *   high-order bits set to 1 and the other 32-N bits set to 0. */
 #define NXM_OF_IP_SRC     NXM_HEADER  (0x0000,  7, 4)
 #define NXM_OF_IP_SRC_W   NXM_HEADER_W(0x0000,  7, 4)
@@ -1530,7 +1526,8 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
  *
  * Format: 32-bit integer in network byte order.
  *
- * Masking: Only CIDR masks are allowed, that is, masks that consist of N
+ * Masking: Fully maskable, in Open vSwitch 1.8 and later.  In earlier
+ *   versions, only CIDR masks are allowed, that is, masks that consist of N
  *   high-order bits set to 1 and the other 32-N bits set to 0. */
 #define NXM_OF_ARP_SPA    NXM_HEADER  (0x0000, 16, 4)
 #define NXM_OF_ARP_SPA_W  NXM_HEADER_W(0x0000, 16, 4)
@@ -1606,7 +1603,8 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
  *
  * Format: 128-bit IPv6 address.
  *
- * Masking: Only CIDR masks are allowed, that is, masks that consist of N
+ * Masking: Fully maskable, in Open vSwitch 1.8 and later.  In previous
+ *   versions, only CIDR masks are allowed, that is, masks that consist of N
  *   high-order bits set to 1 and the other 128-N bits set to 0. */
 #define NXM_NX_IPV6_SRC    NXM_HEADER  (0x0001, 19, 16)
 #define NXM_NX_IPV6_SRC_W  NXM_HEADER_W(0x0001, 19, 16)
@@ -1634,7 +1632,8 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
  *
  * Format: 128-bit IPv6 address.
  *
- * Masking: Only CIDR masks are allowed, that is, masks that consist of N
+ * Masking: Fully maskable, in Open vSwitch 1.8 and later.  In previous
+ *   versions, only CIDR masks are allowed, that is, masks that consist of N
  *   high-order bits set to 1 and the other 128-N bits set to 0. */
 #define NXM_NX_ND_TARGET     NXM_HEADER    (0x0001, 23, 16)
 #define NXM_NX_ND_TARGET_W   NXM_HEADER_W  (0x0001, 23, 16)
@@ -1764,26 +1763,23 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 
 enum nx_flow_format {
     NXFF_OPENFLOW10 = 0,         /* Standard OpenFlow 1.0 compatible. */
-    NXFF_NXM = 2                 /* Nicira extended match. */
+    NXFF_NXM = 2,                /* Nicira extended match. */
+    NXFF_OPENFLOW12 = 3          /* OpenFlow 1.2 format. */
 };
 
 /* NXT_SET_FLOW_FORMAT request. */
 struct nx_set_flow_format {
-    struct nicira_header nxh;
     ovs_be32 format;            /* One of NXFF_*. */
 };
-OFP_ASSERT(sizeof(struct nx_set_flow_format) == 20);
+OFP_ASSERT(sizeof(struct nx_set_flow_format) == 4);
 
 /* NXT_FLOW_MOD (analogous to OFPT_FLOW_MOD).
  *
  * It is possible to limit flow deletions and modifications to certain
- * cookies by using the NXM_NX_COOKIE and NXM_NX_COOKIE_W matches.  For
- * these commands, the "cookie" field is always ignored.  Flow additions
- * make use of the "cookie" field and ignore any NXM_NX_COOKIE*
- * definitions.
+ * cookies by using the NXM_NX_COOKIE(_W) matches.  The "cookie" field
+ * is used only to add or modify flow cookies.
  */
 struct nx_flow_mod {
-    struct nicira_header nxh;
     ovs_be64 cookie;              /* Opaque controller-issued identifier. */
     ovs_be16 command;             /* One of OFPFC_*. */
     ovs_be16 idle_timeout;        /* Idle time before discarding (seconds). */
@@ -1806,11 +1802,10 @@ struct nx_flow_mod {
      *     multiple of 8).
      */
 };
-OFP_ASSERT(sizeof(struct nx_flow_mod) == 48);
+OFP_ASSERT(sizeof(struct nx_flow_mod) == 32);
 
 /* NXT_FLOW_REMOVED (analogous to OFPT_FLOW_REMOVED). */
 struct nx_flow_removed {
-    struct nicira_header nxh;
     ovs_be64 cookie;          /* Opaque controller-issued identifier. */
     ovs_be16 priority;        /* Priority level of flow entry. */
     uint8_t reason;           /* One of OFPRR_*. */
@@ -1827,7 +1822,7 @@ struct nx_flow_removed {
      *   - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of
      *     all-zero bytes. */
 };
-OFP_ASSERT(sizeof(struct nx_flow_removed) == 56);
+OFP_ASSERT(sizeof(struct nx_flow_removed) == 40);
 
 /* Nicira vendor stats request of type NXST_FLOW (analogous to OFPST_FLOW
  * request).
@@ -1836,7 +1831,6 @@ OFP_ASSERT(sizeof(struct nx_flow_removed) == 56);
  * NXM_NX_COOKIE and NXM_NX_COOKIE_W matches.
  */
 struct nx_flow_stats_request {
-    struct nicira_stats_msg nsm;
     ovs_be16 out_port;        /* Require matching entries to include this
                                  as an output port.  A value of OFPP_NONE
                                  indicates no restriction. */
@@ -1851,7 +1845,7 @@ struct nx_flow_stats_request {
      *     message.
      */
 };
-OFP_ASSERT(sizeof(struct nx_flow_stats_request) == 32);
+OFP_ASSERT(sizeof(struct nx_flow_stats_request) == 8);
 
 /* Body for Nicira vendor stats reply of type NXST_FLOW (analogous to
  * OFPST_FLOW reply).
@@ -1882,8 +1876,7 @@ struct nx_flow_stats {
     ovs_be32 duration_sec;    /* Time flow has been alive in seconds. */
     ovs_be32 duration_nsec;   /* Time flow has been alive in nanoseconds
                                  beyond duration_sec. */
-    ovs_be16 priority;        /* Priority of the entry. Only meaningful
-                                 when this is not an exact-match entry. */
+    ovs_be16 priority;        /* Priority of the entry. */
     ovs_be16 idle_timeout;    /* Number of seconds idle before expiration. */
     ovs_be16 hard_timeout;    /* Number of seconds before expiration. */
     ovs_be16 match_len;       /* Length of nx_match. */
@@ -1903,9 +1896,11 @@ struct nx_flow_stats {
 OFP_ASSERT(sizeof(struct nx_flow_stats) == 48);
 
 /* Nicira vendor stats request of type NXST_AGGREGATE (analogous to
- * OFPST_AGGREGATE request). */
+ * OFPST_AGGREGATE request).
+ *
+ * The reply format is identical to the reply format for OFPST_AGGREGATE,
+ * except for the header. */
 struct nx_aggregate_stats_request {
-    struct nicira_stats_msg nsm;
     ovs_be16 out_port;        /* Require matching entries to include this
                                  as an output port.  A value of OFPP_NONE
                                  indicates no restriction. */
@@ -1920,18 +1915,7 @@ struct nx_aggregate_stats_request {
      *     message.
      */
 };
-OFP_ASSERT(sizeof(struct nx_aggregate_stats_request) == 32);
-
-/* Body for nicira_stats_msg reply of type NXST_AGGREGATE (analogous to
- * OFPST_AGGREGATE reply). */
-struct nx_aggregate_stats_reply {
-    struct nicira_stats_msg nsm;
-    ovs_be64 packet_count;     /* Number of packets, UINT64_MAX if unknown. */
-    ovs_be64 byte_count;       /* Number of bytes, UINT64_MAX if unknown. */
-    ovs_be32 flow_count;       /* Number of flows. */
-    uint8_t pad[4];            /* Align to 64 bits. */
-};
-OFP_ASSERT(sizeof(struct nx_aggregate_stats_reply) == 48);
+OFP_ASSERT(sizeof(struct nx_aggregate_stats_request) == 8);
 \f
 /* NXT_SET_CONTROLLER_ID.
  *
@@ -1943,11 +1927,10 @@ OFP_ASSERT(sizeof(struct nx_aggregate_stats_reply) == 48);
  * The NXAST_CONTROLLER action is the only current user of controller
  * connection IDs. */
 struct nx_controller_id {
-    struct nicira_header nxh;
     uint8_t zero[6];            /* Must be zero. */
     ovs_be16 controller_id;     /* New controller connection ID. */
 };
-OFP_ASSERT(sizeof(struct nx_controller_id) == 24);
+OFP_ASSERT(sizeof(struct nx_controller_id) == 8);
 
 /* Action structure for NXAST_CONTROLLER.
  *
@@ -1972,5 +1955,241 @@ struct nx_action_controller {
     uint8_t zero;                   /* Must be zero. */
 };
 OFP_ASSERT(sizeof(struct nx_action_controller) == 16);
+\f
+/* Flow Table Monitoring
+ * =====================
+ *
+ * NXST_FLOW_MONITOR allows a controller to keep track of changes to OpenFlow
+ * flow table(s) or subsets of them, with the following workflow:
+ *
+ * 1. The controller sends an NXST_FLOW_MONITOR request to begin monitoring
+ *    flows.  The 'id' in the request must be unique among all monitors that
+ *    the controller has started and not yet canceled on this OpenFlow
+ *    connection.
+ *
+ * 2. The switch responds with an NXST_FLOW_MONITOR reply.  If the request's
+ *    'flags' included NXFMF_INITIAL, the reply includes all the flows that
+ *    matched the request at the time of the request (with event NXFME_ADDED).
+ *    If 'flags' did not include NXFMF_INITIAL, the reply is empty.
+ *
+ *    The reply uses the xid of the request (as do all replies to OpenFlow
+ *    requests).
+ *
+ * 3. Whenever a change to a flow table entry matches some outstanding monitor
+ *    request's criteria and flags, the switch sends a notification to the
+ *    controller as an additional NXST_FLOW_MONITOR reply with xid 0.
+ *
+ *    When multiple outstanding monitors match a single change, only a single
+ *    notification is sent.  This merged notification includes the information
+ *    requested in any of the individual monitors.  That is, if any of the
+ *    matching monitors requests actions (NXFMF_ACTIONS), the notification
+ *    includes actions, and if any of the monitors request full changes for the
+ *    controller's own changes (NXFMF_OWN), the controller's own changes will
+ *    be included in full.
+ *
+ * 4. The controller may cancel a monitor with NXT_FLOW_MONITOR_CANCEL.  No
+ *    further notifications will be sent on the basis of the canceled monitor
+ *    afterward.
+ *
+ *
+ * Buffer Management
+ * =================
+ *
+ * OpenFlow messages for flow monitor notifications can overflow the buffer
+ * space available to the switch, either temporarily (e.g. due to network
+ * conditions slowing OpenFlow traffic) or more permanently (e.g. the sustained
+ * rate of flow table change exceeds the network bandwidth between switch and
+ * controller).
+ *
+ * When Open vSwitch's notification buffer space reaches a limiting threshold,
+ * OVS reacts as follows:
+ *
+ * 1. OVS sends an NXT_FLOW_MONITOR_PAUSED message to the controller, following
+ *    all the already queued notifications.  After it receives this message,
+ *    the controller knows that its view of the flow table, as represented by
+ *    flow monitor notifications, is incomplete.
+ *
+ * 2. As long as the notification buffer is not empty:
+ *
+ *        - NXMFE_ADD and NXFME_MODIFIED notifications will not be sent.
+ *
+ *        - NXFME_DELETED notifications will still be sent, but only for flows
+ *          that existed before OVS sent NXT_FLOW_MONITOR_PAUSED.
+ *
+ *        - NXFME_ABBREV notifications will not be sent.  They are treated as
+ *          the expanded version (and therefore only the NXFME_DELETED
+ *          components, if any, are sent).
+ *
+ * 3. When the notification buffer empties, OVS sends NXFME_ADD notifications
+ *    for flows added since the buffer reached its limit and NXFME_MODIFIED
+ *    notifications for flows that existed before the limit was reached and
+ *    changed after the limit was reached.
+ *
+ * 4. OVS sends an NXT_FLOW_MONITOR_RESUMED message to the controller.  After
+ *    it receives this message, the controller knows that its view of the flow
+ *    table, as represented by flow monitor notifications, is again complete.
+ *
+ * This allows the maximum buffer space requirement for notifications to be
+ * bounded by the limit plus the maximum number of supported flows.
+ *
+ *
+ * "Flow Removed" messages
+ * =======================
+ *
+ * The flow monitor mechanism is independent of OFPT_FLOW_REMOVED and
+ * NXT_FLOW_REMOVED.  Flow monitor updates for deletion are sent if
+ * NXFMF_DELETE is set on a monitor, regardless of whether the
+ * OFPFF_SEND_FLOW_REM flag was set when the flow was added. */
+
+/* NXST_FLOW_MONITOR request.
+ *
+ * The NXST_FLOW_MONITOR request's body consists of an array of zero or more
+ * instances of this structure.  The request arranges to monitor the flows
+ * that match the specified criteria, which are interpreted in the same way as
+ * for NXST_FLOW.
+ *
+ * 'id' identifies a particular monitor for the purpose of allowing it to be
+ * canceled later with NXT_FLOW_MONITOR_CANCEL.  'id' must be unique among
+ * existing monitors that have not already been canceled.
+ *
+ * The reply includes the initial flow matches for monitors that have the
+ * NXFMF_INITIAL flag set.  No single flow will be included in the reply more
+ * than once, even if more than one requested monitor matches that flow.  The
+ * reply will be empty if none of the monitors has NXFMF_INITIAL set or if none
+ * of the monitors initially matches any flows.
+ *
+ * For NXFMF_ADD, an event will be reported if 'out_port' matches against the
+ * actions of the flow being added or, for a flow that is replacing an existing
+ * flow, if 'out_port' matches against the actions of the flow being replaced.
+ * For NXFMF_DELETE, 'out_port' matches against the actions of a flow being
+ * deleted.  For NXFMF_MODIFY, an event will be reported if 'out_port' matches
+ * either the old or the new actions. */
+struct nx_flow_monitor_request {
+    ovs_be32 id;                /* Controller-assigned ID for this monitor. */
+    ovs_be16 flags;             /* NXFMF_*. */
+    ovs_be16 out_port;          /* Required output port, if not OFPP_NONE. */
+    ovs_be16 match_len;         /* Length of nx_match. */
+    uint8_t table_id;           /* One table's ID or 0xff for all tables. */
+    uint8_t zeros[5];           /* Align to 64 bits (must be zero). */
+    /* Followed by:
+     *   - Exactly match_len (possibly 0) bytes containing the nx_match, then
+     *   - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of
+     *     all-zero bytes. */
+};
+OFP_ASSERT(sizeof(struct nx_flow_monitor_request) == 16);
+
+/* 'flags' bits in struct nx_flow_monitor_request. */
+enum nx_flow_monitor_flags {
+    /* When to send updates. */
+    NXFMF_INITIAL = 1 << 0,     /* Initially matching flows. */
+    NXFMF_ADD = 1 << 1,         /* New matching flows as they are added. */
+    NXFMF_DELETE = 1 << 2,      /* Old matching flows as they are removed. */
+    NXFMF_MODIFY = 1 << 3,      /* Matching flows as they are changed. */
+
+    /* What to include in updates. */
+    NXFMF_ACTIONS = 1 << 4,     /* If set, actions are included. */
+    NXFMF_OWN = 1 << 5,         /* If set, include own changes in full. */
+};
+
+/* NXST_FLOW_MONITOR reply header.
+ *
+ * The body of an NXST_FLOW_MONITOR reply is an array of variable-length
+ * structures, each of which begins with this header.  The 'length' member may
+ * be used to traverse the array, and the 'event' member may be used to
+ * determine the particular structure.
+ *
+ * Every instance is a multiple of 8 bytes long. */
+struct nx_flow_update_header {
+    ovs_be16 length;            /* Length of this entry. */
+    ovs_be16 event;             /* One of NXFME_*. */
+    /* ...other data depending on 'event'... */
+};
+OFP_ASSERT(sizeof(struct nx_flow_update_header) == 4);
+
+/* 'event' values in struct nx_flow_update_header. */
+enum nx_flow_update_event {
+    /* struct nx_flow_update_full. */
+    NXFME_ADDED = 0,            /* Flow was added. */
+    NXFME_DELETED = 1,          /* Flow was deleted. */
+    NXFME_MODIFIED = 2,         /* Flow (generally its actions) was changed. */
+
+    /* struct nx_flow_update_abbrev. */
+    NXFME_ABBREV = 3,           /* Abbreviated reply. */
+};
+
+/* NXST_FLOW_MONITOR reply for NXFME_ADDED, NXFME_DELETED, and
+ * NXFME_MODIFIED. */
+struct nx_flow_update_full {
+    ovs_be16 length;            /* Length is 24. */
+    ovs_be16 event;             /* One of NXFME_*. */
+    ovs_be16 reason;            /* OFPRR_* for NXFME_DELETED, else zero. */
+    ovs_be16 priority;          /* Priority of the entry. */
+    ovs_be16 idle_timeout;      /* Number of seconds idle before expiration. */
+    ovs_be16 hard_timeout;      /* Number of seconds before expiration. */
+    ovs_be16 match_len;         /* Length of nx_match. */
+    uint8_t table_id;           /* ID of flow's table. */
+    uint8_t pad;                /* Reserved, currently zeroed. */
+    ovs_be64 cookie;            /* Opaque controller-issued identifier. */
+    /* Followed by:
+     *   - Exactly match_len (possibly 0) bytes containing the nx_match, then
+     *   - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of
+     *     all-zero bytes, then
+     *   - Actions to fill out the remainder 'length' bytes (always a multiple
+     *     of 8).  If NXFMF_ACTIONS was not specified, or 'event' is
+     *     NXFME_DELETED, no actions are included.
+     */
+};
+OFP_ASSERT(sizeof(struct nx_flow_update_full) == 24);
+
+/* NXST_FLOW_MONITOR reply for NXFME_ABBREV.
+ *
+ * When the controller does not specify NXFMF_OWN in a monitor request, any
+ * flow tables changes due to the controller's own requests (on the same
+ * OpenFlow channel) will be abbreviated, when possible, to this form, which
+ * simply specifies the 'xid' of the OpenFlow request (e.g. an OFPT_FLOW_MOD or
+ * NXT_FLOW_MOD) that caused the change.
+ *
+ * Some changes cannot be abbreviated and will be sent in full:
+ *
+ *   - Changes that only partially succeed.  This can happen if, for example,
+ *     a flow_mod with type OFPFC_MODIFY affects multiple flows, but only some
+ *     of those modifications succeed (e.g. due to hardware limitations).
+ *
+ *     This cannot occur with the current implementation of the Open vSwitch
+ *     software datapath.  It could happen with other datapath implementations.
+ *
+ *   - Changes that race with conflicting changes made by other controllers or
+ *     other flow_mods (not separated by barriers) by the same controller.
+ *
+ *     This cannot occur with the current Open vSwitch implementation
+ *     (regardless of datapath) because Open vSwitch internally serializes
+ *     potentially conflicting changes.
+ *
+ * A flow_mod that does not change the flow table will not trigger any
+ * notification, even an abbreviated one.  For example, a "modify" or "delete"
+ * flow_mod that does not match any flows will not trigger a notification.
+ * Whether an "add" or "modify" that specifies all the same parameters that a
+ * flow already has triggers a notification is unspecified and subject to
+ * change in future versions of Open vSwitch.
+ *
+ * OVS will always send the notifications for a given flow table change before
+ * the reply to a OFPT_BARRIER_REQUEST request that precedes the flow table
+ * change.  Thus, if the controller does not receive an abbreviated
+ * notification for a flow_mod before the next OFPT_BARRIER_REPLY, it will
+ * never receive one. */
+struct nx_flow_update_abbrev {
+    ovs_be16 length;            /* Length is 8. */
+    ovs_be16 event;             /* NXFME_ABBREV. */
+    ovs_be32 xid;               /* Controller-specified xid from flow_mod. */
+};
+OFP_ASSERT(sizeof(struct nx_flow_update_abbrev) == 8);
+
+/* NXT_FLOW_MONITOR_CANCEL.
+ *
+ * Used by a controller to cancel an outstanding monitor. */
+struct nx_flow_monitor_cancel {
+    ovs_be32 id;                /* 'id' from nx_flow_monitor_request. */
+};
+OFP_ASSERT(sizeof(struct nx_flow_monitor_cancel) == 4);
 
 #endif /* openflow/nicira-ext.h */
index 8af656b..56af4c5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -43,33 +43,6 @@ enum ofp_port {
     OFPP_NONE       = 0xffff   /* Not associated with a physical port. */
 };
 
-/* OpenFlow 1.0 specific message types, in addition to the common message
- * types. */
-enum ofp10_type {
-    /* Controller command messages. */
-    OFPT10_PACKET_OUT = 13,     /* Controller/switch message */
-    OFPT10_FLOW_MOD,            /* Controller/switch message */
-    OFPT10_PORT_MOD,            /* Controller/switch message */
-
-    /* Statistics messages. */
-    OFPT10_STATS_REQUEST,       /* Controller/switch message */
-    OFPT10_STATS_REPLY,         /* Controller/switch message */
-
-    /* Barrier messages. */
-    OFPT10_BARRIER_REQUEST,     /* Controller/switch message */
-    OFPT10_BARRIER_REPLY,       /* Controller/switch message */
-
-    /* Queue Configuration messages. */
-    OFPT10_QUEUE_GET_CONFIG_REQUEST,  /* Controller/switch message */
-    OFPT10_QUEUE_GET_CONFIG_REPLY     /* Controller/switch message */
-};
-
-/* OFPT_HELLO.  This message has an empty body, but implementations must
- * ignore any data included in the body, to allow for future extensions. */
-struct ofp_hello {
-    struct ofp_header header;
-};
-
 #define OFP_DEFAULT_MISS_SEND_LEN   128
 
 enum ofp_config_flags {
@@ -87,12 +60,11 @@ enum ofp_config_flags {
 
 /* Switch configuration. */
 struct ofp_switch_config {
-    struct ofp_header header;
     ovs_be16 flags;             /* OFPC_* flags. */
     ovs_be16 miss_send_len;     /* Max bytes of new flow that datapath should
                                    send to the controller. */
 };
-OFP_ASSERT(sizeof(struct ofp_switch_config) == 12);
+OFP_ASSERT(sizeof(struct ofp_switch_config) == 4);
 
 /* OpenFlow 1.0 specific capabilities supported by the datapath (struct
  * ofp_switch_features, member capabilities). */
@@ -161,7 +133,6 @@ OFP_ASSERT(sizeof(struct ofp10_phy_port) == 48);
 
 /* Modify behavior of the physical port */
 struct ofp10_port_mod {
-    struct ofp_header header;
     ovs_be16 port_no;
     uint8_t hw_addr[OFP_ETH_ALEN]; /* The hardware address is not
                                       configurable.  This is used to
@@ -176,30 +147,27 @@ struct ofp10_port_mod {
                                bits to prevent any action taking place. */
     uint8_t pad[4];         /* Pad to 64-bits. */
 };
-OFP_ASSERT(sizeof(struct ofp10_port_mod) == 32);
+OFP_ASSERT(sizeof(struct ofp10_port_mod) == 24);
 
 /* Query for port queue configuration. */
 struct ofp10_queue_get_config_request {
-    struct ofp_header header;
     ovs_be16 port;          /* Port to be queried. Should refer
                                to a valid physical port (i.e. < OFPP_MAX) */
     uint8_t pad[2];
     /* 32-bit alignment. */
 };
-OFP_ASSERT(sizeof(struct ofp10_queue_get_config_request) == 12);
+OFP_ASSERT(sizeof(struct ofp10_queue_get_config_request) == 4);
 
 /* Queue configuration for a given port. */
 struct ofp10_queue_get_config_reply {
-    struct ofp_header header;
     ovs_be16 port;
     uint8_t pad[6];
     /* struct ofp10_packet_queue queues[0]; List of configured queues. */
 };
-OFP_ASSERT(sizeof(struct ofp10_queue_get_config_reply) == 16);
+OFP_ASSERT(sizeof(struct ofp10_queue_get_config_reply) == 8);
 
 /* Packet received on port (datapath -> controller). */
 struct ofp_packet_in {
-    struct ofp_header header;
     ovs_be32 buffer_id;     /* ID assigned by datapath. */
     ovs_be16 total_len;     /* Full length of frame. */
     ovs_be16 in_port;       /* Port on which frame was received. */
@@ -212,7 +180,7 @@ struct ofp_packet_in {
                                offsetof(struct ofp_packet_in, data) ==
                                sizeof(struct ofp_packet_in) - 2. */
 };
-OFP_ASSERT(sizeof(struct ofp_packet_in) == 20);
+OFP_ASSERT(sizeof(struct ofp_packet_in) == 12);
 
 enum ofp10_action_type {
     OFPAT10_OUTPUT,             /* Output to switch port. */
@@ -234,71 +202,13 @@ enum ofp10_action_type {
  * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max
  * number of bytes to send.  A 'max_len' of zero means no bytes of the
  * packet should be sent. */
-struct ofp_action_output {
+struct ofp10_action_output {
     ovs_be16 type;                  /* OFPAT10_OUTPUT. */
     ovs_be16 len;                   /* Length is 8. */
     ovs_be16 port;                  /* Output port. */
     ovs_be16 max_len;               /* Max length to send to controller. */
 };
-OFP_ASSERT(sizeof(struct ofp_action_output) == 8);
-
-/* The VLAN id is 12 bits, so we can use the entire 16 bits to indicate
- * special conditions.  All ones is used to match that no VLAN id was
- * set. */
-#define OFP_VLAN_NONE      0xffff
-
-/* Action structure for OFPAT10_SET_VLAN_VID. */
-struct ofp_action_vlan_vid {
-    ovs_be16 type;                  /* OFPAT10_SET_VLAN_VID. */
-    ovs_be16 len;                   /* Length is 8. */
-    ovs_be16 vlan_vid;              /* VLAN id. */
-    uint8_t pad[2];
-};
-OFP_ASSERT(sizeof(struct ofp_action_vlan_vid) == 8);
-
-/* Action structure for OFPAT10_SET_VLAN_PCP. */
-struct ofp_action_vlan_pcp {
-    ovs_be16 type;                  /* OFPAT10_SET_VLAN_PCP. */
-    ovs_be16 len;                   /* Length is 8. */
-    uint8_t vlan_pcp;               /* VLAN priority. */
-    uint8_t pad[3];
-};
-OFP_ASSERT(sizeof(struct ofp_action_vlan_pcp) == 8);
-
-/* Action structure for OFPAT10_SET_DL_SRC/DST. */
-struct ofp_action_dl_addr {
-    ovs_be16 type;                  /* OFPAT10_SET_DL_SRC/DST. */
-    ovs_be16 len;                   /* Length is 16. */
-    uint8_t dl_addr[OFP_ETH_ALEN];  /* Ethernet address. */
-    uint8_t pad[6];
-};
-OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16);
-
-/* Action structure for OFPAT10_SET_NW_SRC/DST. */
-struct ofp_action_nw_addr {
-    ovs_be16 type;                  /* OFPAT10_SET_TW_SRC/DST. */
-    ovs_be16 len;                   /* Length is 8. */
-    ovs_be32 nw_addr;               /* IP address. */
-};
-OFP_ASSERT(sizeof(struct ofp_action_nw_addr) == 8);
-
-/* Action structure for OFPAT10_SET_NW_TOS. */
-struct ofp_action_nw_tos {
-    ovs_be16 type;                  /* OFPAT10_SET_TW_TOS. */
-    ovs_be16 len;                   /* Length is 8. */
-    uint8_t nw_tos;                 /* IP TOS (DSCP field, 6 bits). */
-    uint8_t pad[3];
-};
-OFP_ASSERT(sizeof(struct ofp_action_nw_tos) == 8);
-
-/* Action structure for OFPAT10_SET_TP_SRC/DST. */
-struct ofp_action_tp_port {
-    ovs_be16 type;                  /* OFPAT10_SET_TP_SRC/DST. */
-    ovs_be16 len;                   /* Length is 8. */
-    ovs_be16 tp_port;               /* TCP/UDP port. */
-    uint8_t pad[2];
-};
-OFP_ASSERT(sizeof(struct ofp_action_tp_port) == 8);
+OFP_ASSERT(sizeof(struct ofp10_action_output) == 8);
 
 /* Action header for OFPAT10_VENDOR. The rest of the body is vendor-defined. */
 struct ofp_action_vendor_header {
@@ -338,7 +248,7 @@ union ofp_action {
     ovs_be16 type;
     struct ofp_action_header header;
     struct ofp_action_vendor_header vendor;
-    struct ofp_action_output output;
+    struct ofp10_action_output output10;
     struct ofp_action_vlan_vid vlan_vid;
     struct ofp_action_vlan_pcp vlan_pcp;
     struct ofp_action_nw_addr nw_addr;
@@ -349,7 +259,6 @@ OFP_ASSERT(sizeof(union ofp_action) == 8);
 
 /* Send packet (controller -> datapath). */
 struct ofp_packet_out {
-    struct ofp_header header;
     ovs_be32 buffer_id;           /* ID assigned by datapath or UINT32_MAX. */
     ovs_be16 in_port;             /* Packet's input port (OFPP_NONE if none). */
     ovs_be16 actions_len;         /* Size of action array in bytes. */
@@ -360,53 +269,47 @@ struct ofp_packet_out {
      *     of the message length.
      */
 };
-OFP_ASSERT(sizeof(struct ofp_packet_out) == 16);
-
-enum ofp_flow_mod_command {
-    OFPFC_ADD,              /* New flow. */
-    OFPFC_MODIFY,           /* Modify all matching flows. */
-    OFPFC_MODIFY_STRICT,    /* Modify entry strictly matching wildcards */
-    OFPFC_DELETE,           /* Delete all matching flows. */
-    OFPFC_DELETE_STRICT     /* Strictly match wildcards and priority. */
-};
+OFP_ASSERT(sizeof(struct ofp_packet_out) == 8);
 
 /* Flow wildcards. */
 enum ofp_flow_wildcards {
-    OFPFW_IN_PORT    = 1 << 0,  /* Switch input port. */
-    OFPFW_DL_VLAN    = 1 << 1,  /* VLAN vid. */
-    OFPFW_DL_SRC     = 1 << 2,  /* Ethernet source address. */
-    OFPFW_DL_DST     = 1 << 3,  /* Ethernet destination address. */
-    OFPFW_DL_TYPE    = 1 << 4,  /* Ethernet frame type. */
-    OFPFW_NW_PROTO   = 1 << 5,  /* IP protocol. */
-    OFPFW_TP_SRC     = 1 << 6,  /* TCP/UDP source port. */
-    OFPFW_TP_DST     = 1 << 7,  /* TCP/UDP destination port. */
+    OFPFW10_IN_PORT    = 1 << 0,  /* Switch input port. */
+    OFPFW10_DL_VLAN    = 1 << 1,  /* VLAN vid. */
+    OFPFW10_DL_SRC     = 1 << 2,  /* Ethernet source address. */
+    OFPFW10_DL_DST     = 1 << 3,  /* Ethernet destination address. */
+    OFPFW10_DL_TYPE    = 1 << 4,  /* Ethernet frame type. */
+    OFPFW10_NW_PROTO   = 1 << 5,  /* IP protocol. */
+    OFPFW10_TP_SRC     = 1 << 6,  /* TCP/UDP source port. */
+    OFPFW10_TP_DST     = 1 << 7,  /* TCP/UDP destination port. */
 
     /* IP source address wildcard bit count.  0 is exact match, 1 ignores the
      * LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard
      * the entire field.  This is the *opposite* of the usual convention where
      * e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded. */
-    OFPFW_NW_SRC_SHIFT = 8,
-    OFPFW_NW_SRC_BITS = 6,
-    OFPFW_NW_SRC_MASK = ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT,
-    OFPFW_NW_SRC_ALL = 32 << OFPFW_NW_SRC_SHIFT,
+    OFPFW10_NW_SRC_SHIFT = 8,
+    OFPFW10_NW_SRC_BITS = 6,
+    OFPFW10_NW_SRC_MASK = (((1 << OFPFW10_NW_SRC_BITS) - 1)
+                           << OFPFW10_NW_SRC_SHIFT),
+    OFPFW10_NW_SRC_ALL = 32 << OFPFW10_NW_SRC_SHIFT,
 
     /* IP destination address wildcard bit count.  Same format as source. */
-    OFPFW_NW_DST_SHIFT = 14,
-    OFPFW_NW_DST_BITS = 6,
-    OFPFW_NW_DST_MASK = ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT,
-    OFPFW_NW_DST_ALL = 32 << OFPFW_NW_DST_SHIFT,
+    OFPFW10_NW_DST_SHIFT = 14,
+    OFPFW10_NW_DST_BITS = 6,
+    OFPFW10_NW_DST_MASK = (((1 << OFPFW10_NW_DST_BITS) - 1)
+                           << OFPFW10_NW_DST_SHIFT),
+    OFPFW10_NW_DST_ALL = 32 << OFPFW10_NW_DST_SHIFT,
 
-    OFPFW_DL_VLAN_PCP = 1 << 20, /* VLAN priority. */
-    OFPFW_NW_TOS = 1 << 21, /* IP ToS (DSCP field, 6 bits). */
+    OFPFW10_DL_VLAN_PCP = 1 << 20, /* VLAN priority. */
+    OFPFW10_NW_TOS = 1 << 21, /* IP ToS (DSCP field, 6 bits). */
 
     /* Wildcard all fields. */
-    OFPFW_ALL = ((1 << 22) - 1)
+    OFPFW10_ALL = ((1 << 22) - 1)
 };
 
 /* The wildcards for ICMP type and code fields use the transport source
  * and destination port fields, respectively. */
-#define OFPFW_ICMP_TYPE OFPFW_TP_SRC
-#define OFPFW_ICMP_CODE OFPFW_TP_DST
+#define OFPFW10_ICMP_TYPE OFPFW10_TP_SRC
+#define OFPFW10_ICMP_CODE OFPFW10_TP_DST
 
 /* Values below this cutoff are 802.3 packets and the two bytes
  * following MAC addresses are used as a frame length.  Otherwise, the
@@ -420,12 +323,12 @@ enum ofp_flow_wildcards {
 #define OFP_DL_TYPE_NOT_ETH_TYPE  0x05ff
 
 /* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate
- * special conditions.  All ones indicates that no VLAN id was set.
+ * special conditions.  All ones indicates that 802.1Q header is not present.
  */
-#define OFP_VLAN_NONE      0xffff
+#define OFP10_VLAN_NONE      0xffff
 
 /* Fields to match against flows */
-struct ofp_match {
+struct ofp10_match {
     ovs_be32 wildcards;        /* Wildcard fields. */
     ovs_be16 in_port;          /* Input switch port. */
     uint8_t dl_src[OFP_ETH_ALEN]; /* Ethernet source address. */
@@ -443,7 +346,7 @@ struct ofp_match {
     ovs_be16 tp_src;           /* TCP/UDP source port. */
     ovs_be16 tp_dst;           /* TCP/UDP destination port. */
 };
-OFP_ASSERT(sizeof(struct ofp_match) == 40);
+OFP_ASSERT(sizeof(struct ofp10_match) == 40);
 
 /* Value used in "idle_timeout" and "hard_timeout" to indicate that the entry
  * is permanent. */
@@ -452,17 +355,13 @@ OFP_ASSERT(sizeof(struct ofp_match) == 40);
 /* By default, choose a priority in the middle. */
 #define OFP_DEFAULT_PRIORITY 0x8000
 
-enum ofp_flow_mod_flags {
-    OFPFF_SEND_FLOW_REM = 1 << 0,  /* Send flow removed message when flow
-                                    * expires or is deleted. */
-    OFPFF_CHECK_OVERLAP = 1 << 1,  /* Check for overlapping entries first. */
-    OFPFF_EMERG         = 1 << 2   /* Ramark this is for emergency. */
+enum ofp10_flow_mod_flags {
+    OFPFF10_EMERG       = 1 << 2   /* Ramark this is for emergency. */
 };
 
 /* Flow setup and teardown (controller -> datapath). */
-struct ofp_flow_mod {
-    struct ofp_header header;
-    struct ofp_match match;      /* Fields to match */
+struct ofp10_flow_mod {
+    struct ofp10_match match;    /* Fields to match */
     ovs_be64 cookie;             /* Opaque controller-issued identifier. */
 
     /* Flow actions. */
@@ -481,12 +380,11 @@ struct ofp_flow_mod {
                                             from the length field in the
                                             header. */
 };
-OFP_ASSERT(sizeof(struct ofp_flow_mod) == 72);
+OFP_ASSERT(sizeof(struct ofp10_flow_mod) == 64);
 
 /* Flow removed (datapath -> controller). */
 struct ofp_flow_removed {
-    struct ofp_header header;
-    struct ofp_match match;   /* Description of fields. */
+    struct ofp10_match match; /* Description of fields. */
     ovs_be64 cookie;          /* Opaque controller-issued identifier. */
 
     ovs_be16 priority;        /* Priority level of flow entry. */
@@ -501,87 +399,33 @@ struct ofp_flow_removed {
     ovs_be64 packet_count;
     ovs_be64 byte_count;
 };
-OFP_ASSERT(sizeof(struct ofp_flow_removed) == 88);
+OFP_ASSERT(sizeof(struct ofp_flow_removed) == 80);
 
 /* OFPT_ERROR: Error message (datapath -> controller). */
 struct ofp_error_msg {
-    struct ofp_header header;
-
     ovs_be16 type;
     ovs_be16 code;
     uint8_t data[0];          /* Variable-length data.  Interpreted based
                                  on the type and code. */
 };
-OFP_ASSERT(sizeof(struct ofp_error_msg) == 12);
-
-enum ofp_stats_types {
-    /* Description of this OpenFlow switch.
-     * The request is struct ofp_stats_msg.
-     * The reply is struct ofp_desc_stats. */
-    OFPST_DESC,
-
-    /* Individual flow statistics.
-     * The request is struct ofp_flow_stats_request.
-     * The reply body is an array of struct ofp_flow_stats. */
-    OFPST_FLOW,
-
-    /* Aggregate flow statistics.
-     * The request is struct ofp_flow_stats_request.
-     * The reply is struct ofp_aggregate_stats_reply. */
-    OFPST_AGGREGATE,
-
-    /* Flow table statistics.
-     * The request is struct ofp_stats_msg.
-     * The reply body is an array of struct ofp_table_stats. */
-    OFPST_TABLE,
-
-    /* Physical port statistics.
-     * The request is struct ofp_port_stats_request.
-     * The reply body is an array of struct ofp_port_stats. */
-    OFPST_PORT,
-
-    /* Queue statistics for a port.
-     * The request body is struct ofp_queue_stats_request.
-     * The reply body is an array of struct ofp_queue_stats. */
-    OFPST_QUEUE,
-
-    /* Vendor extension.
-     * The request and reply begin with "struct ofp_vendor_stats". */
-    OFPST_VENDOR = 0xffff
-};
+OFP_ASSERT(sizeof(struct ofp_error_msg) == 4);
 
 /* Statistics request or reply message. */
-struct ofp_stats_msg {
+struct ofp10_stats_msg {
     struct ofp_header header;
     ovs_be16 type;              /* One of the OFPST_* constants. */
     ovs_be16 flags;             /* Requests: always 0.
                                  * Replies: 0 or OFPSF_REPLY_MORE. */
 };
-OFP_ASSERT(sizeof(struct ofp_stats_msg) == 12);
+OFP_ASSERT(sizeof(struct ofp10_stats_msg) == 12);
 
 enum ofp_stats_reply_flags {
     OFPSF_REPLY_MORE  = 1 << 0  /* More replies to follow. */
 };
 
-#define DESC_STR_LEN   256
-#define SERIAL_NUM_LEN 32
-/* Reply to OFPST_DESC request.  Each entry is a NULL-terminated ASCII
- * string. */
-struct ofp_desc_stats {
-    struct ofp_stats_msg osm;
-    char mfr_desc[DESC_STR_LEN];       /* Manufacturer description. */
-    char hw_desc[DESC_STR_LEN];        /* Hardware description. */
-    char sw_desc[DESC_STR_LEN];        /* Software description. */
-    char serial_num[SERIAL_NUM_LEN];   /* Serial number. */
-    char dp_desc[DESC_STR_LEN];        /* Human readable description of
-                                          the datapath. */
-};
-OFP_ASSERT(sizeof(struct ofp_desc_stats) == 1068);
-
 /* Stats request of type OFPST_AGGREGATE or OFPST_FLOW. */
-struct ofp_flow_stats_request {
-    struct ofp_stats_msg osm;
-    struct ofp_match match;   /* Fields to match. */
+struct ofp10_flow_stats_request {
+    struct ofp10_match match; /* Fields to match. */
     uint8_t table_id;         /* ID of table to read (from ofp_table_stats)
                                  or 0xff for all tables. */
     uint8_t pad;              /* Align to 32 bits. */
@@ -589,14 +433,14 @@ struct ofp_flow_stats_request {
                                  as an output port.  A value of OFPP_NONE
                                  indicates no restriction. */
 };
-OFP_ASSERT(sizeof(struct ofp_flow_stats_request) == 56);
+OFP_ASSERT(sizeof(struct ofp10_flow_stats_request) == 44);
 
 /* Body of reply to OFPST_FLOW request. */
-struct ofp_flow_stats {
+struct ofp10_flow_stats {
     ovs_be16 length;          /* Length of this entry. */
     uint8_t table_id;         /* ID of table flow came from. */
     uint8_t pad;
-    struct ofp_match match;   /* Description of fields. */
+    struct ofp10_match match; /* Description of fields. */
     ovs_be32 duration_sec;    /* Time flow has been alive in seconds. */
     ovs_be32 duration_nsec;   /* Time flow has been alive in nanoseconds
                                  beyond duration_sec. */
@@ -610,46 +454,35 @@ struct ofp_flow_stats {
     ovs_32aligned_be64 byte_count;   /* Number of bytes in flow. */
     struct ofp_action_header actions[0]; /* Actions. */
 };
-OFP_ASSERT(sizeof(struct ofp_flow_stats) == 88);
-
-/* Reply to OFPST_AGGREGATE request. */
-struct ofp_aggregate_stats_reply {
-    struct ofp_stats_msg osm;
-    ovs_32aligned_be64 packet_count; /* Number of packets in flows. */
-    ovs_32aligned_be64 byte_count;   /* Number of bytes in flows. */
-    ovs_be32 flow_count;      /* Number of flows. */
-    uint8_t pad[4];           /* Align to 64 bits. */
-};
-OFP_ASSERT(sizeof(struct ofp_aggregate_stats_reply) == 36);
+OFP_ASSERT(sizeof(struct ofp10_flow_stats) == 88);
 
 /* Body of reply to OFPST_TABLE request. */
-struct ofp_table_stats {
+struct ofp10_table_stats {
     uint8_t table_id;        /* Identifier of table.  Lower numbered tables
                                 are consulted first. */
     uint8_t pad[3];          /* Align to 32-bits. */
     char name[OFP_MAX_TABLE_NAME_LEN];
-    ovs_be32 wildcards;      /* Bitmap of OFPFW_* wildcards that are
+    ovs_be32 wildcards;      /* Bitmap of OFPFW10_* wildcards that are
                                 supported by the table. */
     ovs_be32 max_entries;    /* Max number of entries supported. */
     ovs_be32 active_count;   /* Number of active entries. */
     ovs_32aligned_be64 lookup_count;  /* # of packets looked up in table. */
     ovs_32aligned_be64 matched_count; /* Number of packets that hit table. */
 };
-OFP_ASSERT(sizeof(struct ofp_table_stats) == 64);
+OFP_ASSERT(sizeof(struct ofp10_table_stats) == 64);
 
 /* Stats request of type OFPST_PORT. */
-struct ofp_port_stats_request {
-    struct ofp_stats_msg osm;
+struct ofp10_port_stats_request {
     ovs_be16 port_no;        /* OFPST_PORT message may request statistics
                                 for a single port (specified with port_no)
                                 or for all ports (port_no == OFPP_NONE). */
     uint8_t pad[6];
 };
-OFP_ASSERT(sizeof(struct ofp_port_stats_request) == 20);
+OFP_ASSERT(sizeof(struct ofp10_port_stats_request) == 8);
 
 /* Body of reply to OFPST_PORT request. If a counter is unsupported, set
  * the field to all ones. */
-struct ofp_port_stats {
+struct ofp10_port_stats {
     ovs_be16 port_no;
     uint8_t pad[6];          /* Align to 64-bits. */
     ovs_32aligned_be64 rx_packets;     /* Number of received packets. */
@@ -669,23 +502,22 @@ struct ofp_port_stats {
     ovs_32aligned_be64 rx_crc_err;   /* Number of CRC errors. */
     ovs_32aligned_be64 collisions;   /* Number of collisions. */
 };
-OFP_ASSERT(sizeof(struct ofp_port_stats) == 104);
+OFP_ASSERT(sizeof(struct ofp10_port_stats) == 104);
 
 /* All ones is used to indicate all queues in a port (for stats retrieval). */
 #define OFPQ_ALL      0xffffffff
 
 /* Body for stats request of type OFPST_QUEUE. */
-struct ofp_queue_stats_request {
-    struct ofp_stats_msg osm;
+struct ofp10_queue_stats_request {
     ovs_be16 port_no;        /* All ports if OFPP_ALL. */
     uint8_t pad[2];          /* Align to 32-bits. */
     ovs_be32 queue_id;       /* All queues if OFPQ_ALL. */
 };
-OFP_ASSERT(sizeof(struct ofp_queue_stats_request) == 20);
+OFP_ASSERT(sizeof(struct ofp10_queue_stats_request) == 8);
 
 /* Body for stats reply of type OFPST_QUEUE consists of an array of this
  * structure type. */
-struct ofp_queue_stats {
+struct ofp10_queue_stats {
     ovs_be16 port_no;
     uint8_t pad[2];          /* Align to 32-bits. */
     ovs_be32 queue_id;       /* Queue id. */
@@ -693,18 +525,18 @@ struct ofp_queue_stats {
     ovs_32aligned_be64 tx_packets; /* Number of transmitted packets. */
     ovs_32aligned_be64 tx_errors;  /* # of packets dropped due to overrun. */
 };
-OFP_ASSERT(sizeof(struct ofp_queue_stats) == 32);
+OFP_ASSERT(sizeof(struct ofp10_queue_stats) == 32);
 
 /* Vendor extension stats message. */
-struct ofp_vendor_stats_msg {
-    struct ofp_stats_msg osm;   /* Type OFPST_VENDOR. */
+struct ofp10_vendor_stats_msg {
+    struct ofp10_stats_msg osm; /* Type OFPST_VENDOR. */
     ovs_be32 vendor;            /* Vendor ID:
                                  * - MSB 0: low-order bytes are IEEE OUI.
                                  * - MSB != 0: defined by OpenFlow
                                  *   consortium. */
     /* Followed by vendor-defined arbitrary additional data. */
 };
-OFP_ASSERT(sizeof(struct ofp_vendor_stats_msg) == 16);
+OFP_ASSERT(sizeof(struct ofp10_vendor_stats_msg) == 16);
 
 /* Vendor extension. */
 struct ofp_vendor_header {
index c1f5d64..696c3ec 100644 (file)
@@ -32,7 +32,7 @@
  */
 
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
 #define OFPP11_MAX    0xffffff00
 #define OFPP11_OFFSET (OFPP11_MAX - OFPP_MAX)
 
-/* OpenFlow 1.1 specific message types, in addition to the common message
- * types. */
-enum ofp11_type {
-    /* Controller command messages. */
-    OFPT11_PACKET_OUT = 13,     /* Controller/switch message */
-    OFPT11_FLOW_MOD,            /* Controller/switch message */
-    OFPT11_GROUP_MOD,           /* Controller/switch message */
-    OFPT11_PORT_MOD,            /* Controller/switch message */
-    OFPT11_TABLE_MOD,           /* Controller/switch message */
-
-    /* Statistics messages. */
-    OFPT11_STATS_REQUEST,       /* Controller/switch message */
-    OFPT11_STATS_REPLY,         /* Controller/switch message */
-
-    /* Barrier messages. */
-    OFPT11_BARRIER_REQUEST,     /* Controller/switch message */
-    OFPT11_BARRIER_REPLY,       /* Controller/switch message */
-
-    /* Queue Configuration messages. */
-    OFPT11_QUEUE_GET_CONFIG_REQUEST,  /* Controller/switch message */
-    OFPT11_QUEUE_GET_CONFIG_REPLY,    /* Controller/switch message */
-};
-
 /* OpenFlow 1.1 port config flags are just the common flags. */
 #define OFPPC11_ALL \
     (OFPPC_PORT_DOWN | OFPPC_NO_RECV | OFPPC_NO_FWD | OFPPC_NO_PACKET_IN)
@@ -145,7 +122,6 @@ struct ofp11_port {
 
 /* Modify behavior of the physical port */
 struct ofp11_port_mod {
-    struct ofp_header header;
     ovs_be32 port_no;
     uint8_t pad[4];
     uint8_t hw_addr[OFP_ETH_ALEN]; /* The hardware address is not
@@ -161,11 +137,10 @@ struct ofp11_port_mod {
                                to prevent any action taking place. */
     uint8_t pad3[4];        /* Pad to 64 bits. */
 };
-OFP_ASSERT(sizeof(struct ofp11_port_mod) == 40);
+OFP_ASSERT(sizeof(struct ofp11_port_mod) == 32);
 
 /* Group setup and teardown (controller -> datapath). */
 struct ofp11_group_mod {
-    struct ofp_header header;
     ovs_be16 command;             /* One of OFPGC_*. */
     uint8_t type;                 /* One of OFPGT_*. */
     uint8_t pad;                  /* Pad to 64 bits. */
@@ -173,17 +148,16 @@ struct ofp11_group_mod {
     /* struct ofp11_bucket buckets[0]; The bucket length is inferred from the
                                        length field in the header. */
 };
-OFP_ASSERT(sizeof(struct ofp11_group_mod) == 16);
+OFP_ASSERT(sizeof(struct ofp11_group_mod) == 8);
 
 /* Query for port queue configuration. */
 struct ofp11_queue_get_config_request {
-    struct ofp_header header;
     ovs_be32 port;
     /* Port to be queried. Should refer
        to a valid physical port (i.e. < OFPP_MAX) */
     uint8_t pad[4];
 };
-OFP_ASSERT(sizeof(struct ofp11_queue_get_config_request) == 16);
+OFP_ASSERT(sizeof(struct ofp11_queue_get_config_request) == 8);
 
 /* Group commands */
 enum ofp11_group_mod_command {
@@ -269,19 +243,19 @@ OFP_ASSERT(sizeof(struct ofp11_match) == OFPMT11_STANDARD_LENGTH);
 
 /* Flow wildcards. */
 enum ofp11_flow_wildcards {
-    OFPFW11_IN_PORT    = 1 << 0,  /* Switch input port. */
-    OFPFW11_DL_VLAN    = 1 << 1,  /* VLAN vid. */
-    OFPFW11_DL_SRC     = 1 << 2,  /* Ethernet source address. */
-    OFPFW11_DL_DST     = 1 << 3,  /* Ethernet destination address. */
-    OFPFW11_DL_TYPE    = 1 << 4,  /* Ethernet frame type. */
-    OFPFW11_NW_PROTO   = 1 << 5,  /* IP protocol. */
-    OFPFW11_TP_SRC     = 1 << 6,  /* TCP/UDP source port. */
-    OFPFW11_TP_DST     = 1 << 7,  /* TCP/UDP destination port. */
-    OFPFW11_MPLS_LABEL = 1 << 8,  /* MPLS label. */
-    OFPFW11_MPLS_TC    = 1 << 9,  /* MPLS TC. */
+    OFPFW11_IN_PORT     = 1 << 0,  /* Switch input port. */
+    OFPFW11_DL_VLAN     = 1 << 1,  /* VLAN id. */
+    OFPFW11_DL_VLAN_PCP = 1 << 2,  /* VLAN priority. */
+    OFPFW11_DL_TYPE     = 1 << 3,  /* Ethernet frame type. */
+    OFPFW11_NW_TOS      = 1 << 4,  /* IP ToS (DSCP field, 6 bits). */
+    OFPFW11_NW_PROTO    = 1 << 5,  /* IP protocol. */
+    OFPFW11_TP_SRC      = 1 << 6,  /* TCP/UDP/SCTP source port. */
+    OFPFW11_TP_DST      = 1 << 7,  /* TCP/UDP/SCTP destination port. */
+    OFPFW11_MPLS_LABEL  = 1 << 8,  /* MPLS label. */
+    OFPFW11_MPLS_TC     = 1 << 9,  /* MPLS TC. */
 
     /* Wildcard all fields. */
-    OFPFW11_ALL = ((1 << 10) - 1)
+    OFPFW11_ALL           = ((1 << 10) - 1)
 };
 
 /* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate
@@ -307,6 +281,16 @@ enum ofp11_instruction_type {
     OFPIT11_EXPERIMENTER = 0xFFFF  /* Experimenter instruction */
 };
 
+#define OFP11_INSTRUCTION_ALIGN 8
+
+/* Generic ofp_instruction structure. */
+struct ofp11_instruction {
+    ovs_be16 type;              /* Instruction type */
+    ovs_be16 len;               /* Length of this struct in bytes. */
+    uint8_t pad[4];             /* Align to 64-bits */
+};
+OFP_ASSERT(sizeof(struct ofp11_instruction) == 8);
+
 /* Instruction structure for OFPIT_GOTO_TABLE */
 struct ofp11_instruction_goto_table {
     ovs_be16 type;                 /* OFPIT_GOTO_TABLE */
@@ -337,6 +321,16 @@ struct ofp11_instruction_actions {
 };
 OFP_ASSERT(sizeof(struct ofp11_instruction_actions) == 8);
 
+/* Instruction structure for experimental instructions */
+struct ofp11_instruction_experimenter {
+    ovs_be16 type;              /* OFPIT11_EXPERIMENTER */
+    ovs_be16 len;               /* Length of this struct in bytes */
+    ovs_be32 experimenter;      /* Experimenter ID which takes the same form
+                                   as in struct ofp_vendor_header. */
+    /* Experimenter-defined arbitrary additional data. */
+};
+OFP_ASSERT(sizeof(struct ofp11_instruction_experimenter) == 8);
+
 /* Action structure for OFPAT_OUTPUT, which sends packets out 'port'.
    * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max
    * number of bytes to send. A 'max_len' of zero means no bytes of the
@@ -430,12 +424,11 @@ OFP_ASSERT(sizeof(struct ofp11_action_pop_mpls) == 8);
 
 /* Configure/Modify behavior of a flow table */
 struct ofp11_table_mod {
-    struct ofp_header header;
     uint8_t table_id;       /* ID of the table, 0xFF indicates all tables */
     uint8_t pad[3];         /* Pad to 32 bits */
     ovs_be32 config;        /* Bitmap of OFPTC_* flags */
 };
-OFP_ASSERT(sizeof(struct ofp11_table_mod) == 16);
+OFP_ASSERT(sizeof(struct ofp11_table_mod) == 8);
 
 /* Flags to indicate behavior of the flow table for unmatched packets.
    These flags are used in ofp_table_stats messages to describe the current
@@ -451,7 +444,6 @@ enum ofp11_table_config {
 
 /* Flow setup and teardown (controller -> datapath). */
 struct ofp11_flow_mod {
-    struct ofp_header header;
     ovs_be64 cookie;             /* Opaque controller-issued identifier. */
     ovs_be64 cookie_mask;        /* Mask used to restrict the cookie bits
                                     that must match when the command is
@@ -471,14 +463,14 @@ struct ofp11_flow_mod {
                                     indicates no restriction. */
     ovs_be32 out_group;          /* For OFPFC_DELETE* commands, require
                                     matching entries to include this as an
-                                    output group. A value of OFPG_ANY
+                                    output group. A value of OFPG11_ANY
                                     indicates no restriction. */
     ovs_be16 flags;              /* One of OFPFF_*. */
     uint8_t pad[2];
-    /* Open Flow version specific match */
-    /* struct ofp_instruction instructions[0];  Instruction set */
+    /* Followed by an ofp11_match structure. */
+    /* Followed by an instruction set. */
 };
-OFP_ASSERT(sizeof(struct ofp11_flow_mod) == 48);
+OFP_ASSERT(sizeof(struct ofp11_flow_mod) == 40);
 
 /* Group types. Values in the range [128, 255] are reserved for experimental
  * use. */
@@ -489,6 +481,20 @@ enum ofp11_group_type {
     OFPGT11_FF        /* Fast failover group. */
 };
 
+/* Group numbering. Groups can use any number up to OFPG_MAX. */
+enum ofp11_group {
+    /* Last usable group number. */
+    OFPG11_MAX        = 0xffffff00,
+
+    /* Fake groups. */
+    OFPG11_ALL        = 0xfffffffc,  /* Represents all groups for group delete
+                                        commands. */
+    OFPG11_ANY        = 0xffffffff   /* Wildcard group used only for flow stats
+                                        requests. Selects all flows regardless
+                                        of group (including flows with no
+                                        group). */
+};
+
 /* Bucket for use in groups. */
 struct ofp11_bucket {
     ovs_be16 len;                    /* Length the bucket in bytes, including
@@ -511,25 +517,34 @@ OFP_ASSERT(sizeof(struct ofp11_bucket) == 16);
 
 /* Queue configuration for a given port. */
 struct ofp11_queue_get_config_reply {
-    struct ofp_header header;
     ovs_be32 port;
     uint8_t pad[4];
     /* struct ofp_packet_queue queues[0];  List of configured queues. */
 };
-OFP_ASSERT(sizeof(struct ofp11_queue_get_config_reply) == 16);
+OFP_ASSERT(sizeof(struct ofp11_queue_get_config_reply) == 8);
 
 struct ofp11_stats_msg {
     struct ofp_header header;
     ovs_be16 type;              /* One of the OFPST_* constants. */
     ovs_be16 flags;             /* OFPSF_REQ_* flags (none yet defined). */
     uint8_t pad[4];
-    /* uint8_t body[0];             Body of the request. */
+    /* Followed by the body of the request. */
 };
 OFP_ASSERT(sizeof(struct ofp11_stats_msg) == 16);
 
+/* Vendor extension stats message. */
+struct ofp11_vendor_stats_msg {
+    struct ofp11_stats_msg osm; /* Type OFPST_VENDOR. */
+    ovs_be32 vendor;            /* Vendor ID:
+                                 * - MSB 0: low-order bytes are IEEE OUI.
+                                 * - MSB != 0: defined by OpenFlow
+                                 *   consortium. */
+    /* Followed by vendor-defined arbitrary additional data. */
+};
+OFP_ASSERT(sizeof(struct ofp11_vendor_stats_msg) == 20);
+
 /* Stats request of type OFPST_FLOW. */
 struct ofp11_flow_stats_request {
-    struct ofp11_stats_msg osm;
     uint8_t table_id;         /* ID of table to read (from ofp_table_stats),
                                  0xff for all tables. */
     uint8_t pad[3];           /* Align to 64 bits. */
@@ -537,7 +552,7 @@ struct ofp11_flow_stats_request {
                                  as an output port. A value of OFPP_ANY
                                  indicates no restriction. */
     ovs_be32 out_group;       /* Require matching entries to include this
-                                 as an output group. A value of OFPG_ANY
+                                 as an output group. A value of OFPG11_ANY
                                  indicates no restriction. */
     uint8_t pad2[4];          /* Align to 64 bits. */
     ovs_be64 cookie;          /* Require matching entries to contain this
@@ -545,9 +560,9 @@ struct ofp11_flow_stats_request {
     ovs_be64 cookie_mask;     /* Mask used to restrict the cookie bits that
                                  must match. A value of 0 indicates
                                  no restriction. */
-    struct ofp11_match match; /* Fields to match. */
+    /* Followed by an ofp11_match structure. */
 };
-OFP_ASSERT(sizeof(struct ofp11_flow_stats_request) == 136);
+OFP_ASSERT(sizeof(struct ofp11_flow_stats_request) == 32);
 
 /* Body of reply to OFPST_FLOW request. */
 struct ofp11_flow_stats {
@@ -575,7 +590,6 @@ OFP_ASSERT(sizeof(struct ofp11_flow_stats) == 48);
 
 /* Body of reply to OFPST_TABLE request. */
 struct ofp11_table_stats {
-    struct ofp11_stats_msg osm;
     uint8_t table_id;        /* Identifier of table. Lower numbered tables
                                 are consulted first. */
     uint8_t pad[7];          /* Align to 64-bits. */
@@ -595,23 +609,21 @@ struct ofp11_table_stats {
     ovs_be64 lookup_count;   /* Number of packets looked up in table. */
     ovs_be64 matched_count;  /* Number of packets that hit table. */
 };
-OFP_ASSERT(sizeof(struct ofp11_table_stats) == 104);
+OFP_ASSERT(sizeof(struct ofp11_table_stats) == 88);
 
 /* Body for ofp_stats_request of type OFPST_PORT. */
 struct ofp11_port_stats_request {
-    struct ofp11_stats_msg osm;
     ovs_be32 port_no;        /* OFPST_PORT message must request statistics
                               * either for a single port (specified in
                               * port_no) or for all ports (if port_no ==
                               * OFPP_ANY). */
     uint8_t pad[4];
 };
-OFP_ASSERT(sizeof(struct ofp11_port_stats_request) == 24);
+OFP_ASSERT(sizeof(struct ofp11_port_stats_request) == 8);
 
 /* Body of reply to OFPST_PORT request. If a counter is unsupported, set
  * the field to all ones. */
 struct ofp11_port_stats {
-    struct ofp11_stats_msg osm;
     ovs_be32 port_no;
     uint8_t pad[4];           /* Align to 64-bits. */
     ovs_be64 rx_packets;      /* Number of received packets. */
@@ -631,31 +643,28 @@ struct ofp11_port_stats {
     ovs_be64 rx_crc_err;      /* Number of CRC errors. */
     ovs_be64 collisions;      /* Number of collisions. */
 };
-OFP_ASSERT(sizeof(struct ofp11_port_stats) == 120);
+OFP_ASSERT(sizeof(struct ofp11_port_stats) == 104);
 
 struct ofp11_queue_stats_request {
-    struct ofp11_stats_msg osm;
     ovs_be32 port_no;         /* All ports if OFPP_ANY. */
     ovs_be32 queue_id;        /* All queues if OFPQ_ALL. */
 };
-OFP_ASSERT(sizeof(struct ofp11_queue_stats_request) == 24);
+OFP_ASSERT(sizeof(struct ofp11_queue_stats_request) == 8);
 
 struct ofp11_queue_stats {
-    struct ofp11_stats_msg osm;
     ovs_be32 port_no;
     ovs_be32 queue_id;         /* Queue id. */
     ovs_be64 tx_bytes;         /* Number of transmitted bytes. */
     ovs_be64 tx_packets;       /* Number of transmitted packets. */
     ovs_be64 tx_errors;        /* # of packets dropped due to overrun. */
 };
-OFP_ASSERT(sizeof(struct ofp11_queue_stats) == 48);
+OFP_ASSERT(sizeof(struct ofp11_queue_stats) == 32);
 
 struct ofp11_group_stats_request {
-    struct ofp11_stats_msg osm;
     ovs_be32 group_id;         /* All groups if OFPG_ALL. */
     uint8_t pad[4];            /* Align to 64 bits. */
 };
-OFP_ASSERT(sizeof(struct ofp11_group_stats_request) == 24);
+OFP_ASSERT(sizeof(struct ofp11_group_stats_request) == 8);
 
 /* Body of reply to OFPST11_GROUP request */
 struct ofp11_group_stats {
@@ -674,11 +683,10 @@ OFP_ASSERT(sizeof(struct ofp11_group_stats) == 32);
 
 /* Used in group stats replies. */
 struct ofp11_bucket_counter {
-    struct ofp11_stats_msg osm;
     ovs_be64 packet_count;   /* Number of packets processed by bucket. */
     ovs_be64 byte_count;     /* Number of bytes processed by bucket. */
 };
-OFP_ASSERT(sizeof(struct ofp11_bucket_counter) == 32);
+OFP_ASSERT(sizeof(struct ofp11_bucket_counter) == 16);
 
 /* Body of reply to OFPST11_GROUP_DESC request. */
 struct ofp11_group_desc_stats {
@@ -692,7 +700,6 @@ OFP_ASSERT(sizeof(struct ofp11_group_desc_stats) == 8);
 
 /* Send packet (controller -> datapath). */
 struct ofp11_packet_out {
-    struct ofp_header header;
     ovs_be32 buffer_id;       /* ID assigned by datapath (-1 if none). */
     ovs_be32 in_port;         /* Packet's input port or OFPP_CONTROLLER. */
     ovs_be16 actions_len;     /* Size of action array in bytes. */
@@ -702,11 +709,10 @@ struct ofp11_packet_out {
                                  from the length field in the header.
                                  (Only meaningful if buffer_id == -1.) */
 };
-OFP_ASSERT(sizeof(struct ofp11_packet_out) == 24);
+OFP_ASSERT(sizeof(struct ofp11_packet_out) == 16);
 
 /* Packet received on port (datapath -> controller). */
 struct ofp11_packet_in {
-    struct ofp_header header;
     ovs_be32 buffer_id;     /* ID assigned by datapath. */
     ovs_be32 in_port;       /* Port on which frame was received. */
     ovs_be32 in_phy_port;   /* Physical Port on which frame was received. */
@@ -720,11 +726,10 @@ struct ofp11_packet_in {
                                offsetof(struct ofp_packet_in, data) ==
                                sizeof(struct ofp_packet_in) - 2. */
 };
-OFP_ASSERT(sizeof(struct ofp11_packet_in) == 24);
+OFP_ASSERT(sizeof(struct ofp11_packet_in) == 16);
 
 /* Flow removed (datapath -> controller). */
 struct ofp11_flow_removed {
-    struct ofp_header header;
     ovs_be64 cookie;          /* Opaque controller-issued identifier. */
 
     ovs_be16 priority;        /* Priority level of flow entry. */
@@ -738,8 +743,8 @@ struct ofp11_flow_removed {
     uint8_t pad2[2];          /* Align to 64-bits. */
     ovs_be64 packet_count;
     ovs_be64 byte_count;
-    struct ofp11_match match; /* Description of fields. */
+    /* Followed by an ofp11_match structure. */
 };
-OFP_ASSERT(sizeof(struct ofp11_flow_removed) == 136);
+OFP_ASSERT(sizeof(struct ofp11_flow_removed) == 40);
 
 #endif /* openflow/openflow-1.1.h */
index 3aaa056..0a73ed1 100644 (file)
@@ -32,7 +32,7 @@
  */
 
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  * Copyright (c) 2012 Horms Solutions Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
 
 #include "openflow/openflow-1.1.h"
 
-/* OpenFlow 1.2 specific message types, in addition to the common message
- * types. */
-enum ofp12_type {
-    /* Controller role change request messages. */
-    OFPT12_ROLE_REQUEST = 24,   /* Controller/switch message */
-    OFPT12_ROLE_REPLY,          /* Controller/switch message */
-};
-
 /*
  * OXM Class IDs.
  * The high order bit differentiate reserved classes from member classes.
@@ -121,9 +113,11 @@ enum oxm12_ofb_match_fields {
  */
 
 #define OXM_HEADER(FIELD, LENGTH) \
-       NXM_HEADER(OFPXMC12_OPENFLOW_BASIC, FIELD, LENGTH)
+    NXM_HEADER(OFPXMC12_OPENFLOW_BASIC, FIELD, LENGTH)
 #define OXM_HEADER_W(FIELD, LENGTH) \
-       NXM_HEADER_W(OFPXMC12_OPENFLOW_BASIC, FIELD, LENGTH)
+    NXM_HEADER_W(OFPXMC12_OPENFLOW_BASIC, FIELD, LENGTH)
+
+#define IS_OXM_HEADER(header) (NXM_VENDOR(header) == OFPXMC12_OPENFLOW_BASIC)
 
 #define OXM_OF_IN_PORT        OXM_HEADER   (OFPXMT12_OFB_IN_PORT, 4)
 #define OXM_OF_IN_PHY_PORT    OXM_HEADER   (OFPXMT12_OFB_IN_PHY_PORT, 4)
@@ -196,7 +190,7 @@ enum ofp12_action_type {
                                   to outermost */
     OFPAT12_COPY_TTL_IN,       /* Copy TTL "inwards" -- from outermost to
                                   next-to-outermost */
-    OFPAT12_SET_MPLS_TTL,      /* MPLS TTL */
+    OFPAT12_SET_MPLS_TTL = 15, /* MPLS TTL */
     OFPAT12_DEC_MPLS_TTL,      /* Decrement MPLS TTL */
     OFPAT12_PUSH_VLAN,         /* Push a new VLAN tag */
     OFPAT12_POP_VLAN,          /* Pop the outer VLAN tag */
@@ -211,11 +205,11 @@ enum ofp12_action_type {
 };
 
 enum ofp12_controller_max_len {
-       OFPCML12_MAX       = 0xffe5, /* maximum max_len value which can be used
-                                     * to request a specific byte length. */
-       OFPCML12_NO_BUFFER = 0xffff  /* indicates that no buffering should be
-                                     * applied and the whole packet is to be
-                                     * sent to the controller. */
+    OFPCML12_MAX       = 0xffe5, /* maximum max_len value which can be used
+                                  * to request a specific byte length. */
+    OFPCML12_NO_BUFFER = 0xffff  /* indicates that no buffering should be
+                                  * applied and the whole packet is to be
+                                  * sent to the controller. */
 };
 
 /* Action structure for OFPAT12_SET_FIELD. */
@@ -262,7 +256,6 @@ enum ofp12_queue_properties {
 
 /* Body of reply to OFPST_TABLE request. */
 struct ofp12_table_stats {
-    struct ofp11_stats_msg osm;
     uint8_t table_id;        /* Identifier of table.  Lower numbered tables
                                 are consulted first. */
     uint8_t pad[7];          /* Align to 64-bits. */
@@ -288,17 +281,16 @@ struct ofp12_table_stats {
     ovs_be64 lookup_count;   /* Number of packets looked up in table. */
     ovs_be64 matched_count;  /* Number of packets that hit table. */
 };
-OFP_ASSERT(sizeof(struct ofp12_table_stats) == 144);
+OFP_ASSERT(sizeof(struct ofp12_table_stats) == 128);
 
 /* Body of reply to OFPST12_GROUP_FEATURES request. Group features. */
 struct ofp12_group_features_stats {
-    struct ofp11_stats_msg osm;
     ovs_be32  types;           /* Bitmap of OFPGT_* values supported. */
     ovs_be32  capabilities;    /* Bitmap of OFPGFC12_* capability supported. */
     ovs_be32  max_groups[4];   /* Maximum number of groups for each type. */
     ovs_be32  actions[4];      /* Bitmaps of OFPAT_* that are supported. */
 };
-OFP_ASSERT(sizeof(struct ofp12_group_features_stats) == 56);
+OFP_ASSERT(sizeof(struct ofp12_group_features_stats) == 40);
 
 /* Group configuration flags */
 enum ofp12_group_capabilities {
@@ -319,12 +311,11 @@ OFP_ASSERT(sizeof(struct ofp12_experimenter_stats_header) == 8);
 
 /* Role request and reply message. */
 struct ofp12_role_request {
-    struct ofp_header header; /* Type OFPT12_ROLE_REQUEST/OFPT12_ROLE_REPLY. */
     ovs_be32 role;            /* One of OFPCR12_ROLE_*. */
     uint8_t pad[4];           /* Align to 64 bits. */
     ovs_be64 generation_id;   /* Master Election Generation Id */
 };
-OFP_ASSERT(sizeof(struct ofp12_role_request) == 24);
+OFP_ASSERT(sizeof(struct ofp12_role_request) == 16);
 
 /* Controller roles. */
 enum ofp12_controller_role {
@@ -336,7 +327,6 @@ enum ofp12_controller_role {
 
 /* Packet received on port (datapath -> controller). */
 struct ofp12_packet_in {
-    struct ofp_header header;
     ovs_be32 buffer_id;     /* ID assigned by datapath. */
     ovs_be16 total_len;     /* Full length of frame. */
     uint8_t reason;         /* Reason packet is being sent (one of OFPR_*) */
@@ -352,11 +342,10 @@ struct ofp12_packet_in {
     /* uint8_t pad[2];         Align to 64 bit + 16 bit */
     /* uint8_t data[0];        Ethernet frame */
 };
-OFP_ASSERT(sizeof(struct ofp12_packet_in) == 16);
+OFP_ASSERT(sizeof(struct ofp12_packet_in) == 8);
 
 /* Flow removed (datapath -> controller). */
 struct ofp12_flow_removed {
-    struct ofp_header header;
     ovs_be64 cookie;          /* Opaque controller-issued identifier. */
 
     ovs_be16 priority;        /* Priority level of flow entry. */
@@ -372,6 +361,6 @@ struct ofp12_flow_removed {
     ovs_be64 byte_count;
     /* struct ofp12_match match;  Description of fields. Variable size. */
 };
-OFP_ASSERT(sizeof(struct ofp12_flow_removed) == 48);
+OFP_ASSERT(sizeof(struct ofp12_flow_removed) == 40);
 
 #endif /* openflow/openflow-1.2.h */
index 9ef8873..0bca0d2 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2011 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2011, 2012 The Board of Trustees of The Leland Stanford
  * Junior University
  *
  * We are making the OpenFlow specification and associated documentation
@@ -32,7 +32,7 @@
  */
 
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
 /* The most significant bit being set in the version field indicates an
  * experimental OpenFlow version.
  */
-#define OFP10_VERSION   0x01
-#define OFP11_VERSION   0x02
-#define OFP12_VERSION   0x03
+enum ofp_version {
+    OFP10_VERSION = 0x01,
+    OFP11_VERSION = 0x02,
+    OFP12_VERSION = 0x03,
+};
 
 #define OFP_MAX_TABLE_NAME_LEN 32
 #define OFP_MAX_PORT_NAME_LEN  16
 
 #define OFP_ETH_ALEN 6          /* Bytes in an Ethernet address. */
 
-/* Common OpenFlow message types. */
-enum ofp_type {
-    /* Immutable messages. */
-    OFPT_HELLO,               /* Symmetric message */
-    OFPT_ERROR,               /* Symmetric message */
-    OFPT_ECHO_REQUEST,        /* Symmetric message */
-    OFPT_ECHO_REPLY,          /* Symmetric message */
-    OFPT_VENDOR,              /* Symmetric message */
-
-    /* Switch configuration messages. */
-    OFPT_FEATURES_REQUEST,    /* Controller/switch message */
-    OFPT_FEATURES_REPLY,      /* Controller/switch message */
-    OFPT_GET_CONFIG_REQUEST,  /* Controller/switch message */
-    OFPT_GET_CONFIG_REPLY,    /* Controller/switch message */
-    OFPT_SET_CONFIG,          /* Controller/switch message */
-
-    /* Asynchronous messages. */
-    OFPT_PACKET_IN,           /* Async message */
-    OFPT_FLOW_REMOVED,        /* Async message */
-    OFPT_PORT_STATUS,         /* Async message */
-};
-
 /* Header on all OpenFlow packets. */
 struct ofp_header {
     uint8_t version;    /* An OpenFlow version number, e.g. OFP10_VERSION. */
@@ -179,7 +159,6 @@ OFP_ASSERT(sizeof(struct ofp_queue_prop_min_rate) == 16);
 
 /* Switch features. */
 struct ofp_switch_features {
-    struct ofp_header header;
     ovs_be64 datapath_id;   /* Datapath unique ID.  The lower 48-bits are for
                                a MAC address, while the upper 16-bits are
                                implementer-defined. */
@@ -196,7 +175,7 @@ struct ofp_switch_features {
     /* Followed by an array of struct ofp10_phy_port or struct ofp11_port
      * structures.  The number is inferred from header.length. */
 };
-OFP_ASSERT(sizeof(struct ofp_switch_features) == 32);
+OFP_ASSERT(sizeof(struct ofp_switch_features) == 24);
 
 /* Common capabilities supported by the datapath (struct ofp_switch_features,
  * member capabilities). */
@@ -218,12 +197,80 @@ enum ofp_packet_in_reason {
     OFPR_N_REASONS
 };
 
+enum ofp_flow_mod_command {
+    OFPFC_ADD,              /* New flow. */
+    OFPFC_MODIFY,           /* Modify all matching flows. */
+    OFPFC_MODIFY_STRICT,    /* Modify entry strictly matching wildcards */
+    OFPFC_DELETE,           /* Delete all matching flows. */
+    OFPFC_DELETE_STRICT     /* Strictly match wildcards and priority. */
+};
+
+enum ofp_flow_mod_flags {
+    OFPFF_SEND_FLOW_REM = 1 << 0,  /* Send flow removed message when flow
+                                    * expires or is deleted. */
+    OFPFF_CHECK_OVERLAP = 1 << 1,  /* Check for overlapping entries first. */
+};
+
+/* Action structure for OFPAT10_SET_VLAN_VID and OFPAT11_SET_VLAN_VID. */
+struct ofp_action_vlan_vid {
+    ovs_be16 type;                  /* Type. */
+    ovs_be16 len;                   /* Length is 8. */
+    ovs_be16 vlan_vid;              /* VLAN id. */
+    uint8_t pad[2];
+};
+OFP_ASSERT(sizeof(struct ofp_action_vlan_vid) == 8);
+
+/* Action structure for OFPAT10_SET_VLAN_PCP and OFPAT11_SET_VLAN_PCP. */
+struct ofp_action_vlan_pcp {
+    ovs_be16 type;                  /* Type. */
+    ovs_be16 len;                   /* Length is 8. */
+    uint8_t vlan_pcp;               /* VLAN priority. */
+    uint8_t pad[3];
+};
+OFP_ASSERT(sizeof(struct ofp_action_vlan_pcp) == 8);
+
+/* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */
+struct ofp_action_dl_addr {
+    ovs_be16 type;                  /* Type. */
+    ovs_be16 len;                   /* Length is 16. */
+    uint8_t dl_addr[OFP_ETH_ALEN];  /* Ethernet address. */
+    uint8_t pad[6];
+};
+OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16);
+
+/* Action structure for OFPAT10_SET_NW_SRC/DST and OFPAT11_SET_NW_SRC/DST. */
+struct ofp_action_nw_addr {
+    ovs_be16 type;                  /* Type. */
+    ovs_be16 len;                   /* Length is 8. */
+    ovs_be32 nw_addr;               /* IP address. */
+};
+OFP_ASSERT(sizeof(struct ofp_action_nw_addr) == 8);
+
+/* Action structure for OFPAT10_SET_NW_TOS and OFPAT11_SET_NW_TOS. */
+struct ofp_action_nw_tos {
+    ovs_be16 type;                  /* Type.. */
+    ovs_be16 len;                   /* Length is 8. */
+    uint8_t nw_tos;                 /* DSCP in high 6 bits, rest ignored. */
+    uint8_t pad[3];
+};
+OFP_ASSERT(sizeof(struct ofp_action_nw_tos) == 8);
+
+/* Action structure for OFPAT10_SET_TP_SRC/DST and OFPAT11_SET_TP_SRC/DST. */
+struct ofp_action_tp_port {
+    ovs_be16 type;                  /* Type. */
+    ovs_be16 len;                   /* Length is 8. */
+    ovs_be16 tp_port;               /* TCP/UDP port. */
+    uint8_t pad[2];
+};
+OFP_ASSERT(sizeof(struct ofp_action_tp_port) == 8);
+
 /* Why was this flow removed? */
 enum ofp_flow_removed_reason {
     OFPRR_IDLE_TIMEOUT,         /* Flow idle time exceeded idle_timeout. */
     OFPRR_HARD_TIMEOUT,         /* Time exceeded hard_timeout. */
     OFPRR_DELETE,               /* Evicted by a DELETE flow mod. */
-    OFPRR_GROUP_DELETE          /* Group was removed. */
+    OFPRR_GROUP_DELETE,         /* Group was removed. */
+    OFPRR_EVICTION,             /* Switch eviction to free resources. */
 };
 
 /* What changed about the physical port */
@@ -235,12 +282,34 @@ enum ofp_port_reason {
 
 /* A physical port has changed in the datapath */
 struct ofp_port_status {
-    struct ofp_header header;
     uint8_t reason;          /* One of OFPPR_*. */
     uint8_t pad[7];          /* Align to 64-bits. */
     /* Followed by struct ofp10_phy_port or struct ofp11_port.  */
 };
-OFP_ASSERT(sizeof(struct ofp_port_status) == 16);
+OFP_ASSERT(sizeof(struct ofp_port_status) == 8);
+
+#define DESC_STR_LEN   256
+#define SERIAL_NUM_LEN 32
+/* Body of reply to OFPST_DESC request.  Each entry is a NULL-terminated ASCII
+ * string. */
+struct ofp_desc_stats {
+    char mfr_desc[DESC_STR_LEN];       /* Manufacturer description. */
+    char hw_desc[DESC_STR_LEN];        /* Hardware description. */
+    char sw_desc[DESC_STR_LEN];        /* Software description. */
+    char serial_num[SERIAL_NUM_LEN];   /* Serial number. */
+    char dp_desc[DESC_STR_LEN];        /* Human readable description of
+                                          the datapath. */
+};
+OFP_ASSERT(sizeof(struct ofp_desc_stats) == 1056);
+
+/* Reply to OFPST_AGGREGATE request. */
+struct ofp_aggregate_stats_reply {
+    ovs_32aligned_be64 packet_count; /* Number of packets in flows. */
+    ovs_32aligned_be64 byte_count;   /* Number of bytes in flows. */
+    ovs_be32 flow_count;      /* Number of flows. */
+    uint8_t pad[4];           /* Align to 64 bits. */
+};
+OFP_ASSERT(sizeof(struct ofp_aggregate_stats_reply) == 24);
 
 /* The match type indicates the match structure (set of fields that compose the
  * match) in use. The match type is placed in the type field at the beginning
@@ -255,4 +324,14 @@ enum ofp_match_type {
     OFPMT_OXM = 1,              /* OpenFlow Extensible Match */
 };
 
+/* Group numbering. Groups can use any number up to OFPG_MAX. */
+enum ofp_group {
+    /* Last usable group number. */
+    OFPG_MAX        = 0xffffff00,
+
+    /* Fake groups. */
+    OFPG_ALL        = 0xfffffffc,  /* All groups, for group delete commands. */
+    OFPG_ANY        = 0xffffffff   /* Wildcard, for flow stats requests. */
+};
+
 #endif /* openflow/openflow-common.h */
index 7ae750a..b2516c0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index d238a16..7e5845a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
  *
  * This file is offered under your choice of two licenses: Apache 2.0 or GNU
  * GPL 2.0 or later.  The permission statements for each of these licenses is
index cf0e519..338b422 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
  *
  * This file is offered under your choice of two licenses: Apache 2.0 or GNU
  * GPL 2.0 or later.  The permission statements for each of these licenses is
index b9a1c8e..c494791 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
  *
  * This file is offered under your choice of two licenses: Apache 2.0 or GNU
  * GPL 2.0 or later.  The permission statements for each of these licenses is
index ad05757..72caa5c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 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.
index c545f29..dd64e61 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index f396761..23c45f1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 884c6de..f94c527 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index ed31463..b3924c3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index c6608a1..ca80c97 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 1ed195b..13f61e5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -47,6 +47,37 @@ struct msghdr {
     int            msg_flags;
 };
 
+struct cmsghdr {
+    size_t cmsg_len;
+    int cmsg_level;
+    int cmsg_type;
+    unsigned char cmsg_data[];
+};
+
+#define __CMSG_ALIGNTO sizeof(size_t)
+#define CMSG_ALIGN(LEN) \
+        (((LEN) + __CMSG_ALIGNTO - 1) / __CMSG_ALIGNTO * __CMSG_ALIGNTO)
+#define CMSG_DATA(CMSG) ((CMSG)->cmsg_data)
+#define CMSG_LEN(LEN) (sizeof(struct cmsghdr) + (LEN))
+#define CMSG_SPACE(LEN) CMSG_ALIGN(CMSG_LEN(LEN))
+#define CMSG_FIRSTHDR(MSG) \
+    ((MSG)->msg_controllen ? (struct cmsghdr *) (MSG)->msg_control : NULL)
+#define CMSG_NXTHDR(MSG, CMSG) __cmsg_nxthdr(MSG, CMSG)
+
+static inline struct cmsghdr *
+__cmsg_nxthdr(struct msghdr *msg, struct cmsghdr *cmsg)
+{
+    size_t ofs = (char *) cmsg - (char *) msg->msg_control;
+    size_t next_ofs = ofs + CMSG_ALIGN(cmsg->cmsg_len);
+    return (next_ofs < msg->msg_controllen
+            ? (void *) ((char *) msg->msg_control + next_ofs)
+            : NULL);
+}
+
+enum {
+    SCM_RIGHTS = 1
+};
+
 enum {
     SOCK_DGRAM,
     SOCK_RAW,
index 31e1d76..dbc2d9c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index ba73f28..f4c59ad 100644 (file)
@@ -4,6 +4,7 @@
 /dirs.c
 /coverage-counters.c
 /ofp-errors.inc
+/ofp-msgs.inc
 /vswitch-idl.c
 /vswitch-idl.h
 /vswitch-idl.ovsidl
index aa7f560..b95cc56 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index bbd7475..f0f55d7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 11e9738..8f081eb 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -79,6 +79,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/lockfile.h \
        lib/mac-learning.c \
        lib/mac-learning.h \
+       lib/memory.c \
+       lib/memory.h \
        lib/meta-flow.c \
        lib/meta-flow.h \
        lib/multipath.c \
@@ -95,8 +97,12 @@ lib_libopenvswitch_a_SOURCES = \
        lib/nx-match.h \
        lib/odp-util.c \
        lib/odp-util.h \
+       lib/ofp-actions.c \
+       lib/ofp-actions.h \
        lib/ofp-errors.c \
        lib/ofp-errors.h \
+       lib/ofp-msgs.c \
+       lib/ofp-msgs.h \
        lib/ofp-parse.c \
        lib/ofp-parse.h \
        lib/ofp-print.c \
@@ -136,8 +142,12 @@ lib_libopenvswitch_a_SOURCES = \
        lib/sha1.h \
        lib/shash.c \
        lib/shash.h \
+       lib/simap.c \
+       lib/simap.h \
        lib/signals.c \
        lib/signals.h \
+       lib/smap.c \
+       lib/smap.h \
        lib/socket-util.c \
        lib/socket-util.h \
        lib/sort.c \
@@ -168,6 +178,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/timer.h \
        lib/timeval.c \
        lib/timeval.h \
+       lib/token-bucket.c \
+       lib/token-bucket.h \
        lib/tunalloc.c \
        lib/tunalloc.h \
        lib/type-props.h \
@@ -192,7 +204,9 @@ lib_libopenvswitch_a_SOURCES = \
        lib/vlog.c \
        lib/vlog.h \
        lib/vswitch-idl.c \
-       lib/vswitch-idl.h
+       lib/vswitch-idl.h \
+       lib/worker.c \
+       lib/worker.h
 
 nodist_lib_libopenvswitch_a_SOURCES = \
        lib/dirs.c
@@ -233,6 +247,14 @@ lib_libopenvswitch_a_SOURCES += \
        lib/route-table.h
 endif
 
+if HAVE_IF_DL
+lib_libopenvswitch_a_SOURCES += \
+       lib/netdev-bsd.c \
+       lib/rtbsd.c \
+       lib/rtbsd.h \
+       lib/route-table-bsd.c
+endif
+
 if HAVE_OPENSSL
 lib_libopenvswitch_a_SOURCES += lib/stream-ssl.c
 nodist_lib_libopenvswitch_a_SOURCES += lib/dhparams.c
@@ -260,6 +282,7 @@ MAN_FRAGMENTS += \
        lib/daemon.man \
        lib/daemon-syn.man \
        lib/leak-checker.man \
+       lib/memory-unixctl.man \
        lib/ovs.tmac \
        lib/ssl-bootstrap.man \
        lib/ssl-bootstrap-syn.man \
@@ -293,6 +316,7 @@ lib/dirs.c: lib/dirs.c.in Makefile
                -e 's,[@]srcdir[@],$(srcdir),g' \
                -e 's,[@]LOGDIR[@],"$(LOGDIR)",g' \
                -e 's,[@]RUNDIR[@],"$(RUNDIR)",g' \
+               -e 's,[@]DBDIR[@],"$(DBDIR)",g' \
                -e 's,[@]bindir[@],"$(bindir)",g' \
                -e 's,[@]sysconfdir[@],"$(sysconfdir)",g' \
                -e 's,[@]pkgdatadir[@],"$(pkgdatadir)",g') \
@@ -301,16 +325,24 @@ lib/dirs.c: lib/dirs.c.in Makefile
 
 $(srcdir)/lib/ofp-errors.inc: \
        lib/ofp-errors.h $(srcdir)/build-aux/extract-ofp-errors
-       $(PYTHON) $(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.c: $(srcdir)/lib/ofp-errors.inc
 EXTRA_DIST += build-aux/extract-ofp-errors lib/ofp-errors.inc
 
+$(srcdir)/lib/ofp-msgs.inc: \
+       lib/ofp-msgs.h $(srcdir)/build-aux/extract-ofp-msgs
+       $(run_python) $(srcdir)/build-aux/extract-ofp-msgs \
+               $(srcdir)/lib/ofp-msgs.h $@ > $@.tmp && mv $@.tmp $@
+$(srcdir)/lib/ofp-msgs.c: $(srcdir)/lib/ofp-msgs.inc
+EXTRA_DIST += build-aux/extract-ofp-msgs lib/ofp-msgs.inc
+
 INSTALL_DATA_LOCAL += lib-install-data-local
 lib-install-data-local:
        $(MKDIR_P) $(DESTDIR)$(RUNDIR)
        $(MKDIR_P) $(DESTDIR)$(PKIDIR)
        $(MKDIR_P) $(DESTDIR)$(LOGDIR)
+       $(MKDIR_P) $(DESTDIR)$(DBDIR)
 
 if !USE_LINKER_SECTIONS
 # All distributed sources, with names adjust properly for referencing
index b8ba3c1..ae8007d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
 #include "flow.h"
 #include "meta-flow.h"
 #include "nx-match.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
 #include "ofp-util.h"
 #include "openflow/nicira-ext.h"
 
 VLOG_DEFINE_THIS_MODULE(autopath);
 
-/* Loads 'ofp_port' into the appropriate register in accordance with the
- * autopath action. */
 void
-autopath_execute(const struct nx_action_autopath *ap, struct flow *flow,
-                 uint16_t ofp_port)
-{
-    struct mf_subfield dst;
-
-    nxm_decode(&dst, ap->dst, ap->ofs_nbits);
-    mf_set_subfield_value(&dst, ofp_port, flow);
-}
-
-void
-autopath_parse(struct nx_action_autopath *ap, const char *s_)
+autopath_parse(struct ofpact_autopath *ap, const char *s_)
 {
     char *s;
-    char *id_str, *dst_s, *save_ptr;
-    struct mf_subfield dst;
     int id_int;
+    char *id_str, *dst, *save_ptr;
+
+    ofpact_init_AUTOPATH(ap);
 
     s = xstrdup(s_);
     save_ptr = NULL;
     id_str = strtok_r(s, ", ", &save_ptr);
-    dst_s = strtok_r(NULL, ", ", &save_ptr);
+    dst = strtok_r(NULL, ", ", &save_ptr);
 
-    if (!dst_s) {
+    if (!dst) {
         ovs_fatal(0, "%s: not enough arguments to autopath action", s_);
     }
 
@@ -65,33 +55,55 @@ autopath_parse(struct nx_action_autopath *ap, const char *s_)
         ovs_fatal(0, "%s: autopath id %d is not in valid range "
                   "1 to %"PRIu32, s_, id_int, UINT32_MAX);
     }
+    ap->port = id_int;
 
-    mf_parse_subfield(&dst, dst_s);
-    if (dst.n_bits < 16) {
+    mf_parse_subfield(&ap->dst, dst);
+    if (ap->dst.n_bits < 16) {
         ovs_fatal(0, "%s: %d-bit destination field has %u possible values, "
                   "less than required 65536",
-                  s_, dst.n_bits, 1u << dst.n_bits);
+                  s_, ap->dst.n_bits, 1u << ap->dst.n_bits);
     }
 
-    ofputil_init_NXAST_AUTOPATH(ap);
-    ap->id = htonl(id_int);
-    ap->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
-    ap->dst = htonl(dst.field->nxm_header);
-
     free(s);
 }
 
 enum ofperr
-autopath_check(const struct nx_action_autopath *ap, const struct flow *flow)
+autopath_from_openflow(const struct nx_action_autopath *nap,
+                       struct ofpact_autopath *autopath)
 {
-    struct mf_subfield dst;
+    ofpact_init_AUTOPATH(autopath);
+    autopath->dst.field = mf_from_nxm_header(ntohl(nap->dst));
+    autopath->dst.ofs = nxm_decode_ofs(nap->ofs_nbits);
+    autopath->dst.n_bits = nxm_decode_n_bits(nap->ofs_nbits);
+    autopath->port = ntohl(nap->id);
 
-    nxm_decode(&dst, ap->dst, ap->ofs_nbits);
-    if (dst.n_bits < 16) {
+    if (autopath->dst.n_bits < 16) {
         VLOG_WARN("at least 16 bit destination is required for autopath "
                   "action.");
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
-    return mf_check_dst(&dst, flow);
+    return autopath_check(autopath, NULL);
+}
+
+enum ofperr
+autopath_check(const struct ofpact_autopath *autopath, const struct flow *flow)
+{
+    VLOG_WARN_ONCE("The autopath action is deprecated and may be removed in"
+                   " February 2013.  Please email dev@openvswitch.org with"
+                   " concerns.");
+    return mf_check_dst(&autopath->dst, flow);
+}
+
+void
+autopath_to_nxast(const struct ofpact_autopath *autopath,
+                  struct ofpbuf *openflow)
+{
+    struct nx_action_autopath *ap;
+
+    ap = ofputil_put_NXAST_AUTOPATH__DEPRECATED(openflow);
+    ap->ofs_nbits = nxm_encode_ofs_nbits(autopath->dst.ofs,
+                                         autopath->dst.n_bits);
+    ap->dst = htonl(autopath->dst.field->nxm_header);
+    ap->id = htonl(autopath->port);
 }
index 19e2d07..337e7d1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 struct flow;
 struct nx_action_autopath;
+struct ofpact_autopath;
+struct ofpbuf;
 
 /* NXAST_AUTOPATH  helper functions.
  *
  * See include/openflow/nicira-ext.h for NXAST_AUTOPATH specification. */
 
-void autopath_execute(const struct nx_action_autopath *, struct flow *,
-                      uint16_t ofp_port);
-void autopath_parse(struct nx_action_autopath *, const char *);
-enum ofperr autopath_check(const struct nx_action_autopath *,
+void autopath_parse(struct ofpact_autopath *, const char *);
+
+enum ofperr autopath_from_openflow(const struct nx_action_autopath *,
+                                   struct ofpact_autopath *);
+enum ofperr autopath_check(const struct ofpact_autopath *,
                            const struct flow *);
+void autopath_to_nxast(const struct ofpact_autopath *,
+                       struct ofpbuf *openflow);
 
 #endif /* autopath.h */
index 1c0dfd6..c6829d7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index b1f25fa..cee0602 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 76a667a..d607526 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * 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.
index c76e45f..8980496 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
 static inline unsigned long *
 bitmap_unit__(const unsigned long *bitmap, size_t offset)
 {
-    return (unsigned long *) &bitmap[offset / BITMAP_ULONG_BITS];
+    return CONST_CAST(unsigned long *, &bitmap[offset / BITMAP_ULONG_BITS]);
 }
 
 static inline unsigned long
index 3b86ee4..25a0fa1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -133,7 +133,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 *,
-                                              uint16_t vlan);
+                                              uint16_t vlan, tag_type *tags);
 static void bond_update_fake_slave_stats(struct bond *);
 
 /* Attempts to parse 's' as the name of a bond balancing mode.  If successful,
@@ -255,6 +255,12 @@ bond_reconfigure(struct bond *bond, const struct bond_settings *s)
     if (bond->balance != s->balance) {
         bond->balance = s->balance;
         revalidate = true;
+
+        if (bond->balance == BM_STABLE) {
+            VLOG_WARN_ONCE("Stable bond mode is deprecated and may be removed"
+                           " in February 2013. Please email"
+                           " dev@openvswitch.org with concerns.");
+        }
     }
 
     if (bond->basis != s->basis) {
@@ -488,7 +494,7 @@ static bool
 may_send_learning_packets(const struct bond *bond)
 {
     return bond->lacp_status == LACP_DISABLED
-        && bond->balance != BM_AB
+        && bond->balance != BM_STABLE
         && bond->active_slave;
 }
 
@@ -498,8 +504,9 @@ may_send_learning_packets(const struct bond *bond)
  * is located.  For each MAC that has been learned on a port other than 'bond',
  * it should call bond_compose_learning_packet().
  *
- * This function will only return true if 'bond' is in SLB mode and LACP is not
- * negotiated.  Otherwise sending learning packets isn't necessary.
+ * This function will only return true if 'bond' is in SLB or active-backup
+ * mode and LACP is not negotiated.  Otherwise sending learning packets isn't
+ * necessary.
  *
  * Calling this function resets the state that it checks. */
 bool
@@ -522,17 +529,17 @@ bond_compose_learning_packet(struct bond *bond,
 {
     struct bond_slave *slave;
     struct ofpbuf *packet;
+    tag_type tags = 0;
     struct flow flow;
 
     assert(may_send_learning_packets(bond));
 
     memset(&flow, 0, sizeof flow);
     memcpy(flow.dl_src, eth_src, ETH_ADDR_LEN);
-    slave = choose_output_slave(bond, &flow, vlan);
+    slave = choose_output_slave(bond, &flow, vlan, &tags);
 
     packet = ofpbuf_new(0);
-    compose_benign_packet(packet, "Open vSwitch Bond Failover", 0xf177,
-                          eth_src);
+    compose_rarp(packet, eth_src);
     if (vlan) {
         eth_push_vlan(packet, htons(vlan));
     }
@@ -637,7 +644,7 @@ void *
 bond_choose_output_slave(struct bond *bond, const struct flow *flow,
                          uint16_t vlan, tag_type *tags)
 {
-    struct bond_slave *slave = choose_output_slave(bond, flow, vlan);
+    struct bond_slave *slave = choose_output_slave(bond, flow, vlan, tags);
     if (slave) {
         *tags |= bond->balance == BM_STABLE ? bond->stb_tag : slave->tag;
         return slave->aux;
@@ -1405,7 +1412,7 @@ choose_stb_slave(const struct bond *bond, uint32_t flow_hash)
 
 static struct bond_slave *
 choose_output_slave(const struct bond *bond, const struct flow *flow,
-                    uint16_t vlan)
+                    uint16_t vlan, tag_type *tags)
 {
     struct bond_entry *e;
 
@@ -1441,6 +1448,7 @@ choose_output_slave(const struct bond *bond, const struct flow *flow,
             }
             e->tag = tag_create_random();
         }
+        *tags |= e->tag;
         return e->slave;
 
     default:
index 6d8161d..7329db7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 733d79a..e0f8e6b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, 2012 Nicira Networks.
+/* Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
 #include "meta-flow.h"
 #include "nx-match.h"
 #include "ofpbuf.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
 #include "ofp-util.h"
 #include "openflow/nicira-ext.h"
 VLOG_DEFINE_THIS_MODULE(bundle);
 
 static uint16_t
-execute_ab(const struct nx_action_bundle *nab,
+execute_ab(const struct ofpact_bundle *bundle,
            bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
 {
     size_t i;
 
-    for (i = 0; i < ntohs(nab->n_slaves); i++) {
-        uint16_t slave = bundle_get_slave(nab, i);
-
+    for (i = 0; i < bundle->n_slaves; i++) {
+        uint16_t slave = bundle->slaves[i];
         if (slave_enabled(slave, aux)) {
             return slave;
         }
@@ -52,18 +52,18 @@ execute_ab(const struct nx_action_bundle *nab,
 }
 
 static uint16_t
-execute_hrw(const struct nx_action_bundle *nab, const struct flow *flow,
+execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow,
             bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
 {
     uint32_t flow_hash, best_hash;
     int best, i;
 
-    flow_hash = flow_hash_fields(flow, ntohs(nab->fields), ntohs(nab->basis));
+    flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis);
     best = -1;
     best_hash = 0;
 
-    for (i = 0; i < ntohs(nab->n_slaves); i++) {
-        if (slave_enabled(bundle_get_slave(nab, i), aux)) {
+    for (i = 0; i < bundle->n_slaves; i++) {
+        if (slave_enabled(bundle->slaves[i], aux)) {
             uint32_t hash = hash_2words(i, flow_hash);
 
             if (best < 0 || hash > best_hash) {
@@ -73,33 +73,26 @@ execute_hrw(const struct nx_action_bundle *nab, const struct flow *flow,
         }
     }
 
-    return best >= 0 ? bundle_get_slave(nab, best) : OFPP_NONE;
+    return best >= 0 ? bundle->slaves[best] : OFPP_NONE;
 }
 
-/* Executes 'nab' on 'flow'.  Uses 'slave_enabled' to determine if the slave
+/* 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 nx_action_bundle *nab, const struct flow *flow,
+bundle_execute(const struct ofpact_bundle *bundle, const struct flow *flow,
                bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
 {
-    switch (ntohs(nab->algorithm)) {
-    case NX_BD_ALG_HRW: return execute_hrw(nab, flow, slave_enabled, aux);
-    case NX_BD_ALG_ACTIVE_BACKUP: return execute_ab(nab, slave_enabled, aux);
-    default: NOT_REACHED();
-    }
-}
+    switch (bundle->algorithm) {
+    case NX_BD_ALG_HRW:
+        return execute_hrw(bundle, flow, slave_enabled, aux);
 
-void
-bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow,
-                    bool (*slave_enabled)(uint16_t ofp_port, void *aux),
-                    void *aux)
-{
-    struct mf_subfield dst;
+    case NX_BD_ALG_ACTIVE_BACKUP:
+        return execute_ab(bundle, slave_enabled, aux);
 
-    nxm_decode(&dst, nab->dst, nab->ofs_nbits);
-    mf_set_subfield_value(&dst, bundle_execute(nab, flow, slave_enabled, aux),
-                          flow);
+    default:
+        NOT_REACHED();
+    }
 }
 
 /* Checks that 'nab' specifies a bundle action which is supported by this
@@ -107,41 +100,43 @@ bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow,
  * ofputil_check_output_port().  Returns 0 if 'nab' is supported, otherwise an
  * OFPERR_* error code. */
 enum ofperr
-bundle_check(const struct nx_action_bundle *nab, int max_ports,
-             const struct flow *flow)
+bundle_from_openflow(const struct nx_action_bundle *nab,
+                     struct ofpbuf *ofpacts)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-    uint16_t n_slaves, fields, algorithm, subtype;
+    struct ofpact_bundle *bundle;
+    uint16_t subtype;
     uint32_t slave_type;
     size_t slaves_size, i;
     enum ofperr error;
 
+    bundle = ofpact_put_BUNDLE(ofpacts);
+
     subtype = ntohs(nab->subtype);
-    n_slaves = ntohs(nab->n_slaves);
-    fields = ntohs(nab->fields);
-    algorithm = ntohs(nab->algorithm);
+    bundle->n_slaves = ntohs(nab->n_slaves);
+    bundle->basis = ntohs(nab->basis);
+    bundle->fields = ntohs(nab->fields);
+    bundle->algorithm = ntohs(nab->algorithm);
     slave_type = ntohl(nab->slave_type);
     slaves_size = ntohs(nab->len) - sizeof *nab;
 
     error = OFPERR_OFPBAC_BAD_ARGUMENT;
-    if (!flow_hash_fields_valid(fields)) {
-        VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, fields);
-    } else if (n_slaves > BUNDLE_MAX_SLAVES) {
+    if (!flow_hash_fields_valid(bundle->fields)) {
+        VLOG_WARN_RL(&rl, "unsupported fields %d", (int) bundle->fields);
+    } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) {
         VLOG_WARN_RL(&rl, "too may slaves");
-    } else if (algorithm != NX_BD_ALG_HRW
-               && algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
-        VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16, algorithm);
+    } else if (bundle->algorithm != NX_BD_ALG_HRW
+               && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
+        VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm);
     } else if (slave_type != NXM_OF_IN_PORT) {
         VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type);
     } else {
         error = 0;
     }
 
-    for (i = 0; i < sizeof(nab->zero); i++) {
-        if (nab->zero[i]) {
-            VLOG_WARN_RL(&rl, "reserved field is nonzero");
-            error = OFPERR_OFPBAC_BAD_ARGUMENT;
-        }
+    if (!is_all_zeros(nab->zero, sizeof nab->zero)) {
+        VLOG_WARN_RL(&rl, "reserved field is nonzero");
+        error = OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
     if (subtype == NXAST_BUNDLE && (nab->ofs_nbits || nab->dst)) {
@@ -150,34 +145,61 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
     }
 
     if (subtype == NXAST_BUNDLE_LOAD) {
-        struct mf_subfield dst;
+        bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst));
+        bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits);
+        bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits);
 
-        nxm_decode(&dst, nab->dst, nab->ofs_nbits);
-        if (dst.n_bits < 16) {
+        if (bundle->dst.n_bits < 16) {
             VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit "
                          "destination.");
             error = OFPERR_OFPBAC_BAD_ARGUMENT;
-        } else if (!error) {
-            error = mf_check_dst(&dst, flow);
         }
     }
 
-    if (slaves_size < n_slaves * sizeof(ovs_be16)) {
+    if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) {
         VLOG_WARN_RL(&rl, "Nicira action %"PRIu16" only has %zu bytes "
                      "allocated for slaves.  %zu bytes are required for "
                      "%"PRIu16" slaves.", subtype, slaves_size,
-                     n_slaves * sizeof(ovs_be16), n_slaves);
+                     bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves);
         error = OFPERR_OFPBAC_BAD_LEN;
     }
 
-    for (i = 0; i < n_slaves; i++) {
-        uint16_t ofp_port = bundle_get_slave(nab, i);
-        enum ofperr ofputil_error;
+    for (i = 0; i < bundle->n_slaves; i++) {
+        uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]);
+        ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port);
+    }
+
+    bundle = ofpacts->l2;
+    ofpact_update_len(ofpacts, &bundle->ofpact);
+
+    if (!error) {
+        error = bundle_check(bundle, OFPP_MAX, NULL);
+    }
+    return error;
+}
+
+enum ofperr
+bundle_check(const struct ofpact_bundle *bundle, int max_ports,
+             const struct flow *flow)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    size_t i;
+
+    if (bundle->dst.field) {
+        enum ofperr error = mf_check_dst(&bundle->dst, flow);
+        if (error) {
+            return error;
+        }
+    }
+
+    for (i = 0; i < bundle->n_slaves; i++) {
+        uint16_t ofp_port = bundle->slaves[i];
+        enum ofperr error;
 
-        ofputil_error = ofputil_check_output_port(ofp_port, max_ports);
-        if (ofputil_error) {
+        error = ofputil_check_output_port(ofp_port, max_ports);
+        if (error) {
             VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port);
-            error = ofputil_error;
+            return error;
         }
 
         /* Controller slaves are unsupported due to the lack of a max_len
@@ -185,23 +207,50 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
          * seem to be a real-world use-case for supporting it. */
         if (ofp_port == OFPP_CONTROLLER) {
             VLOG_WARN_RL(&rl, "unsupported controller slave");
-            error = OFPERR_OFPBAC_BAD_OUT_PORT;
+            return OFPERR_OFPBAC_BAD_OUT_PORT;
         }
     }
 
-    return error;
+    return 0;
+}
+
+void
+bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow)
+{
+    int slaves_len = ROUND_UP(2 * bundle->n_slaves, OFP_ACTION_ALIGN);
+    struct nx_action_bundle *nab;
+    ovs_be16 *slaves;
+    size_t i;
+
+    nab = (bundle->dst.field
+           ? ofputil_put_NXAST_BUNDLE_LOAD(openflow)
+           : ofputil_put_NXAST_BUNDLE(openflow));
+    nab->len = htons(ntohs(nab->len) + slaves_len);
+    nab->algorithm = htons(bundle->algorithm);
+    nab->fields = htons(bundle->fields);
+    nab->basis = htons(bundle->basis);
+    nab->slave_type = htonl(NXM_OF_IN_PORT);
+    nab->n_slaves = htons(bundle->n_slaves);
+    if (bundle->dst.field) {
+        nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs,
+                                              bundle->dst.n_bits);
+        nab->dst = htonl(bundle->dst.field->nxm_header);
+    }
+
+    slaves = ofpbuf_put_zeros(openflow, slaves_len);
+    for (i = 0; i < bundle->n_slaves; i++) {
+        slaves[i] = htons(bundle->slaves[i]);
+    }
 }
 
 /* Helper for bundle_parse and bundle_parse_load. */
 static void
-bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr,
+bundle_parse__(const char *s, char **save_ptr,
                const char *fields, const char *basis, const char *algorithm,
-               const char *slave_type, const char *dst_s,
-               const char *slave_delim)
+               const char *slave_type, const char *dst,
+               const char *slave_delim, struct ofpbuf *ofpacts)
 {
-    enum ofputil_action_code code;
-    struct nx_action_bundle *nab;
-    uint16_t n_slaves;
+    struct ofpact_bundle *bundle;
 
     if (!slave_delim) {
         ovs_fatal(0, "%s: not enough arguments to bundle action", s);
@@ -212,72 +261,56 @@ bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr,
                    s, slave_delim);
     }
 
-    code = dst_s ? OFPUTIL_NXAST_BUNDLE_LOAD : OFPUTIL_NXAST_BUNDLE;
-    b->l2 = ofputil_put_action(code, b);
+    bundle = ofpact_put_BUNDLE(ofpacts);
 
-    n_slaves = 0;
     for (;;) {
-        ovs_be16 slave_be;
+        uint16_t slave_port;
         char *slave;
 
         slave = strtok_r(NULL, ", [", save_ptr);
-        if (!slave || n_slaves >= BUNDLE_MAX_SLAVES) {
+        if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
             break;
         }
 
-        slave_be = htons(atoi(slave));
-        ofpbuf_put(b, &slave_be, sizeof slave_be);
+        slave_port = atoi(slave);
+        ofpbuf_put(ofpacts, &slave_port, sizeof slave_port);
 
-        n_slaves++;
+        bundle = ofpacts->l2;
+        bundle->n_slaves++;
     }
+    ofpact_update_len(ofpacts, &bundle->ofpact);
 
-    /* Slaves array must be multiple of 8 bytes long. */
-    if (b->size % 8) {
-        ofpbuf_put_zeros(b, 8 - (b->size % 8));
-    }
-
-    nab = b->l2;
-    nab->len = htons(b->size - ((char *) b->l2 - (char *) b->data));
-    nab->n_slaves = htons(n_slaves);
-    nab->basis = htons(atoi(basis));
+    bundle->basis = atoi(basis);
 
     if (!strcasecmp(fields, "eth_src")) {
-        nab->fields = htons(NX_HASH_FIELDS_ETH_SRC);
+        bundle->fields = NX_HASH_FIELDS_ETH_SRC;
     } else if (!strcasecmp(fields, "symmetric_l4")) {
-        nab->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4);
+        bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
     } else {
         ovs_fatal(0, "%s: unknown fields `%s'", s, fields);
     }
 
     if (!strcasecmp(algorithm, "active_backup")) {
-        nab->algorithm = htons(NX_BD_ALG_ACTIVE_BACKUP);
+        bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
     } else if (!strcasecmp(algorithm, "hrw")) {
-        nab->algorithm = htons(NX_BD_ALG_HRW);
+        bundle->algorithm = NX_BD_ALG_HRW;
     } else {
         ovs_fatal(0, "%s: unknown algorithm `%s'", s, algorithm);
     }
 
-    if (!strcasecmp(slave_type, "ofport")) {
-        nab->slave_type = htonl(NXM_OF_IN_PORT);
-    } else {
+    if (strcasecmp(slave_type, "ofport")) {
         ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type);
     }
 
-    if (dst_s) {
-        struct mf_subfield dst;
-
-        mf_parse_subfield(&dst, dst_s);
-        nab->dst = htonl(dst.field->nxm_header);
-        nab->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
+    if (dst) {
+        mf_parse_subfield(&bundle->dst, dst);
     }
-
-    b->l2 = NULL;
 }
 
 /* Converts a bundle action string contained in 's' to an nx_action_bundle and
  * stores it in 'b'.  Sets 'b''s l2 pointer to NULL. */
 void
-bundle_parse(struct ofpbuf *b, const char *s)
+bundle_parse(const char *s, struct ofpbuf *ofpacts)
 {
     char *fields, *basis, *algorithm, *slave_type, *slave_delim;
     char *tokstr, *save_ptr;
@@ -290,15 +323,15 @@ bundle_parse(struct ofpbuf *b, const char *s)
     slave_type = strtok_r(NULL, ", ", &save_ptr);
     slave_delim = strtok_r(NULL, ": ", &save_ptr);
 
-    bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
-                   slave_delim);
+    bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
+                   slave_delim, ofpacts);
     free(tokstr);
 }
 
 /* Converts a bundle_load action string contained in 's' to an nx_action_bundle
  * and stores it in 'b'.  Sets 'b''s l2 pointer to NULL. */
 void
-bundle_parse_load(struct ofpbuf *b, const char *s)
+bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
 {
     char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim;
     char *tokstr, *save_ptr;
@@ -312,22 +345,22 @@ bundle_parse_load(struct ofpbuf *b, const char *s)
     dst = strtok_r(NULL, ", ", &save_ptr);
     slave_delim = strtok_r(NULL, ": ", &save_ptr);
 
-    bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, dst,
-                   slave_delim);
+    bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, dst,
+                   slave_delim, ofpacts);
 
     free(tokstr);
 }
 
 /* Appends a human-readable representation of 'nab' to 's'. */
 void
-bundle_format(const struct nx_action_bundle *nab, struct ds *s)
+bundle_format(const struct ofpact_bundle *bundle, struct ds *s)
 {
-    const char *action, *fields, *algorithm, *slave_type;
+    const char *action, *fields, *algorithm;
     size_t i;
 
-    fields = flow_hash_fields_to_str(ntohs(nab->fields));
+    fields = flow_hash_fields_to_str(bundle->fields);
 
-    switch (ntohs(nab->algorithm)) {
+    switch (bundle->algorithm) {
     case NX_BD_ALG_HRW:
         algorithm = "hrw";
         break;
@@ -338,43 +371,23 @@ bundle_format(const struct nx_action_bundle *nab, struct ds *s)
         algorithm = "<unknown>";
     }
 
-    switch (ntohl(nab->slave_type)) {
-    case NXM_OF_IN_PORT:
-        slave_type = "ofport";
-        break;
-    default:
-        slave_type = "<unknown>";
-    }
-
-    switch (ntohs(nab->subtype)) {
-    case NXAST_BUNDLE:
-        action = "bundle";
-        break;
-    case NXAST_BUNDLE_LOAD:
-        action = "bundle_load";
-        break;
-    default:
-        NOT_REACHED();
-    }
+    action = bundle->dst.field ? "bundle_load" : "bundle";
 
     ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields,
-                  ntohs(nab->basis), algorithm, slave_type);
-
-    if (nab->subtype == htons(NXAST_BUNDLE_LOAD)) {
-        struct mf_subfield dst;
+                  bundle->basis, algorithm, "ofport");
 
-        nxm_decode(&dst, nab->dst, nab->ofs_nbits);
-        mf_format_subfield(&dst, s);
+    if (bundle->dst.field) {
+        mf_format_subfield(&bundle->dst, s);
         ds_put_cstr(s, ",");
     }
 
     ds_put_cstr(s, "slaves:");
-    for (i = 0; i < ntohs(nab->n_slaves); i++) {
+    for (i = 0; i < bundle->n_slaves; i++) {
         if (i) {
             ds_put_cstr(s, ",");
         }
 
-        ds_put_format(s, "%"PRIu16, bundle_get_slave(nab, i));
+        ds_put_format(s, "%"PRIu16, bundle->slaves[i]);
     }
 
     ds_put_cstr(s, ")");
index 580ecf8..5b6bb67 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 Nicira Networks.
+/* Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 struct ds;
 struct flow;
+struct ofpact_bundle;
 struct ofpbuf;
 
 /* NXAST_BUNDLE helper functions.
  *
  * See include/openflow/nicira-ext.h for NXAST_BUNDLE specification. */
 
-uint16_t bundle_execute(const struct nx_action_bundle *, const struct flow *,
+uint16_t bundle_execute(const struct ofpact_bundle *, const struct flow *,
                         bool (*slave_enabled)(uint16_t ofp_port, void *aux),
                         void *aux);
-void bundle_execute_load(const struct nx_action_bundle *, struct flow *,
-                         bool (*slave_enabled)(uint16_t ofp_port, void *aux),
-                         void *aux);
-enum ofperr bundle_check(const struct nx_action_bundle *, int max_ports,
+enum ofperr bundle_from_openflow(const struct nx_action_bundle *,
+                                 struct ofpbuf *ofpact);
+enum ofperr bundle_check(const struct ofpact_bundle *, int max_ports,
                          const struct flow *);
-void bundle_parse(struct ofpbuf *, const char *);
-void bundle_parse_load(struct ofpbuf *b, const char *);
-void bundle_format(const struct nx_action_bundle *, struct ds *);
-
-/* Returns the 'i'th slave in 'nab'. */
-static inline uint16_t
-bundle_get_slave(const struct nx_action_bundle *nab, size_t i)
-{
-    return ntohs(((ovs_be16 *)(nab + 1))[i]);
-}
+void bundle_to_nxast(const struct ofpact_bundle *, struct ofpbuf *of10);
+void bundle_parse(const char *, struct ofpbuf *ofpacts);
+void bundle_parse_load(const char *, struct ofpbuf *ofpacts);
+void bundle_format(const struct ofpact_bundle *, struct ds *);
 
 #endif /* bundle.h */
index 993137f..1eba6fe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2010, 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.
index aa1e06f..c43dff7 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009 Nicira Networks, Inc.
+/* Copyright (c) 2008, 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 84d8696..5fa51fd 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009 Nicira Networks, Inc.
+/* Copyright (c) 2008, 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 66bca75..fc999ab 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -62,19 +62,19 @@ static const uint8_t eth_addr_ccm_x[6] = {
 #define CCM_RDI_MASK 0x80
 #define CFM_HEALTH_INTERVAL 6
 struct ccm {
-    uint8_t  mdlevel_version; /* MD Level and Version */
-    uint8_t  opcode;
-    uint8_t  flags;
-    uint8_t  tlv_offset;
+    uint8_t mdlevel_version; /* MD Level and Version */
+    uint8_t opcode;
+    uint8_t flags;
+    uint8_t tlv_offset;
     ovs_be32 seq;
     ovs_be16 mpid;
-    uint8_t  maid[CCM_MAID_LEN];
+    uint8_t maid[CCM_MAID_LEN];
 
     /* Defined by ITU-T Y.1731 should be zero */
     ovs_be16 interval_ms_x;      /* Transmission interval in ms. */
     ovs_be64 mpid64;             /* MPID in extended mode. */
     uint8_t opdown;              /* Operationally down. */
-    uint8_t  zero[5];
+    uint8_t zero[5];
 
     /* TLV space. */
     uint8_t end_tlv;
@@ -87,6 +87,7 @@ struct cfm {
 
     uint64_t mpid;
     bool extended;         /* Extended mode. */
+    bool booted;           /* A full fault interval has occured. */
     enum cfm_fault_reason fault;  /* Connectivity fault status. */
     enum cfm_fault_reason recv_fault;  /* Bit mask of faults occuring on
                                           receive. */
@@ -118,7 +119,7 @@ struct cfm {
                                  received. */
     int health_interval;      /* Number of fault_intervals since health was
                                  recomputed. */
-
+    long long int last_tx;    /* Last CCM transmission time. */
 };
 
 /* Remote MPs represent foreign network entities that are configured to have
@@ -132,6 +133,7 @@ struct remote_mp {
     uint32_t seq;        /* Most recently received sequence number. */
     uint8_t num_health_ccm; /* Number of received ccm frames every
                                CFM_HEALTH_INTERVAL * 'fault_interval'. */
+    long long int last_rx; /* Last CCM reception time. */
 
 };
 
@@ -159,21 +161,19 @@ cfm_fault_reason_to_str(int reason) {
 }
 
 static void
-ds_put_cfm_fault(struct ds *ds, int old_fault, int new_fault)
+ds_put_cfm_fault(struct ds *ds, int fault)
 {
     int i;
 
     for (i = 0; i < CFM_FAULT_N_REASONS; i++) {
         int reason = 1 << i;
 
-        if ((old_fault | new_fault) & reason) {
-            ds_put_format(ds, " %s%s",
-                          (!(old_fault & reason) ? "+"
-                           : !(new_fault & reason) ? "-"
-                           : ""),
-                          cfm_fault_reason_to_str(reason));
+        if (fault & reason) {
+            ds_put_format(ds, "%s ", cfm_fault_reason_to_str(reason));
         }
     }
+
+    ds_chomp(ds, ' ');
 }
 
 static void
@@ -299,6 +299,7 @@ cfm_create(const char *name)
     cfm->remote_opup = true;
     cfm->fault_override = -1;
     cfm->health = -1;
+    cfm->last_tx = 0;
     return cfm;
 }
 
@@ -375,8 +376,9 @@ cfm_run(struct cfm *cfm)
         HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) {
 
             if (!rmp->recv) {
-                VLOG_DBG("%s: no CCM from RMP %"PRIu64" in the last %lldms",
-                         cfm->name, rmp->mpid, interval);
+                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);
             } else {
@@ -394,16 +396,21 @@ cfm_run(struct cfm *cfm)
             cfm->fault |= CFM_FAULT_RECV;
         }
 
-        if (old_cfm_fault != cfm->fault) {
+        if (old_cfm_fault != cfm->fault && !VLOG_DROP_INFO(&rl)) {
             struct ds ds = DS_EMPTY_INITIALIZER;
 
-            ds_put_cfm_fault(&ds, old_cfm_fault, cfm->fault);
-            VLOG_INFO_RL(&rl, "%s: CFM fault status changed:%s", cfm->name,
-                         ds_cstr_ro(&ds));
+            ds_put_cstr(&ds, "from [");
+            ds_put_cfm_fault(&ds, old_cfm_fault);
+            ds_put_cstr(&ds, "] to [");
+            ds_put_cfm_fault(&ds, cfm->fault);
+            ds_put_char(&ds, ']');
+            VLOG_INFO("%s: CFM faults changed %s.", cfm->name, ds_cstr(&ds));
             ds_destroy(&ds);
         }
 
+        cfm->booted = true;
         timer_set_duration(&cfm->fault_timer, interval);
+        VLOG_DBG("%s: new fault interval", cfm->name);
     }
 }
 
@@ -460,11 +467,23 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet,
     if (cfm->ccm_interval == 0) {
         assert(cfm->extended);
         ccm->interval_ms_x = htons(cfm->ccm_interval_ms);
+    } else {
+        ccm->interval_ms_x = htons(0);
     }
 
-    if (hmap_is_empty(&cfm->remote_mps)) {
+    if (cfm->booted && hmap_is_empty(&cfm->remote_mps)) {
         ccm->flags |= CCM_RDI_MASK;
     }
+
+    if (cfm->last_tx) {
+        long long int delay = time_msec() - cfm->last_tx;
+        if (delay > (cfm->ccm_interval_ms * 3 / 2)) {
+            VLOG_WARN("%s: long delay of %lldms (expected %dms) sending CCM"
+                      " seq %"PRIu32, cfm->name, delay, cfm->ccm_interval_ms,
+                      cfm->seq);
+        }
+    }
+    cfm->last_tx = time_msec();
 }
 
 void
@@ -606,7 +625,7 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p)
         if (ccm_rdi) {
             cfm_fault |= CFM_FAULT_RDI;
             VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu64, cfm->name,
-                     rmp->mpid);
+                     ccm_mpid);
         }
 
         VLOG_DBG("%s: received CCM (seq %"PRIu32") (mpid %"PRIu64")"
@@ -621,7 +640,6 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p)
             }
 
             if (rmp->seq && ccm_seq != (rmp->seq + 1)) {
-                cfm_fault |= CFM_FAULT_SEQUENCE;
                 VLOG_WARN_RL(&rl, "%s: (mpid %"PRIu64") detected sequence"
                              " numbers which indicate possible connectivity"
                              " problems (previous %"PRIu32") (current %"PRIu32
@@ -636,6 +654,7 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p)
             cfm->recv_fault |= cfm_fault;
             rmp->seq = ccm_seq;
             rmp->opup = !ccm_opdown;
+            rmp->last_rx = time_msec();
         }
     }
 }
@@ -665,12 +684,17 @@ cfm_get_health(const struct cfm *cfm)
 
 /* Gets the operational state of 'cfm'.  'cfm' is considered operationally down
  * if it has received a CCM with the operationally down bit set from any of its
- * remote maintenance points. Returns true if 'cfm' is operationally up. False
- * otherwise. */
-bool
+ * remote maintenance points. Returns 1 if 'cfm' is operationally up, 0 if
+ * 'cfm' is operationally down, or -1 if 'cfm' has no operational state
+ * (because it isn't in extended mode). */
+int
 cfm_get_opup(const struct cfm *cfm)
 {
-    return cfm->remote_opup;
+    if (cfm->extended) {
+        return cfm->remote_opup;
+    } else {
+        return -1;
+    }
 }
 
 /* Populates 'rmps' with an array of remote maintenance points reachable by
@@ -710,8 +734,8 @@ cfm_print_details(struct ds *ds, const struct cfm *cfm)
 
     fault = cfm_get_fault(cfm);
     if (fault) {
-        ds_put_cstr(ds, "\tfault:");
-        ds_put_cfm_fault(ds, fault, fault);
+        ds_put_cstr(ds, "\tfault: ");
+        ds_put_cfm_fault(ds, fault);
         ds_put_cstr(ds, "\n");
     }
 
index 509da2b..de4c299 100644 (file)
--- a/lib/cfm.h
+++ b/lib/cfm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, 2011 Nicira Networks.
+/* Copyright (c) 2010, 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.
@@ -33,8 +33,7 @@ struct ofpbuf;
     CFM_FAULT_REASON(LOOPBACK, loopback)   \
     CFM_FAULT_REASON(OVERFLOW, overflow)   \
     CFM_FAULT_REASON(OVERRIDE, override)   \
-    CFM_FAULT_REASON(INTERVAL, interval)   \
-    CFM_FAULT_REASON(SEQUENCE, sequence)
+    CFM_FAULT_REASON(INTERVAL, interval)
 
 enum cfm_fault_bit_index {
 #define CFM_FAULT_REASON(NAME, STR) CFM_FAULT_INDEX_##NAME,
@@ -72,7 +71,7 @@ bool cfm_should_process_flow(const struct cfm *cfm, const struct flow *);
 void cfm_process_heartbeat(struct cfm *, const struct ofpbuf *packet);
 int cfm_get_fault(const struct cfm *);
 int cfm_get_health(const struct cfm *);
-bool cfm_get_opup(const struct cfm *);
+int cfm_get_opup(const struct cfm *);
 void cfm_get_remote_mpids(const struct cfm *, const uint64_t **rmps,
                           size_t *n_rmps);
 const char *cfm_fault_reason_to_str(int fault);
index 30cc31d..38f1a4f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -118,6 +118,20 @@ cls_rule_set_reg_masked(struct cls_rule *rule, unsigned int reg_idx,
     rule->flow.regs[reg_idx] = value & mask;
 }
 
+void
+cls_rule_set_metadata(struct cls_rule *rule, ovs_be64 metadata)
+{
+    cls_rule_set_metadata_masked(rule, metadata, htonll(UINT64_MAX));
+}
+
+void
+cls_rule_set_metadata_masked(struct cls_rule *rule, ovs_be64 metadata,
+                             ovs_be64 mask)
+{
+    rule->wc.metadata_mask = mask;
+    rule->flow.metadata = metadata & mask;
+}
+
 void
 cls_rule_set_tun_id(struct cls_rule *rule, ovs_be64 tun_id)
 {
@@ -146,38 +160,72 @@ cls_rule_set_dl_type(struct cls_rule *rule, ovs_be16 dl_type)
     rule->flow.dl_type = dl_type;
 }
 
+/* Modifies 'value_src' so that the Ethernet address must match
+ * 'value_dst' exactly. 'mask_dst' is set to all 1s */
+static void
+cls_rule_set_eth(const uint8_t value_src[ETH_ADDR_LEN],
+                 uint8_t value_dst[ETH_ADDR_LEN],
+                 uint8_t mask_dst[ETH_ADDR_LEN])
+{
+    memcpy(value_dst, value_src, ETH_ADDR_LEN);
+    memset(mask_dst, 0xff, ETH_ADDR_LEN);
+}
+
+/* Modifies 'value_src' so that the Ethernet address must match
+ * 'value_src' after each byte is ANDed with the appropriate byte in
+ * 'mask_src'. 'mask_dst' is set to 'mask_src' */
+static void
+cls_rule_set_eth_masked(const uint8_t value_src[ETH_ADDR_LEN],
+                        const uint8_t mask_src[ETH_ADDR_LEN],
+                        uint8_t value_dst[ETH_ADDR_LEN],
+                        uint8_t mask_dst[ETH_ADDR_LEN])
+{
+    size_t i;
+
+    for (i = 0; i < ETH_ADDR_LEN; i++) {
+        value_dst[i] = value_src[i] & mask_src[i];
+        mask_dst[i] = mask_src[i];
+    }
+}
+
+/* Modifies 'rule' so that the source Ethernet address
+ * must match 'dl_src' exactly. */
 void
 cls_rule_set_dl_src(struct cls_rule *rule, const uint8_t dl_src[ETH_ADDR_LEN])
 {
-    rule->wc.wildcards &= ~FWW_DL_SRC;
-    memcpy(rule->flow.dl_src, dl_src, ETH_ADDR_LEN);
+    cls_rule_set_eth(dl_src, rule->flow.dl_src, rule->wc.dl_src_mask);
+}
+
+/* Modifies 'rule' so that the source Ethernet address
+ * must match 'dl_src' after each byte is ANDed with
+ * the appropriate byte in 'mask'. */
+void
+cls_rule_set_dl_src_masked(struct cls_rule *rule,
+                           const uint8_t dl_src[ETH_ADDR_LEN],
+                           const uint8_t mask[ETH_ADDR_LEN])
+{
+    cls_rule_set_eth_masked(dl_src, mask,
+                            rule->flow.dl_src, rule->wc.dl_src_mask);
 }
 
-/* Modifies 'rule' so that the Ethernet address must match 'dl_dst' exactly. */
+/* Modifies 'rule' so that the destination Ethernet address
+ * must match 'dl_dst' exactly. */
 void
 cls_rule_set_dl_dst(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN])
 {
-    rule->wc.wildcards &= ~(FWW_DL_DST | FWW_ETH_MCAST);
-    memcpy(rule->flow.dl_dst, dl_dst, ETH_ADDR_LEN);
+    cls_rule_set_eth(dl_dst, rule->flow.dl_dst, rule->wc.dl_dst_mask);
 }
 
-/* Modifies 'rule' so that the Ethernet address must match 'dl_dst' after each
- * byte is ANDed with the appropriate byte in 'mask'.
- *
- * This function will assert-fail if 'mask' is invalid.  Only 'mask' values
- * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */
+/* Modifies 'rule' so that the destination Ethernet address
+ * must match 'dl_src' after each byte is ANDed with
+ * the appropriate byte in 'mask'. */
 void
 cls_rule_set_dl_dst_masked(struct cls_rule *rule,
                            const uint8_t dl_dst[ETH_ADDR_LEN],
                            const uint8_t mask[ETH_ADDR_LEN])
 {
-    flow_wildcards_t *wc = &rule->wc.wildcards;
-    size_t i;
-
-    *wc = flow_wildcards_set_dl_dst_mask(*wc, mask);
-    for (i = 0; i < ETH_ADDR_LEN; i++) {
-        rule->flow.dl_dst[i] = dl_dst[i] & mask[i];
-    }
+    cls_rule_set_eth_masked(dl_dst, mask,
+                            rule->flow.dl_dst, rule->wc.dl_dst_mask);
 }
 
 void
@@ -218,14 +266,39 @@ cls_rule_set_any_vid(struct cls_rule *rule)
 void
 cls_rule_set_dl_vlan(struct cls_rule *rule, ovs_be16 dl_vlan)
 {
-    flow_set_vlan_vid(&rule->flow, dl_vlan);
-    if (dl_vlan == htons(OFP_VLAN_NONE)) {
+    flow_set_dl_vlan(&rule->flow, dl_vlan);
+    if (dl_vlan == htons(OFP10_VLAN_NONE)) {
         rule->wc.vlan_tci_mask = htons(UINT16_MAX);
     } else {
         rule->wc.vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
     }
 }
 
+/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI). */
+void
+cls_rule_set_vlan_vid(struct cls_rule *rule, ovs_be16 vid)
+{
+    cls_rule_set_vlan_vid_masked(rule, vid, htons(VLAN_VID_MASK | VLAN_CFI));
+}
+
+
+/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI), with the corresponding 'mask'. */
+void
+cls_rule_set_vlan_vid_masked(struct cls_rule *rule,
+                             ovs_be16 vid, ovs_be16 mask)
+{
+    ovs_be16 pcp_mask = htons(VLAN_PCP_MASK);
+    ovs_be16 vid_mask = htons(VLAN_VID_MASK | VLAN_CFI);
+
+    mask &= vid_mask;
+    flow_set_vlan_vid(&rule->flow, vid & mask);
+    rule->wc.vlan_tci_mask = mask | (rule->wc.vlan_tci_mask & pcp_mask);
+}
+
 /* Modifies 'rule' so that the VLAN PCP is wildcarded.  If the VID is already
  * wildcarded, then 'rule' will match a packet regardless of whether it has an
  * 802.1Q header or not. */
@@ -364,15 +437,31 @@ cls_rule_set_icmp_code(struct cls_rule *rule, uint8_t icmp_code)
 void
 cls_rule_set_arp_sha(struct cls_rule *rule, const uint8_t sha[ETH_ADDR_LEN])
 {
-    rule->wc.wildcards &= ~FWW_ARP_SHA;
-    memcpy(rule->flow.arp_sha, sha, ETH_ADDR_LEN);
+    cls_rule_set_eth(sha, rule->flow.arp_sha, rule->wc.arp_sha_mask);
+}
+
+void
+cls_rule_set_arp_sha_masked(struct cls_rule *rule,
+                           const uint8_t arp_sha[ETH_ADDR_LEN],
+                           const uint8_t mask[ETH_ADDR_LEN])
+{
+    cls_rule_set_eth_masked(arp_sha, mask,
+                            rule->flow.arp_sha, rule->wc.arp_sha_mask);
 }
 
 void
 cls_rule_set_arp_tha(struct cls_rule *rule, const uint8_t tha[ETH_ADDR_LEN])
 {
-    rule->wc.wildcards &= ~FWW_ARP_THA;
-    memcpy(rule->flow.arp_tha, tha, ETH_ADDR_LEN);
+    cls_rule_set_eth(tha, rule->flow.arp_tha, rule->wc.arp_tha_mask);
+}
+
+void
+cls_rule_set_arp_tha_masked(struct cls_rule *rule,
+                           const uint8_t arp_tha[ETH_ADDR_LEN],
+                           const uint8_t mask[ETH_ADDR_LEN])
+{
+    cls_rule_set_eth_masked(arp_tha, mask,
+                            rule->flow.arp_tha, rule->wc.arp_tha_mask);
 }
 
 void
@@ -408,8 +497,15 @@ cls_rule_set_ipv6_dst_masked(struct cls_rule *rule, const struct in6_addr *dst,
 void
 cls_rule_set_ipv6_label(struct cls_rule *rule, ovs_be32 ipv6_label)
 {
-    rule->wc.wildcards &= ~FWW_IPV6_LABEL;
-    rule->flow.ipv6_label = ipv6_label;
+    cls_rule_set_ipv6_label_masked(rule, ipv6_label, htonl(UINT32_MAX));
+}
+
+void
+cls_rule_set_ipv6_label_masked(struct cls_rule *rule, ovs_be32 ipv6_label,
+                               ovs_be32 mask)
+{
+    rule->flow.ipv6_label = ipv6_label & mask;
+    rule->wc.ipv6_label_mask = mask;
 }
 
 void
@@ -448,6 +544,17 @@ cls_rule_hash(const struct cls_rule *rule, uint32_t basis)
     return hash_int(rule->priority, h1);
 }
 
+static void
+format_eth_masked(struct ds *s, const char *name, const uint8_t eth[6],
+                  const uint8_t mask[6])
+{
+    if (!eth_addr_is_zero(mask)) {
+        ds_put_format(s, "%s=", name);
+        eth_format_masked(eth, mask, s);
+        ds_put_char(s, ',');
+    }
+}
+
 static void
 format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip,
                   ovs_be32 netmask)
@@ -500,7 +607,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
 
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     if (rule->priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%d,", rule->priority);
@@ -570,6 +677,17 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
                       ntohll(f->tun_id), ntohll(wc->tun_id_mask));
         break;
     }
+    switch (wc->metadata_mask) {
+    case 0:
+        break;
+    case CONSTANT_HTONLL(UINT64_MAX):
+        ds_put_format(s, "metadata=%#"PRIx64",", ntohll(f->metadata));
+        break;
+    default:
+        ds_put_format(s, "metadata=%#"PRIx64"/%#"PRIx64",",
+                      ntohll(f->metadata), ntohll(wc->metadata_mask));
+        break;
+    }
     if (!(w & FWW_IN_PORT)) {
         ds_put_format(s, "in_port=%"PRIu16",", f->in_port);
     }
@@ -597,32 +715,23 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
                           ntohs(f->vlan_tci), ntohs(wc->vlan_tci_mask));
         }
     }
-    if (!(w & FWW_DL_SRC)) {
-        ds_put_format(s, "dl_src="ETH_ADDR_FMT",", ETH_ADDR_ARGS(f->dl_src));
-    }
-    switch (w & (FWW_DL_DST | FWW_ETH_MCAST)) {
-    case 0:
-        ds_put_format(s, "dl_dst="ETH_ADDR_FMT",", ETH_ADDR_ARGS(f->dl_dst));
-        break;
-    case FWW_DL_DST:
-        ds_put_format(s, "dl_dst="ETH_ADDR_FMT"/01:00:00:00:00:00,",
-                      ETH_ADDR_ARGS(f->dl_dst));
-        break;
-    case FWW_ETH_MCAST:
-        ds_put_format(s, "dl_dst="ETH_ADDR_FMT"/fe:ff:ff:ff:ff:ff,",
-                      ETH_ADDR_ARGS(f->dl_dst));
-        break;
-    case FWW_DL_DST | FWW_ETH_MCAST:
-        break;
-    }
+    format_eth_masked(s, "dl_src", f->dl_src, wc->dl_src_mask);
+    format_eth_masked(s, "dl_dst", f->dl_dst, wc->dl_dst_mask);
     if (!skip_type && !(w & FWW_DL_TYPE)) {
         ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type));
     }
     if (f->dl_type == htons(ETH_TYPE_IPV6)) {
         format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->ipv6_src_mask);
         format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->ipv6_dst_mask);
-        if (!(w & FWW_IPV6_LABEL)) {
-            ds_put_format(s, "ipv6_label=0x%05"PRIx32",", ntohl(f->ipv6_label));
+        if (wc->ipv6_label_mask) {
+            if (wc->ipv6_label_mask == htonl(UINT32_MAX)) {
+                ds_put_format(s, "ipv6_label=0x%05"PRIx32",",
+                              ntohl(f->ipv6_label));
+            } else {
+                ds_put_format(s, "ipv6_label=0x%05"PRIx32"/0x%05"PRIx32",",
+                              ntohl(f->ipv6_label),
+                              ntohl(wc->ipv6_label_mask));
+            }
         }
     } else {
         format_ip_netmask(s, "nw_src", f->nw_src, wc->nw_src_mask);
@@ -636,14 +745,8 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
         }
     }
     if (f->dl_type == htons(ETH_TYPE_ARP)) {
-        if (!(w & FWW_ARP_SHA)) {
-            ds_put_format(s, "arp_sha="ETH_ADDR_FMT",",
-                    ETH_ADDR_ARGS(f->arp_sha));
-        }
-        if (!(w & FWW_ARP_THA)) {
-            ds_put_format(s, "arp_tha="ETH_ADDR_FMT",",
-                    ETH_ADDR_ARGS(f->arp_tha));
-        }
+        format_eth_masked(s, "arp_sha", f->arp_sha, wc->arp_sha_mask);
+        format_eth_masked(s, "arp_tha", f->arp_tha, wc->arp_tha_mask);
     }
     if (!(w & FWW_NW_DSCP)) {
         ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos & IP_DSCP_MASK);
@@ -680,14 +783,8 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
         format_be16_masked(s, "icmp_code", f->tp_dst, wc->tp_dst_mask);
         format_ipv6_netmask(s, "nd_target", &f->nd_target,
                             &wc->nd_target_mask);
-        if (!(w & FWW_ARP_SHA)) {
-            ds_put_format(s, "nd_sll="ETH_ADDR_FMT",",
-                    ETH_ADDR_ARGS(f->arp_sha));
-        }
-        if (!(w & FWW_ARP_THA)) {
-            ds_put_format(s, "nd_tll="ETH_ADDR_FMT",",
-                    ETH_ADDR_ARGS(f->arp_tha));
-        }
+        format_eth_masked(s, "nd_sll", f->arp_sha, wc->arp_sha_mask);
+        format_eth_masked(s, "nd_tll", f->arp_tha, wc->arp_tha_mask);
    } else {
         format_be16_masked(s, "tp_src", f->tp_src, wc->tp_src_mask);
         format_be16_masked(s, "tp_dst", f->tp_dst, wc->tp_dst_mask);
@@ -900,6 +997,47 @@ classifier_rule_overlaps(const struct classifier *cls,
 
     return false;
 }
+
+/* Returns true if 'rule' exactly matches 'criteria' or if 'rule' is more
+ * specific than 'criteria'.  That is, 'rule' matches 'criteria' and this
+ * function returns true if, for every field:
+ *
+ *   - 'criteria' and 'rule' specify the same (non-wildcarded) value for the
+ *     field, or
+ *
+ *   - 'criteria' wildcards the field,
+ *
+ * Conversely, 'rule' does not match 'criteria' and this function returns false
+ * if, for at least one field:
+ *
+ *   - 'criteria' and 'rule' specify different values for the field, or
+ *
+ *   - 'criteria' specifies a value for the field but 'rule' wildcards it.
+ *
+ * Equivalently, the truth table for whether a field matches is:
+ *
+ *                                     rule
+ *
+ *                   c         wildcard    exact
+ *                   r        +---------+---------+
+ *                   i   wild |   yes   |   yes   |
+ *                   t   card |         |         |
+ *                   e        +---------+---------+
+ *                   r  exact |    no   |if values|
+ *                   i        |         |are equal|
+ *                   a        +---------+---------+
+ *
+ * This is the matching rule used by OpenFlow 1.0 non-strict OFPT_FLOW_MOD
+ * commands and by OpenFlow 1.0 aggregate and flow stats.
+ *
+ * Ignores rule->priority and criteria->priority. */
+bool
+cls_rule_is_loose_match(const struct cls_rule *rule,
+                        const struct cls_rule *criteria)
+{
+    return (!flow_wildcards_has_extra(&rule->wc, &criteria->wc)
+            && flow_equal_except(&rule->flow, &criteria->flow, &criteria->wc));
+}
 \f
 /* Iteration. */
 
@@ -925,40 +1063,14 @@ search_table(const struct cls_table *table, const struct cls_rule *target)
     return NULL;
 }
 
-/* Initializes 'cursor' for iterating through 'cls' rules that exactly match
- * 'target' or are more specific than 'target'.  That is, a given 'rule'
- * matches 'target' if, for every field:
+/* Initializes 'cursor' for iterating through rules in 'cls':
  *
- *   - 'target' and 'rule' specify the same (non-wildcarded) value for the
- *     field, or
- *
- *   - 'target' wildcards the field,
- *
- * but not if:
- *
- *   - 'target' and 'rule' specify different values for the field, or
- *
- *   - 'target' specifies a value for the field but 'rule' wildcards it.
- *
- * Equivalently, the truth table for whether a field matches is:
- *
- *                                     rule
- *
- *                             wildcard    exact
- *                            +---------+---------+
- *                   t   wild |   yes   |   yes   |
- *                   a   card |         |         |
- *                   r        +---------+---------+
- *                   g  exact |    no   |if values|
- *                   e        |         |are equal|
- *                   t        +---------+---------+
- *
- * This is the matching rule used by OpenFlow 1.0 non-strict OFPT_FLOW_MOD
- * commands and by OpenFlow 1.0 aggregate and flow stats.
+ *     - If 'target' is null, the cursor will visit every rule in 'cls'.
  *
- * Ignores target->priority.
+ *     - If 'target' is nonnull, the cursor will visit each 'rule' in 'cls'
+ *       such that cls_rule_is_loose_match(rule, target) returns true.
  *
- * 'target' may be NULL to iterate over every rule in 'cls'. */
+ * Ignores target->priority. */
 void
 cls_cursor_init(struct cls_cursor *cursor, const struct classifier *cls,
                 const struct cls_rule *target)
@@ -1179,7 +1291,7 @@ flow_equal_except(const struct flow *a, const struct flow *b,
     const flow_wildcards_t wc = wildcards->wildcards;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
@@ -1188,6 +1300,7 @@ flow_equal_except(const struct flow *a, const struct flow *b,
     }
 
     return (!((a->tun_id ^ b->tun_id) & wildcards->tun_id_mask)
+            && !((a->metadata ^ b->metadata) & wildcards->metadata_mask)
             && !((a->nw_src ^ b->nw_src) & wildcards->nw_src_mask)
             && !((a->nw_dst ^ b->nw_dst) & wildcards->nw_dst_mask)
             && (wc & FWW_IN_PORT || a->in_port == b->in_port)
@@ -1195,24 +1308,20 @@ flow_equal_except(const struct flow *a, const struct flow *b,
             && (wc & FWW_DL_TYPE || a->dl_type == b->dl_type)
             && !((a->tp_src ^ b->tp_src) & wildcards->tp_src_mask)
             && !((a->tp_dst ^ b->tp_dst) & wildcards->tp_dst_mask)
-            && (wc & FWW_DL_SRC || eth_addr_equals(a->dl_src, b->dl_src))
-            && (wc & FWW_DL_DST
-                || (!((a->dl_dst[0] ^ b->dl_dst[0]) & 0xfe)
-                    && a->dl_dst[1] == b->dl_dst[1]
-                    && a->dl_dst[2] == b->dl_dst[2]
-                    && a->dl_dst[3] == b->dl_dst[3]
-                    && a->dl_dst[4] == b->dl_dst[4]
-                    && a->dl_dst[5] == b->dl_dst[5]))
-            && (wc & FWW_ETH_MCAST
-                || !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
+            && eth_addr_equal_except(a->dl_src, b->dl_src,
+                                     wildcards->dl_src_mask)
+            && eth_addr_equal_except(a->dl_dst, b->dl_dst,
+                                     wildcards->dl_dst_mask)
             && (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
             && (wc & FWW_NW_TTL || a->nw_ttl == b->nw_ttl)
             && (wc & FWW_NW_DSCP || !((a->nw_tos ^ b->nw_tos) & IP_DSCP_MASK))
             && (wc & FWW_NW_ECN || !((a->nw_tos ^ b->nw_tos) & IP_ECN_MASK))
             && !((a->nw_frag ^ b->nw_frag) & wildcards->nw_frag_mask)
-            && (wc & FWW_ARP_SHA || eth_addr_equals(a->arp_sha, b->arp_sha))
-            && (wc & FWW_ARP_THA || eth_addr_equals(a->arp_tha, b->arp_tha))
-            && (wc & FWW_IPV6_LABEL || a->ipv6_label == b->ipv6_label)
+            && eth_addr_equal_except(a->arp_sha, b->arp_sha,
+                                     wildcards->arp_sha_mask)
+            && eth_addr_equal_except(a->arp_tha, b->arp_tha,
+                                     wildcards->arp_tha_mask)
+            && !((a->ipv6_label ^ b->ipv6_label) & wildcards->ipv6_label_mask)
             && ipv6_equal_except(&a->ipv6_src, &b->ipv6_src,
                     &wildcards->ipv6_src_mask)
             && ipv6_equal_except(&a->ipv6_dst, &b->ipv6_dst,
index 48eb596..9322345 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -87,15 +87,23 @@ void cls_rule_init_catchall(struct cls_rule *, unsigned int priority);
 
 void cls_rule_zero_wildcarded_fields(struct cls_rule *);
 
+bool cls_rule_is_loose_match(const struct cls_rule *rule,
+                             const struct cls_rule *criteria);
+
 void cls_rule_set_reg(struct cls_rule *, unsigned int reg_idx, uint32_t value);
 void cls_rule_set_reg_masked(struct cls_rule *, unsigned int reg_idx,
                              uint32_t value, uint32_t mask);
+void cls_rule_set_metadata(struct cls_rule *, ovs_be64 metadata);
+void cls_rule_set_metadata_masked(struct cls_rule *, ovs_be64 metadata,
+                                  ovs_be64 mask);
 void cls_rule_set_tun_id(struct cls_rule *, ovs_be64 tun_id);
 void cls_rule_set_tun_id_masked(struct cls_rule *,
                                 ovs_be64 tun_id, ovs_be64 mask);
 void cls_rule_set_in_port(struct cls_rule *, uint16_t ofp_port);
 void cls_rule_set_dl_type(struct cls_rule *, ovs_be16);
 void cls_rule_set_dl_src(struct cls_rule *, const uint8_t[6]);
+void cls_rule_set_dl_src_masked(struct cls_rule *, const uint8_t dl_src[6],
+                                const uint8_t mask[6]);
 void cls_rule_set_dl_dst(struct cls_rule *, const uint8_t[6]);
 void cls_rule_set_dl_dst_masked(struct cls_rule *, const uint8_t dl_dst[6],
                                 const uint8_t mask[6]);
@@ -104,6 +112,9 @@ void cls_rule_set_dl_tci_masked(struct cls_rule *,
                                 ovs_be16 tci, ovs_be16 mask);
 void cls_rule_set_any_vid(struct cls_rule *);
 void cls_rule_set_dl_vlan(struct cls_rule *, ovs_be16);
+void cls_rule_set_vlan_vid(struct cls_rule *, ovs_be16);
+void cls_rule_set_vlan_vid_masked(struct cls_rule *,
+                                  ovs_be16 vid, ovs_be16 mask);
 void cls_rule_set_any_pcp(struct cls_rule *);
 void cls_rule_set_dl_vlan_pcp(struct cls_rule *, uint8_t);
 void cls_rule_set_tp_src(struct cls_rule *, ovs_be16);
@@ -126,7 +137,11 @@ void cls_rule_set_nw_frag_masked(struct cls_rule *,
 void cls_rule_set_icmp_type(struct cls_rule *, uint8_t);
 void cls_rule_set_icmp_code(struct cls_rule *, uint8_t);
 void cls_rule_set_arp_sha(struct cls_rule *, const uint8_t[6]);
+void cls_rule_set_arp_sha_masked(struct cls_rule *, const uint8_t[6],
+                                 const uint8_t [6]);
 void cls_rule_set_arp_tha(struct cls_rule *, const uint8_t[6]);
+void cls_rule_set_arp_tha_masked(struct cls_rule *, const uint8_t[6],
+                                 const uint8_t [6]);
 void cls_rule_set_ipv6_src(struct cls_rule *, const struct in6_addr *);
 void cls_rule_set_ipv6_src_masked(struct cls_rule *, const struct in6_addr *,
                                   const struct in6_addr *);
@@ -134,6 +149,7 @@ void cls_rule_set_ipv6_dst(struct cls_rule *, const struct in6_addr *);
 void cls_rule_set_ipv6_dst_masked(struct cls_rule *, const struct in6_addr *,
                                   const struct in6_addr *);
 void cls_rule_set_ipv6_label(struct cls_rule *, ovs_be32);
+void cls_rule_set_ipv6_label_masked(struct cls_rule *, ovs_be32, ovs_be32);
 void cls_rule_set_nd_target(struct cls_rule *, const struct in6_addr *);
 void cls_rule_set_nd_target_masked(struct cls_rule *, const struct in6_addr *,
                                    const struct in6_addr *);
index 9adf47f..ca443a3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 1c88003..803f433 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * 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.
index 0d7ee9f..27612a7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #define SENTINEL(N)
 #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
+ * when a smaller type would suffice.
+ *
+ * In GNU C, "enum __attribute__((packed)) name { ... }" defines 'name' as an
+ * enum compatible with a type that is no bigger than necessary.  This is the
+ * intended use of OVS_PACKED_ENUM.
+ *
+ * OVS_PACKED_ENUM is intended for use only as a space optimization, since it
+ * only works with GCC.  That means that it must not be used in wire protocols
+ * or otherwise exposed outside of a single process. */
+#if __GNUC__ && !__CHECKER__
+#define OVS_PACKED_ENUM __attribute__((__packed__))
+#else
+#define OVS_PACKED_ENUM
+#endif
+
 #endif /* compiler.h */
index ee27af0..f152474 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 0f389bf..968c489 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 47e803e..98a83de 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index e5f35e9..12402d7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 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.
index 8ef5491..84ed614 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,7 +39,8 @@
 VLOG_DEFINE_THIS_MODULE(daemon);
 
 /* --detach: Should we run in the background? */
-static bool detach;
+static bool detach;             /* Was --detach specified? */
+static bool detached;           /* Have we already detached? */
 
 /* --pidfile: Name of pidfile (null if none). */
 static char *pidfile;
@@ -245,6 +246,43 @@ daemonize(void)
     daemonize_complete();
 }
 
+/* Calls fork() and on success returns its return value.  On failure, logs an
+ * error and exits unsuccessfully.
+ *
+ * Post-fork, but before returning, this function calls a few other functions
+ * that are generally useful if the child isn't planning to exec a new
+ * process. */
+pid_t
+fork_and_clean_up(void)
+{
+    pid_t pid;
+
+    pid = fork();
+    if (pid > 0) {
+        /* Running in parent process. */
+        fatal_signal_fork();
+    } else if (!pid) {
+        /* Running in child process. */
+        time_postfork();
+        lockfile_postfork();
+    } else {
+        VLOG_FATAL("fork failed (%s)", strerror(errno));
+    }
+
+    return pid;
+}
+
+/* Forks, then:
+ *
+ *   - In the parent, waits for the child to signal that it has completed its
+ *     startup sequence.  Then stores -1 in '*fdp' and returns the child's pid.
+ *
+ *   - In the child, stores a fd in '*fdp' and returns 0.  The caller should
+ *     pass the fd to fork_notify_startup() after it finishes its startup
+ *     sequence.
+ *
+ * If something goes wrong with the fork, logs a critical error and aborts the
+ * process. */
 static pid_t
 fork_and_wait_for_startup(int *fdp)
 {
@@ -253,14 +291,13 @@ fork_and_wait_for_startup(int *fdp)
 
     xpipe(fds);
 
-    pid = fork();
+    pid = fork_and_clean_up();
     if (pid > 0) {
         /* Running in parent process. */
         size_t bytes_read;
         char c;
 
         close(fds[1]);
-        fatal_signal_fork();
         if (read_fully(fds[0], &c, 1, &bytes_read) != 0) {
             int retval;
             int status;
@@ -290,11 +327,7 @@ fork_and_wait_for_startup(int *fdp)
     } else if (!pid) {
         /* Running in child process. */
         close(fds[0]);
-        time_postfork();
-        lockfile_postfork();
         *fdp = fds[1];
-    } else {
-        VLOG_FATAL("fork failed (%s)", strerror(errno));
     }
 
     return pid;
@@ -340,13 +373,11 @@ static void
 monitor_daemon(pid_t daemon_pid)
 {
     /* XXX Should log daemon's stderr output at startup time. */
-    const char *saved_program_name;
     time_t last_restart;
     char *status_msg;
     int crashes;
 
-    saved_program_name = program_name;
-    program_name = xasprintf("monitor(%s)", program_name);
+    subprogram_name = "monitor";
     status_msg = xstrdup("healthy");
     last_restart = TIME_MIN;
     crashes = 0;
@@ -355,7 +386,7 @@ monitor_daemon(pid_t daemon_pid)
         int status;
 
         proctitle_set("%s: monitoring pid %lu (%s)",
-                      saved_program_name, (unsigned long int) daemon_pid,
+                      program_name, (unsigned long int) daemon_pid,
                       status_msg);
 
         do {
@@ -417,8 +448,7 @@ monitor_daemon(pid_t daemon_pid)
 
     /* Running in new daemon process. */
     proctitle_restore();
-    free((char *) program_name);
-    program_name = saved_program_name;
+    subprogram_name = "";
 }
 
 /* Close standard file descriptors (except any that the client has requested we
@@ -484,22 +514,38 @@ daemonize_start(void)
 }
 
 /* If daemonization is configured, then this function notifies the parent
- * process that the child process has completed startup successfully.
+ * process that the child process has completed startup successfully.  It also
+ * call daemonize_post_detach().
  *
  * Calling this function more than once has no additional effect. */
 void
 daemonize_complete(void)
 {
-    fork_notify_startup(daemonize_fd);
-    daemonize_fd = -1;
+    if (!detached) {
+        detached = true;
 
+        fork_notify_startup(daemonize_fd);
+        daemonize_fd = -1;
+        daemonize_post_detach();
+    }
+}
+
+/* If daemonization is configured, then this function does traditional Unix
+ * daemonization behavior: join a new session, chdir to the root (if not
+ * disabled), and close the standard file descriptors.
+ *
+ * It only makes sense to call this function as part of an implementation of a
+ * special daemon subprocess.  A normal daemon should just call
+ * daemonize_complete(). */
+void
+daemonize_post_detach(void)
+{
     if (detach) {
         setsid();
         if (chdir_) {
             ignore(chdir("/"));
         }
         close_standard_fds();
-        detach = false;
     }
 }
 
index 1b4f988..8cbcfaf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -73,4 +73,7 @@ void daemon_usage(void);
 pid_t read_pidfile(const char *name);
 pid_t read_pidfile_if_exists(const char *name);
 
+pid_t fork_and_clean_up(void);
+void daemonize_post_detach(void);
+
 #endif /* daemon.h */
index 8858cd5..6f97298 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 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.
index 38cede0..54f9c5f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Nicira Networks.
+ * Copyright (c) 2008 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 5b6b874..658a74b 100644 (file)
@@ -1,6 +1,6 @@
 #line 2 "@srcdir@/lib/dirs.c.in"
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
 #include <config.h>
 #include "dirs.h"
 #include <stdlib.h>
+#include "util.h"
 
 struct directory {
     const char *value;          /* Actual value; NULL if not yet determined. */
@@ -65,6 +66,23 @@ ovs_logdir(void)
     return get_dir(&d);
 }
 
+const char *
+ovs_dbdir(void)
+{
+    static const char *dbdir;
+    if (!dbdir) {
+        dbdir = getenv("OVS_DBDIR");
+        if (!dbdir || !dbdir[0]) {
+            char *sysconfdir = getenv("OVS_SYSCONFDIR");
+
+            dbdir = (sysconfdir
+                     ? xasprintf("%s/openvswitch", sysconfdir)
+                     : @DBDIR@);
+        }
+    }
+    return dbdir;
+}
+
 const char *
 ovs_bindir(void)
 {
index 57107ff..811a51f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ const char *ovs_sysconfdir(void); /* /usr/local/etc */
 const char *ovs_pkgdatadir(void); /* /usr/local/share/openvswitch */
 const char *ovs_rundir(void);     /* /usr/local/var/run/openvswitch */
 const char *ovs_logdir(void);     /* /usr/local/var/log/openvswitch */
+const char *ovs_dbdir(void);      /* /usr/local/etc/openvswitch */
 const char *ovs_bindir(void);     /* /usr/local/bin */
 
 #endif /* dirs.h */
index 932b36f..d9c50cc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -54,6 +54,7 @@
 #include "random.h"
 #include "shash.h"
 #include "sset.h"
+#include "timeval.h"
 #include "unaligned.h"
 #include "util.h"
 #include "vlog.h"
 VLOG_DEFINE_THIS_MODULE(dpif_linux);
 enum { MAX_PORTS = USHRT_MAX };
 
-enum { N_UPCALL_SOCKS = 16 };
-BUILD_ASSERT_DECL(IS_POW2(N_UPCALL_SOCKS));
-BUILD_ASSERT_DECL(N_UPCALL_SOCKS <= 32); /* We use a 32-bit word as a mask. */
+enum { N_CHANNELS = 17 };
+BUILD_ASSERT_DECL(IS_POW2(N_CHANNELS - 1));
+BUILD_ASSERT_DECL(N_CHANNELS > 1);
+BUILD_ASSERT_DECL(N_CHANNELS <= 32); /* We use a 32-bit word as a mask. */
 
 /* This ethtool flag was introduced in Linux 2.6.24, so it might be
  * missing if we have old headers. */
@@ -129,15 +131,68 @@ static int dpif_linux_flow_transact(struct dpif_linux_flow *request,
 static void dpif_linux_flow_get_stats(const struct dpif_linux_flow *,
                                       struct dpif_flow_stats *);
 
+/* Packet drop monitoring.
+ *
+ * When kernel-to-user Netlink buffers overflow, the kernel notifies us that
+ * one or more packets were dropped, but it doesn't tell us anything about
+ * those packets.  However, the administrator really wants to know.  So we do
+ * the next best thing, and keep track of the top sources of packets received
+ * on each kernel-to-user channel, since the top sources are those that will
+ * cause the buffers to overflow.
+ *
+ * We use a variation on the "Space-Saving" algorithm in Metwally et al.,
+ * "Efficient Computation of Frequent and Top-k Elements in Data Streams", ACM
+ * Transactions on Database Systems 31:3 (2006).  This algorithm yields
+ * perfectly accurate results when the data stream's unique values (in this
+ * case, port numbers) fit into our data structure, and degrades gracefully
+ * even for challenging distributions (e.g. Zipf).
+ *
+ * Our implementation is very simple, without any of the special flourishes
+ * described in the paper.  It avoids the need to use a hash for lookup by
+ * keeping the constant factor (N_SKETCHES) very small.  The error calculations
+ * in the paper make it sound like the results should still be satisfactory.
+ *
+ * "space-saving" and "Metwally" seem like awkward names for data structures,
+ * so we call this a "sketch" even though technically that's a different sort
+ * of summary structure.
+ */
+
+/* One of N_SKETCHES counting elements per channel in the Metwally
+ * "space-saving" algorithm. */
+enum { N_SKETCHES = 8 };        /* Number of elements per channel. */
+struct dpif_sketch {
+    uint32_t port_no;           /* Port number. */
+    unsigned int hits;          /* Number of hits. */
+    unsigned int error;         /* Upper bound on error in 'hits'. */
+};
+
+/* One of N_CHANNELS channels per dpif between the kernel and userspace. */
+struct dpif_channel {
+    struct nl_sock *sock;       /* Netlink socket. */
+    struct dpif_sketch sketches[N_SKETCHES]; /* From max to min 'hits'. */
+    long long int last_poll;    /* Last time this channel was polled. */
+};
+
+static void update_sketch(struct dpif_channel *, uint32_t port_no);
+static void scale_sketches(struct dpif *);
+static void report_loss(struct dpif *, struct dpif_channel *);
+
+/* Interval, in milliseconds, at which to scale down the sketch values by a
+ * factor of 2.  The Metwally algorithm doesn't do this, which makes sense in
+ * the context it assumes, but in our situation we ought to weight recent data
+ * more heavily than old data, so in my opinion this is reasonable. */
+#define SCALE_INTERVAL (60 * 1000)
+
 /* Datapath interface for the openvswitch Linux kernel module. */
 struct dpif_linux {
     struct dpif dpif;
     int dp_ifindex;
 
     /* Upcall messages. */
-    struct nl_sock *upcall_socks[N_UPCALL_SOCKS];
+    struct dpif_channel channels[N_CHANNELS];
     uint32_t ready_mask;        /* 1-bit for each sock with unread messages. */
-    int epoll_fd;               /* epoll fd that includes the upcall socks. */
+    int epoll_fd;               /* epoll fd that includes channel socks. */
+    long long int next_scale;   /* Next time to scale down the sketches. */
 
     /* Change notification. */
     struct sset changed_ports;  /* Ports that have changed. */
@@ -248,24 +303,27 @@ open_dpif(const struct dpif_linux_dp *dp, struct dpif **dpifp)
     dpif_init(&dpif->dpif, &dpif_linux_class, dp->name,
               dp->dp_ifindex, dp->dp_ifindex);
 
+    dpif->next_scale = LLONG_MAX;
+
     dpif->dp_ifindex = dp->dp_ifindex;
     sset_init(&dpif->changed_ports);
     *dpifp = &dpif->dpif;
 }
 
 static void
-destroy_upcall_socks(struct dpif_linux *dpif)
+destroy_channels(struct dpif_linux *dpif)
 {
-    int i;
+    struct dpif_channel *ch;
 
     if (dpif->epoll_fd >= 0) {
         close(dpif->epoll_fd);
         dpif->epoll_fd = -1;
     }
-    for (i = 0; i < N_UPCALL_SOCKS; i++) {
-        nl_sock_destroy(dpif->upcall_socks[i]);
-        dpif->upcall_socks[i] = NULL;
+    for (ch = dpif->channels; ch < &dpif->channels[N_CHANNELS]; ch++) {
+        nl_sock_destroy(ch->sock);
+        ch->sock = NULL;
     }
+    dpif->next_scale = LLONG_MAX;
 }
 
 static void
@@ -274,7 +332,7 @@ dpif_linux_close(struct dpif *dpif_)
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
 
     nln_notifier_destroy(dpif->port_notifier);
-    destroy_upcall_socks(dpif);
+    destroy_channels(dpif);
     sset_destroy(&dpif->changed_ports);
     free(dpif);
 }
@@ -292,8 +350,15 @@ dpif_linux_destroy(struct dpif *dpif_)
 }
 
 static void
-dpif_linux_run(struct dpif *dpif OVS_UNUSED)
+dpif_linux_run(struct dpif *dpif_)
 {
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+
+    if (time_msec() >= dpif->next_scale) {
+        dpif->next_scale = time_msec() + SCALE_INTERVAL;
+        scale_sketches(dpif_);
+    }
+
     if (nln) {
         nln_run(nln);
     }
@@ -359,11 +424,13 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
         netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false);
     }
 
-    /* Loop until we find a port that isn't used. */
+    /* Unless a specific port was requested, loop until we find a port
+     * that isn't used. */
     do {
         uint32_t upcall_pid;
 
-        request.port_no = ++dpif->alloc_port_no;
+        request.port_no = *port_nop != UINT16_MAX ? *port_nop
+                          : ++dpif->alloc_port_no;
         upcall_pid = dpif_linux_port_get_pid(dpif_, request.port_no);
         request.upcall_pid = &upcall_pid;
         error = dpif_linux_vport_transact(&request, &reply, &buf);
@@ -376,10 +443,13 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
             /* Older datapath has lower limit. */
             max_ports = dpif->alloc_port_no;
             dpif->alloc_port_no = 0;
+        } else if (error == EBUSY && *port_nop != UINT16_MAX) {
+            VLOG_INFO("%s: requested port %"PRIu16" is in use",
+                     dpif_name(dpif_), *port_nop);
         }
 
         ofpbuf_delete(buf);
-    } while ((i++ < max_ports)
+    } while ((*port_nop == UINT16_MAX) && (i++ < max_ports)
              && (error == EBUSY || error == EFBIG));
 
     return error;
@@ -460,8 +530,12 @@ dpif_linux_port_get_pid(const struct dpif *dpif_, uint16_t port_no)
     if (dpif->epoll_fd < 0) {
         return 0;
     } else {
-        int idx = port_no & (N_UPCALL_SOCKS - 1);
-        return nl_sock_pid(dpif->upcall_socks[idx]);
+        int idx;
+
+        idx = (port_no != UINT16_MAX
+               ? 1 + (port_no & (N_CHANNELS - 2))
+               : 0);
+        return nl_sock_pid(dpif->channels[idx].sock);
     }
 }
 
@@ -521,8 +595,8 @@ dpif_linux_port_dump_next(const struct dpif *dpif OVS_UNUSED, void *state_,
         return error;
     }
 
-    dpif_port->name = (char *) vport.name;
-    dpif_port->type = (char *) netdev_vport_get_netdev_type(&vport);
+    dpif_port->name = CONST_CAST(char *, vport.name);
+    dpif_port->type = CONST_CAST(char *, netdev_vport_get_netdev_type(&vport));
     dpif_port->port_no = vport.port_no;
     return 0;
 }
@@ -594,7 +668,7 @@ dpif_linux_flow_get(const struct dpif *dpif_,
             dpif_linux_flow_get_stats(&reply, stats);
         }
         if (actionsp) {
-            buf->data = (void *) reply.actions;
+            buf->data = CONST_CAST(struct nlattr *, reply.actions);
             buf->size = reply.actions_len;
             *actionsp = buf;
         } else {
@@ -899,24 +973,38 @@ dpif_linux_operate__(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops)
         switch (op->type) {
         case DPIF_OP_FLOW_PUT:
             put = &op->u.flow_put;
-            if (!op->error && put->stats) {
-                struct dpif_linux_flow reply;
-
-                op->error = dpif_linux_flow_from_ofpbuf(&reply, txn->reply);
+            if (put->stats) {
                 if (!op->error) {
-                    dpif_linux_flow_get_stats(&reply, put->stats);
+                    struct dpif_linux_flow reply;
+
+                    op->error = dpif_linux_flow_from_ofpbuf(&reply,
+                                                            txn->reply);
+                    if (!op->error) {
+                        dpif_linux_flow_get_stats(&reply, put->stats);
+                    }
+                }
+
+                if (op->error) {
+                    memset(put->stats, 0, sizeof *put->stats);
                 }
             }
             break;
 
         case DPIF_OP_FLOW_DEL:
             del = &op->u.flow_del;
-            if (!op->error && del->stats) {
-                struct dpif_linux_flow reply;
-
-                op->error = dpif_linux_flow_from_ofpbuf(&reply, txn->reply);
+            if (del->stats) {
                 if (!op->error) {
-                    dpif_linux_flow_get_stats(&reply, del->stats);
+                    struct dpif_linux_flow reply;
+
+                    op->error = dpif_linux_flow_from_ofpbuf(&reply,
+                                                            txn->reply);
+                    if (!op->error) {
+                        dpif_linux_flow_get_stats(&reply, del->stats);
+                    }
+                }
+
+                if (op->error) {
+                    memset(del->stats, 0, sizeof *del->stats);
                 }
             }
             break;
@@ -983,37 +1071,42 @@ dpif_linux_recv_set(struct dpif *dpif_, bool enable)
     }
 
     if (!enable) {
-        destroy_upcall_socks(dpif);
+        destroy_channels(dpif);
     } else {
-        int i;
+        struct dpif_channel *ch;
         int error;
 
-        dpif->epoll_fd = epoll_create(N_UPCALL_SOCKS);
+        dpif->epoll_fd = epoll_create(N_CHANNELS);
         if (dpif->epoll_fd < 0) {
             return errno;
         }
 
-        for (i = 0; i < N_UPCALL_SOCKS; i++) {
+        for (ch = dpif->channels; ch < &dpif->channels[N_CHANNELS]; ch++) {
+            int indx = ch - dpif->channels;
             struct epoll_event event;
 
-            error = nl_sock_create(NETLINK_GENERIC, &dpif->upcall_socks[i]);
+            error = nl_sock_create(NETLINK_GENERIC, &ch->sock);
             if (error) {
-                destroy_upcall_socks(dpif);
+                destroy_channels(dpif);
                 return error;
             }
 
             memset(&event, 0, sizeof event);
             event.events = EPOLLIN;
-            event.data.u32 = i;
-            if (epoll_ctl(dpif->epoll_fd, EPOLL_CTL_ADD,
-                          nl_sock_fd(dpif->upcall_socks[i]), &event) < 0) {
+            event.data.u32 = indx;
+            if (epoll_ctl(dpif->epoll_fd, EPOLL_CTL_ADD, nl_sock_fd(ch->sock),
+                          &event) < 0) {
                 error = errno;
-                destroy_upcall_socks(dpif);
+                destroy_channels(dpif);
                 return error;
             }
+
+            memset(ch->sketches, 0, sizeof ch->sketches);
+            ch->last_poll = LLONG_MIN;
         }
 
         dpif->ready_mask = 0;
+        dpif->next_scale = time_msec() + SCALE_INTERVAL;
     }
 
     set_upcall_pids(dpif_);
@@ -1076,9 +1169,11 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall *upcall,
     memset(upcall, 0, sizeof *upcall);
     upcall->type = type;
     upcall->packet = buf;
-    upcall->packet->data = (void *) nl_attr_get(a[OVS_PACKET_ATTR_PACKET]);
+    upcall->packet->data = CONST_CAST(struct nlattr *,
+                                      nl_attr_get(a[OVS_PACKET_ATTR_PACKET]));
     upcall->packet->size = nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]);
-    upcall->key = (void *) nl_attr_get(a[OVS_PACKET_ATTR_KEY]);
+    upcall->key = CONST_CAST(struct nlattr *,
+                             nl_attr_get(a[OVS_PACKET_ATTR_KEY]));
     upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]);
     upcall->userdata = (a[OVS_PACKET_ATTR_USERDATA]
                         ? nl_attr_get_u64(a[OVS_PACKET_ATTR_USERDATA])
@@ -1100,12 +1195,12 @@ dpif_linux_recv(struct dpif *dpif_, struct dpif_upcall *upcall,
     }
 
     if (!dpif->ready_mask) {
-        struct epoll_event events[N_UPCALL_SOCKS];
+        struct epoll_event events[N_CHANNELS];
         int retval;
         int i;
 
         do {
-            retval = epoll_wait(dpif->epoll_fd, events, N_UPCALL_SOCKS, 0);
+            retval = epoll_wait(dpif->epoll_fd, events, N_CHANNELS, 0);
         } while (retval < 0 && errno == EINTR);
         if (retval < 0) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
@@ -1119,7 +1214,7 @@ dpif_linux_recv(struct dpif *dpif_, struct dpif_upcall *upcall,
 
     while (dpif->ready_mask) {
         int indx = ffs(dpif->ready_mask) - 1;
-        struct nl_sock *upcall_sock = dpif->upcall_socks[indx];
+        struct dpif_channel *ch = &dpif->channels[indx];
 
         dpif->ready_mask &= ~(1u << indx);
 
@@ -1131,7 +1226,17 @@ dpif_linux_recv(struct dpif *dpif_, struct dpif_upcall *upcall,
                 return EAGAIN;
             }
 
-            error = nl_sock_recv(upcall_sock, buf, false);
+            error = nl_sock_recv(ch->sock, buf, false);
+            if (error == ENOBUFS) {
+                /* ENOBUFS typically means that we've received so many
+                 * packets that the buffer overflowed.  Try again
+                 * immediately because there's almost certainly a packet
+                 * waiting for us. */
+                report_loss(dpif_, ch);
+                continue;
+            }
+
+            ch->last_poll = time_msec();
             if (error) {
                 if (error == EAGAIN) {
                     break;
@@ -1141,6 +1246,13 @@ dpif_linux_recv(struct dpif *dpif_, struct dpif_upcall *upcall,
 
             error = parse_odp_packet(buf, upcall, &dp_ifindex);
             if (!error && dp_ifindex == dpif->dp_ifindex) {
+                const struct nlattr *in_port;
+
+                in_port = nl_attr_find__(upcall->key, upcall->key_len,
+                                         OVS_KEY_ATTR_IN_PORT);
+                if (in_port) {
+                    update_sketch(ch, nl_attr_get_u32(in_port));
+                }
                 return 0;
             }
             if (error) {
@@ -1168,14 +1280,14 @@ static void
 dpif_linux_recv_purge(struct dpif *dpif_)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-    int i;
+    struct dpif_channel *ch;
 
     if (dpif->epoll_fd < 0) {
        return;
     }
 
-    for (i = 0; i < N_UPCALL_SOCKS; i++) {
-        nl_sock_drain(dpif->upcall_socks[i]);
+    for (ch = dpif->channels; ch < &dpif->channels[N_CHANNELS]; ch++) {
+        nl_sock_drain(ch->sock);
     }
 }
 
@@ -1801,3 +1913,96 @@ dpif_linux_flow_get_stats(const struct dpif_linux_flow *flow,
     stats->used = flow->used ? get_32aligned_u64(flow->used) : 0;
     stats->tcp_flags = flow->tcp_flags ? *flow->tcp_flags : 0;
 }
+\f
+/* Metwally "space-saving" algorithm implementation. */
+
+/* Updates 'ch' to record that a packet was received on 'port_no'. */
+static void
+update_sketch(struct dpif_channel *ch, uint32_t port_no)
+{
+    struct dpif_sketch *sk;
+
+    /* Find an existing counting element for 'port_no' or, if none, replace the
+     * counting element with the fewest hits by 'port_no'. */
+    for (sk = ch->sketches; ; sk++) {
+        if (port_no == sk->port_no) {
+            break;
+        } else if (sk == &ch->sketches[N_SKETCHES - 1]) {
+            sk->port_no = port_no;
+            sk->error = sk->hits;
+            break;
+        }
+    }
+
+    /* Increment the hit count, then re-sort the counting elements (usually
+     * nothing needs to be done). */
+    sk->hits++;
+    while (sk > ch->sketches && sk[-1].hits > sk->hits) {
+        struct dpif_sketch tmp = sk[-1];
+        sk[-1] = *sk;
+        *sk = tmp;
+        sk--;
+    }
+}
+
+/* Divide the counts of all the the counting elements in 'dpif' by 2.  See the
+ * comment on SCALE_INTERVAL. */
+static void
+scale_sketches(struct dpif *dpif_)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    struct dpif_channel *ch;
+
+    for (ch = dpif->channels; ch < &dpif->channels[N_CHANNELS]; ch++) {
+        struct dpif_sketch *sk;
+
+        for (sk = ch->sketches; sk < &ch->sketches[N_SKETCHES]; sk++) {
+            sk->hits /= 2;
+            sk->error /= 2;
+        }
+    }
+}
+
+/* Logs information about a packet that was recently lost in 'ch' (in
+ * 'dpif_'). */
+static void
+report_loss(struct dpif *dpif_, struct dpif_channel *ch)
+{
+    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
+    struct dpif_sketch *sk;
+    struct ds s;
+
+    if (VLOG_DROP_ERR(&rl)) {
+        return;
+    }
+
+    ds_init(&s);
+    if (ch->last_poll != LLONG_MIN) {
+        ds_put_format(&s, " (last polled %lld ms ago)",
+                      time_msec() - ch->last_poll);
+    }
+    ds_put_cstr(&s, ", most frequent sources are");
+    for (sk = ch->sketches; sk < &ch->sketches[N_SKETCHES]; sk++) {
+        if (sk->hits) {
+            struct dpif_port port;
+
+            ds_put_format(&s, " %"PRIu32, sk->port_no);
+            if (!dpif_port_query_by_number(dpif_, sk->port_no, &port)) {
+                ds_put_format(&s, "(%s)", port.name);
+                dpif_port_destroy(&port);
+            }
+            if (sk->error) {
+                ds_put_format(&s, ": %u to %u,",
+                              sk->hits - sk->error, sk->hits);
+            } else {
+                ds_put_format(&s, ": %u,", sk->hits);
+            }
+        }
+    }
+    ds_chomp(&s, ',');
+
+    VLOG_ERR("%s: lost packet on channel %td%s",
+             dpif_name(dpif_), ch - dpif->channels, ds_cstr(&s));
+    ds_destroy(&s);
+}
index b08d28e..c9150b0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 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.
index fb23fa3..48bc92d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -70,8 +70,13 @@ enum { MAX_QUEUE_LEN = 128 };   /* Maximum number of packets per queue. */
 enum { QUEUE_MASK = MAX_QUEUE_LEN - 1 };
 BUILD_ASSERT_DECL(IS_POW2(MAX_QUEUE_LEN));
 
+struct dp_netdev_upcall {
+    struct dpif_upcall upcall;  /* Queued upcall information. */
+    struct ofpbuf buf;          /* ofpbuf instance for upcall.packet. */
+};
+
 struct dp_netdev_queue {
-    struct dpif_upcall *upcalls[MAX_QUEUE_LEN];
+    struct dp_netdev_upcall upcalls[MAX_QUEUE_LEN];
     unsigned int head, tail;
 };
 
@@ -171,9 +176,9 @@ dpif_netdev_enumerate(struct sset *all_dps)
     struct shash_node *node;
 
     SHASH_FOR_EACH(node, &dp_netdevs) {
-        sset_add(all_dps, node->name);        
+        sset_add(all_dps, node->name);
     }
-    return 0;    
+    return 0;
 }
 
 static struct dpif *
@@ -259,10 +264,8 @@ dp_netdev_purge_queues(struct dp_netdev *dp)
         struct dp_netdev_queue *q = &dp->queues[i];
 
         while (q->tail != q->head) {
-            struct dpif_upcall *upcall = q->upcalls[q->tail++ & QUEUE_MASK];
-
-            ofpbuf_delete(upcall->packet);
-            free(upcall);
+            struct dp_netdev_upcall *u = &q->upcalls[q->tail++ & QUEUE_MASK];
+            ofpbuf_uninit(&u->buf);
         }
     }
 }
@@ -416,7 +419,16 @@ dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
     struct dp_netdev *dp = get_dp_netdev(dpif);
     int port_no;
 
-    port_no = choose_port(dpif, netdev);
+    if (*port_nop != UINT16_MAX) {
+        if (*port_nop >= MAX_PORTS) {
+            return EFBIG;
+        } else if (dp->ports[*port_nop]) {
+            return EBUSY;
+        }
+        port_no = *port_nop;
+    } else {
+        port_no = choose_port(dpif, netdev);
+    }
     if (port_no >= 0) {
         *port_nop = port_no;
         return do_add_port(dp, netdev_get_name(netdev),
@@ -722,10 +734,9 @@ set_flow_actions(struct dp_netdev_flow *flow,
 }
 
 static int
-add_flow(struct dpif *dpif, const struct flow *key,
-         const struct nlattr *actions, size_t actions_len)
+dp_netdev_flow_add(struct dp_netdev *dp, const struct flow *key,
+                   const struct nlattr *actions, size_t actions_len)
 {
-    struct dp_netdev *dp = get_dp_netdev(dpif);
     struct dp_netdev_flow *flow;
     int error;
 
@@ -771,7 +782,8 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
                 if (put->stats) {
                     memset(put->stats, 0, sizeof *put->stats);
                 }
-                return add_flow(dpif, &key, put->actions, put->actions_len);
+                return dp_netdev_flow_add(dp, &key, put->actions,
+                                          put->actions_len);
             } else {
                 return EFBIG;
             }
@@ -960,12 +972,13 @@ dpif_netdev_recv(struct dpif *dpif, struct dpif_upcall *upcall,
 {
     struct dp_netdev_queue *q = find_nonempty_queue(dpif);
     if (q) {
-        struct dpif_upcall *u = q->upcalls[q->tail++ & QUEUE_MASK];
-        *upcall = *u;
-        free(u);
+        struct dp_netdev_upcall *u = &q->upcalls[q->tail++ & QUEUE_MASK];
+
+        *upcall = u->upcall;
+        upcall->packet = buf;
 
         ofpbuf_uninit(buf);
-        *buf = *u->packet;
+        *buf = u->buf;
 
         return 0;
     } else {
@@ -1011,7 +1024,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
     if (packet->size < ETH_HEADER_LEN) {
         return;
     }
-    flow_extract(packet, 0, 0, port->port_no, &key);
+    flow_extract(packet, 0, 0, odp_port_to_ofp_port(port->port_no), &key);
     flow = dp_netdev_lookup_flow(dp, &key);
     if (flow) {
         dp_netdev_flow_used(flow, &key, packet);
@@ -1087,6 +1100,7 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
                          int queue_no, const struct flow *flow, uint64_t arg)
 {
     struct dp_netdev_queue *q = &dp->queues[queue_no];
+    struct dp_netdev_upcall *u;
     struct dpif_upcall *upcall;
     struct ofpbuf *buf;
     size_t key_len;
@@ -1096,22 +1110,23 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
         return ENOBUFS;
     }
 
-    buf = ofpbuf_new(ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size);
+    u = &q->upcalls[q->head++ & QUEUE_MASK];
+
+    buf = &u->buf;
+    ofpbuf_init(buf, ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size);
     odp_flow_key_from_flow(buf, flow);
     key_len = buf->size;
     ofpbuf_pull(buf, key_len);
     ofpbuf_reserve(buf, 2);
     ofpbuf_put(buf, packet->data, packet->size);
 
-    upcall = xzalloc(sizeof *upcall);
+    upcall = &u->upcall;
     upcall->type = queue_no;
     upcall->packet = buf;
     upcall->key = buf->base;
     upcall->key_len = key_len;
     upcall->userdata = arg;
 
-    q->upcalls[q->head++ & QUEUE_MASK] = upcall;
-
     return 0;
 }
 
index e069e1e..f264e1b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -110,8 +110,12 @@ struct dpif_class {
     /* Retrieves statistics for 'dpif' into 'stats'. */
     int (*get_stats)(const struct dpif *dpif, struct dpif_dp_stats *stats);
 
-    /* Adds 'netdev' as a new port in 'dpif'.  If successful, sets '*port_no'
-     * to the new port's port number. */
+    /* Adds 'netdev' as a new port in 'dpif'.  If '*port_no' is not
+     * UINT16_MAX, attempts to use that as the port's port number.
+     *
+     * If port is successfully added, sets '*port_no' to the new port's
+     * port number.  Returns EBUSY if caller attempted to choose a port
+     * number, and it was in use. */
     int (*port_add)(struct dpif *dpif, struct netdev *netdev,
                     uint16_t *port_no);
 
@@ -136,6 +140,10 @@ struct dpif_class {
      * actions as the OVS_USERSPACE_ATTR_PID attribute's value, for use in
      * flows whose packets arrived on port 'port_no'.
      *
+     * A 'port_no' of UINT16_MAX should be treated as a special case.  The
+     * implementation should return a reserved PID, not allocated to any port,
+     * that the client may use for special purposes.
+     *
      * The return value only needs to be meaningful when DPIF_UC_ACTION has
      * been enabled in the 'dpif''s listen mask, and it is allowed to change
      * when DPIF_UC_ACTION is disabled and then re-enabled.
index 30e9b55..7be7b2a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -411,19 +411,27 @@ dpif_get_dp_stats(const struct dpif *dpif, struct dpif_dp_stats *stats)
     return error;
 }
 
-/* Attempts to add 'netdev' as a port on 'dpif'.  If successful, returns 0 and
- * sets '*port_nop' to the new port's port number (if 'port_nop' is non-null).
- * On failure, returns a positive errno value and sets '*port_nop' to
- * UINT16_MAX (if 'port_nop' is non-null). */
+/* Attempts to add 'netdev' as a port on 'dpif'.  If 'port_nop' is
+ * non-null and its value is not UINT16_MAX, then attempts to use the
+ * value as the port number.
+ *
+ * If successful, returns 0 and sets '*port_nop' to the new port's port
+ * number (if 'port_nop' is non-null).  On failure, returns a positive
+ * errno value and sets '*port_nop' to UINT16_MAX (if 'port_nop' is
+ * non-null). */
 int
 dpif_port_add(struct dpif *dpif, struct netdev *netdev, uint16_t *port_nop)
 {
     const char *netdev_name = netdev_get_name(netdev);
-    uint16_t port_no;
+    uint16_t port_no = UINT16_MAX;
     int error;
 
     COVERAGE_INC(dpif_port_add);
 
+    if (port_nop) {
+        port_no = *port_nop;
+    }
+
     error = dpif->dpif_class->port_add(dpif, netdev, &port_no);
     if (!error) {
         VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu16,
@@ -542,6 +550,9 @@ dpif_get_max_ports(const struct dpif *dpif)
  * as the OVS_USERSPACE_ATTR_PID attribute's value, for use in flows whose
  * packets arrived on port 'port_no'.
  *
+ * A 'port_no' of UINT16_MAX is a special case: it returns a reserved PID, not
+ * 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
  * the 'dpif''s listen mask.  It is allowed to change when DPIF_UC_ACTION is
  * disabled and then re-enabled, so a client that does that must be prepared to
@@ -675,15 +686,16 @@ dpif_port_poll_wait(const struct dpif *dpif)
 }
 
 /* Extracts the flow stats for a packet.  The 'flow' and 'packet'
- * arguments must have been initialized through a call to flow_extract(). */
+ * arguments must have been initialized through a call to flow_extract().
+ * 'used' is stored into stats->used. */
 void
 dpif_flow_stats_extract(const struct flow *flow, const struct ofpbuf *packet,
-                        struct dpif_flow_stats *stats)
+                        long long int used, struct dpif_flow_stats *stats)
 {
     stats->tcp_flags = packet_get_tcp_flags(packet, flow);
     stats->n_bytes = packet->size;
     stats->n_packets = 1;
-    stats->used = time_msec();
+    stats->used = used;
 }
 
 /* Appends a human-readable representation of 'stats' to 's'. */
index bdd4fee..45c78a5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -129,7 +129,7 @@ struct dpif_flow_stats {
 };
 
 void dpif_flow_stats_extract(const struct flow *, const struct ofpbuf *packet,
-                             struct dpif_flow_stats *);
+                             long long int used, struct dpif_flow_stats *);
 void dpif_flow_stats_format(const struct dpif_flow_stats *, struct ds *);
 
 enum dpif_flow_put_flags {
index a07830a..1354de6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index abbb363..2204170 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 8e58302..b648eef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -250,6 +250,54 @@ ds_get_preprocessed_line(struct ds *ds, FILE *file)
     return EOF;
 }
 
+/* Reads a line from 'file' into 'ds' and does some preprocessing on it:
+ *
+ *    - If the line begins with #, prints it on stdout and reads the next line.
+ *
+ *    - Otherwise, if the line contains an # somewhere else, strips it and
+ *      everything following it (as a comment).
+ *
+ *    - If (after comment removal) the line contains only white space, prints
+ *      a blank line on stdout and reads the next line.
+ *
+ *    - Otherwise, returns the line to the caller.
+ *
+ * This is useful in some of the OVS tests, where we want to check that parsing
+ * and then re-formatting some kind of data does not change it, but we also
+ * want to be able to put comments in the input.
+ *
+ * Returns 0 if successful, EOF if no non-blank line was found. */
+int
+ds_get_test_line(struct ds *ds, FILE *file)
+{
+    for (;;) {
+        char *s, *comment;
+        int retval;
+
+        retval = ds_get_line(ds, file);
+        if (retval) {
+            return retval;
+        }
+
+        s = ds_cstr(ds);
+        if (*s == '#') {
+            puts(s);
+            continue;
+        }
+
+        comment = strchr(s, '#');
+        if (comment) {
+            *comment = '\0';
+        }
+        if (s[strspn(s, " \t\n")] == '\0') {
+            putchar('\n');
+            continue;
+        }
+
+        return 0;
+    }
+}
+
 char *
 ds_cstr(struct ds *ds)
 {
@@ -263,7 +311,7 @@ ds_cstr(struct ds *ds)
 const char *
 ds_cstr_ro(const struct ds *ds)
 {
-    return ds_cstr((struct ds *) ds);
+    return ds_cstr(CONST_CAST(struct ds *, ds));
 }
 
 /* Returns a null-terminated string representing the current contents of 'ds',
index a8a27ad..098caaf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -62,6 +62,7 @@ void ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size,
                      uintptr_t ofs, bool ascii);
 int ds_get_line(struct ds *, FILE *);
 int ds_get_preprocessed_line(struct ds *, FILE *);
+int ds_get_test_line(struct ds *, FILE *);
 
 char *ds_cstr(struct ds *);
 const char *ds_cstr_ro(const struct ds *);
index f38655c..c1f0886 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 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.
index 6d256e0..6322b9f 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010 Nicira Networks
+/* Copyright (c) 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.
index a6ce1b7..21ebb5a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -75,8 +75,8 @@ fatal_signal_init(void)
         inited = true;
 
         xpipe(signal_fds);
-        set_nonblocking(signal_fds[0]);
-        set_nonblocking(signal_fds[1]);
+        xset_nonblocking(signal_fds[0]);
+        xset_nonblocking(signal_fds[1]);
 
         sigemptyset(&fatal_signal_set);
         for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
@@ -152,12 +152,14 @@ fatal_signal_handler(int sig_nr)
 void
 fatal_signal_run(void)
 {
-    int sig_nr;
+    sig_atomic_t sig_nr;
 
     fatal_signal_init();
 
     sig_nr = stored_sig_nr;
     if (sig_nr != SIG_ATOMIC_MAX) {
+        VLOG_WARN("terminating with signal %d (%s)",
+                  (int)sig_nr, signal_name(sig_nr));
         call_hooks(sig_nr);
 
         /* Re-raise the signal with the default handling so that the program
index 94a1f1f..8a1a84b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * 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.
index ef1dd6d..f4446c9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
 #include <string.h>
 #include "byte-order.h"
 #include "coverage.h"
+#include "csum.h"
 #include "dynamic-string.h"
 #include "hash.h"
 #include "ofpbuf.h"
@@ -179,7 +180,7 @@ parse_ipv6(struct ofpbuf *packet, struct flow *flow)
                 || (nexthdr == IPPROTO_DSTOPTS)) {
             /* These headers, while different, have the fields we care about
              * in the same location and with the same interpretation. */
-            const struct ip6_ext *ext_hdr = (struct ip6_ext *)packet->data;
+            const struct ip6_ext *ext_hdr = packet->data;
             nexthdr = ext_hdr->ip6e_nxt;
             if (!ofpbuf_try_pull(packet, (ext_hdr->ip6e_len + 1) * 8)) {
                 return EINVAL;
@@ -189,13 +190,13 @@ parse_ipv6(struct ofpbuf *packet, struct flow *flow)
              * we care about are in the same location as the generic
              * option header--only the header length is calculated
              * differently. */
-            const struct ip6_ext *ext_hdr = (struct ip6_ext *)packet->data;
+            const struct ip6_ext *ext_hdr = packet->data;
             nexthdr = ext_hdr->ip6e_nxt;
             if (!ofpbuf_try_pull(packet, (ext_hdr->ip6e_len + 2) * 4)) {
                return EINVAL;
             }
         } else if (nexthdr == IPPROTO_FRAGMENT) {
-            const struct ip6_frag *frag_hdr = (struct ip6_frag *)packet->data;
+            const struct ip6_frag *frag_hdr = packet->data;
 
             nexthdr = frag_hdr->ip6f_nxt;
             if (!ofpbuf_try_pull(packet, sizeof *frag_hdr)) {
@@ -444,12 +445,13 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     const flow_wildcards_t wc = wildcards->wildcards;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         flow->regs[i] &= wildcards->reg_masks[i];
     }
     flow->tun_id &= wildcards->tun_id_mask;
+    flow->metadata &= wildcards->metadata_mask;
     flow->nw_src &= wildcards->nw_src_mask;
     flow->nw_dst &= wildcards->nw_dst_mask;
     if (wc & FWW_IN_PORT) {
@@ -461,22 +463,12 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     }
     flow->tp_src &= wildcards->tp_src_mask;
     flow->tp_dst &= wildcards->tp_dst_mask;
-    if (wc & FWW_DL_SRC) {
-        memset(flow->dl_src, 0, sizeof flow->dl_src);
-    }
-    if (wc & FWW_DL_DST) {
-        flow->dl_dst[0] &= 0x01;
-        memset(&flow->dl_dst[1], 0, 5);
-    }
-    if (wc & FWW_ETH_MCAST) {
-        flow->dl_dst[0] &= 0xfe;
-    }
+    eth_addr_bitand(flow->dl_src, wildcards->dl_src_mask, flow->dl_src);
+    eth_addr_bitand(flow->dl_dst, wildcards->dl_dst_mask, flow->dl_dst);
     if (wc & FWW_NW_PROTO) {
         flow->nw_proto = 0;
     }
-    if (wc & FWW_IPV6_LABEL) {
-        flow->ipv6_label = htonl(0);
-    }
+    flow->ipv6_label &= wildcards->ipv6_label_mask;
     if (wc & FWW_NW_DSCP) {
         flow->nw_tos &= ~IP_DSCP_MASK;
     }
@@ -487,12 +479,8 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
         flow->nw_ttl = 0;
     }
     flow->nw_frag &= wildcards->nw_frag_mask;
-    if (wc & FWW_ARP_SHA) {
-        memset(flow->arp_sha, 0, sizeof flow->arp_sha);
-    }
-    if (wc & FWW_ARP_THA) {
-        memset(flow->arp_tha, 0, sizeof flow->arp_tha);
-    }
+    eth_addr_bitand(flow->arp_sha, wildcards->arp_sha_mask, flow->arp_sha);
+    eth_addr_bitand(flow->arp_tha, wildcards->arp_tha_mask, flow->arp_tha);
     flow->ipv6_src = ipv6_addr_bitand(&flow->ipv6_src,
             &wildcards->ipv6_src_mask);
     flow->ipv6_dst = ipv6_addr_bitand(&flow->ipv6_dst,
@@ -506,14 +494,11 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     fmd->tun_id = flow->tun_id;
-    fmd->tun_id_mask = htonll(UINT64_MAX);
-
+    fmd->metadata = flow->metadata;
     memcpy(fmd->regs, flow->regs, sizeof fmd->regs);
-    memset(fmd->reg_masks, 0xff, sizeof fmd->reg_masks);
-
     fmd->in_port = flow->in_port;
 }
 
@@ -530,9 +515,11 @@ flow_format(struct ds *ds, const struct flow *flow)
 {
     ds_put_format(ds, "priority:%"PRIu32
                       ",tunnel:%#"PRIx64
+                      ",metadata:%#"PRIx64
                       ",in_port:%04"PRIx16,
                       flow->skb_priority,
                       ntohll(flow->tun_id),
+                      ntohll(flow->metadata),
                       flow->in_port);
 
     ds_put_format(ds, ",tci(");
@@ -595,7 +582,7 @@ flow_print(FILE *stream, const struct flow *flow)
 void
 flow_wildcards_init_catchall(struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     wc->wildcards = FWW_ALL;
     wc->tun_id_mask = htonll(0);
@@ -603,12 +590,18 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
     wc->nw_dst_mask = htonl(0);
     wc->ipv6_src_mask = in6addr_any;
     wc->ipv6_dst_mask = in6addr_any;
+    wc->ipv6_label_mask = htonl(0);
     wc->nd_target_mask = in6addr_any;
     memset(wc->reg_masks, 0, sizeof wc->reg_masks);
+    wc->metadata_mask = htonll(0);
     wc->vlan_tci_mask = htons(0);
     wc->nw_frag_mask = 0;
     wc->tp_src_mask = htons(0);
     wc->tp_dst_mask = htons(0);
+    memset(wc->dl_src_mask, 0, ETH_ADDR_LEN);
+    memset(wc->dl_dst_mask, 0, ETH_ADDR_LEN);
+    memset(wc->arp_sha_mask, 0, ETH_ADDR_LEN);
+    memset(wc->arp_tha_mask, 0, ETH_ADDR_LEN);
     memset(wc->zeros, 0, sizeof wc->zeros);
 }
 
@@ -617,7 +610,7 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
 void
 flow_wildcards_init_exact(struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     wc->wildcards = 0;
     wc->tun_id_mask = htonll(UINT64_MAX);
@@ -625,12 +618,18 @@ flow_wildcards_init_exact(struct flow_wildcards *wc)
     wc->nw_dst_mask = htonl(UINT32_MAX);
     wc->ipv6_src_mask = in6addr_exact;
     wc->ipv6_dst_mask = in6addr_exact;
+    wc->ipv6_label_mask = htonl(UINT32_MAX);
     wc->nd_target_mask = in6addr_exact;
     memset(wc->reg_masks, 0xff, sizeof wc->reg_masks);
+    wc->metadata_mask = htonll(UINT64_MAX);
     wc->vlan_tci_mask = htons(UINT16_MAX);
     wc->nw_frag_mask = UINT8_MAX;
     wc->tp_src_mask = htons(UINT16_MAX);
     wc->tp_dst_mask = htons(UINT16_MAX);
+    memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN);
+    memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN);
+    memset(wc->arp_sha_mask, 0xff, ETH_ADDR_LEN);
+    memset(wc->arp_tha_mask, 0xff, ETH_ADDR_LEN);
     memset(wc->zeros, 0, sizeof wc->zeros);
 }
 
@@ -641,7 +640,7 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc)
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     if (wc->wildcards
         || wc->tun_id_mask != htonll(UINT64_MAX)
@@ -650,8 +649,14 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc)
         || wc->tp_src_mask != htons(UINT16_MAX)
         || wc->tp_dst_mask != htons(UINT16_MAX)
         || wc->vlan_tci_mask != htons(UINT16_MAX)
+        || wc->metadata_mask != htonll(UINT64_MAX)
+        || !eth_mask_is_exact(wc->dl_src_mask)
+        || !eth_mask_is_exact(wc->dl_dst_mask)
+        || !eth_mask_is_exact(wc->arp_sha_mask)
+        || !eth_mask_is_exact(wc->arp_tha_mask)
         || !ipv6_mask_is_exact(&wc->ipv6_src_mask)
         || !ipv6_mask_is_exact(&wc->ipv6_dst_mask)
+        || wc->ipv6_label_mask != htonl(UINT32_MAX)
         || !ipv6_mask_is_exact(&wc->nd_target_mask)
         || wc->nw_frag_mask != UINT8_MAX) {
         return false;
@@ -673,7 +678,7 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc)
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     if (wc->wildcards != FWW_ALL
         || wc->tun_id_mask != htonll(0)
@@ -682,8 +687,14 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc)
         || wc->tp_src_mask != htons(0)
         || wc->tp_dst_mask != htons(0)
         || wc->vlan_tci_mask != htons(0)
+        || wc->metadata_mask != htonll(0)
+        || !eth_addr_is_zero(wc->dl_src_mask)
+        || !eth_addr_is_zero(wc->dl_dst_mask)
+        || !eth_addr_is_zero(wc->arp_sha_mask)
+        || !eth_addr_is_zero(wc->arp_tha_mask)
         || !ipv6_mask_is_any(&wc->ipv6_src_mask)
         || !ipv6_mask_is_any(&wc->ipv6_dst_mask)
+        || wc->ipv6_label_mask != htonl(0)
         || !ipv6_mask_is_any(&wc->nd_target_mask)
         || wc->nw_frag_mask != 0) {
         return false;
@@ -708,7 +719,7 @@ flow_wildcards_combine(struct flow_wildcards *dst,
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     dst->wildcards = src1->wildcards | src2->wildcards;
     dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask;
@@ -718,14 +729,21 @@ flow_wildcards_combine(struct flow_wildcards *dst,
                                         &src2->ipv6_src_mask);
     dst->ipv6_dst_mask = ipv6_addr_bitand(&src1->ipv6_dst_mask,
                                         &src2->ipv6_dst_mask);
+    dst->ipv6_label_mask = src1->ipv6_label_mask & src2->ipv6_label_mask;
     dst->nd_target_mask = ipv6_addr_bitand(&src1->nd_target_mask,
                                         &src2->nd_target_mask);
     for (i = 0; i < FLOW_N_REGS; i++) {
         dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i];
     }
+    dst->metadata_mask = src1->metadata_mask & src2->metadata_mask;
     dst->vlan_tci_mask = src1->vlan_tci_mask & src2->vlan_tci_mask;
     dst->tp_src_mask = src1->tp_src_mask & src2->tp_src_mask;
     dst->tp_dst_mask = src1->tp_dst_mask & src2->tp_dst_mask;
+    dst->nw_frag_mask = src1->nw_frag_mask & src2->nw_frag_mask;
+    eth_addr_bitand(src1->dl_src_mask, src2->dl_src_mask, dst->dl_src_mask);
+    eth_addr_bitand(src1->dl_dst_mask, src2->dl_dst_mask, dst->dl_dst_mask);
+    eth_addr_bitand(src1->arp_sha_mask, src2->arp_sha_mask, dst->arp_sha_mask);
+    eth_addr_bitand(src1->arp_tha_mask, src2->arp_tha_mask, dst->arp_tha_mask);
 }
 
 /* Returns a hash of the wildcards in 'wc'. */
@@ -735,7 +753,7 @@ flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis)
     /* If you change struct flow_wildcards and thereby trigger this
      * assertion, please check that the new struct flow_wildcards has no holes
      * in it before you update the assertion. */
-    BUILD_ASSERT_DECL(sizeof *wc == 80 + FLOW_N_REGS * 4);
+    BUILD_ASSERT_DECL(sizeof *wc == 112 + FLOW_N_REGS * 4);
     return hash_bytes(wc, sizeof *wc, basis);
 }
 
@@ -747,18 +765,25 @@ flow_wildcards_equal(const struct flow_wildcards *a,
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     if (a->wildcards != b->wildcards
         || a->tun_id_mask != b->tun_id_mask
         || a->nw_src_mask != b->nw_src_mask
         || a->nw_dst_mask != b->nw_dst_mask
         || a->vlan_tci_mask != b->vlan_tci_mask
+        || a->metadata_mask != b->metadata_mask
         || !ipv6_addr_equals(&a->ipv6_src_mask, &b->ipv6_src_mask)
         || !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask)
+        || a->ipv6_label_mask != b->ipv6_label_mask
         || !ipv6_addr_equals(&a->nd_target_mask, &b->nd_target_mask)
         || a->tp_src_mask != b->tp_src_mask
-        || a->tp_dst_mask != b->tp_dst_mask) {
+        || a->tp_dst_mask != b->tp_dst_mask
+        || a->nw_frag_mask != b->nw_frag_mask
+        || !eth_addr_equals(a->dl_src_mask, b->dl_src_mask)
+        || !eth_addr_equals(a->dl_dst_mask, b->dl_dst_mask)
+        || !eth_addr_equals(a->arp_sha_mask, b->arp_sha_mask)
+        || !eth_addr_equals(a->arp_tha_mask, b->arp_tha_mask)) {
         return false;
     }
 
@@ -778,9 +803,10 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
                          const struct flow_wildcards *b)
 {
     int i;
+    uint8_t eth_masked[ETH_ADDR_LEN];
     struct in6_addr ipv6_masked;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
@@ -788,6 +814,26 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
         }
     }
 
+    eth_addr_bitand(a->dl_src_mask, b->dl_src_mask, eth_masked);
+    if (!eth_addr_equals(eth_masked, b->dl_src_mask)) {
+        return true;
+    }
+
+    eth_addr_bitand(a->dl_dst_mask, b->dl_dst_mask, eth_masked);
+    if (!eth_addr_equals(eth_masked, b->dl_dst_mask)) {
+        return true;
+    }
+
+    eth_addr_bitand(a->arp_sha_mask, b->arp_sha_mask, eth_masked);
+    if (!eth_addr_equals(eth_masked, b->arp_sha_mask)) {
+        return true;
+    }
+
+    eth_addr_bitand(a->arp_tha_mask, b->arp_tha_mask, eth_masked);
+    if (!eth_addr_equals(eth_masked, b->arp_tha_mask)) {
+        return true;
+    }
+
     ipv6_masked = ipv6_addr_bitand(&a->ipv6_src_mask, &b->ipv6_src_mask);
     if (!ipv6_addr_equals(&ipv6_masked, &b->ipv6_src_mask)) {
         return true;
@@ -807,9 +853,12 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
             || (a->tun_id_mask & b->tun_id_mask) != b->tun_id_mask
             || (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask
             || (a->nw_dst_mask & b->nw_dst_mask) != b->nw_dst_mask
+            || (a->ipv6_label_mask & b->ipv6_label_mask) != b->ipv6_label_mask
             || (a->vlan_tci_mask & b->vlan_tci_mask) != b->vlan_tci_mask
+            || (a->metadata_mask & b->metadata_mask) != b->metadata_mask
             || (a->tp_src_mask & b->tp_src_mask) != b->tp_src_mask
-            || (a->tp_dst_mask & b->tp_dst_mask) != b->tp_dst_mask);
+            || (a->tp_dst_mask & b->tp_dst_mask) != b->tp_dst_mask
+            || (a->nw_frag_mask & b->nw_frag_mask) != b->nw_frag_mask);
 }
 
 /* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
@@ -820,83 +869,6 @@ flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask)
     wc->reg_masks[idx] = mask;
 }
 
-/* Returns the wildcard bitmask for the Ethernet destination address
- * that 'wc' specifies.  The bitmask has a 0 in each bit that is wildcarded
- * and a 1 in each bit that must match.  */
-const uint8_t *
-flow_wildcards_to_dl_dst_mask(flow_wildcards_t wc)
-{
-    static const uint8_t    no_wild[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-    static const uint8_t  addr_wild[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
-    static const uint8_t mcast_wild[] = {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff};
-    static const uint8_t   all_wild[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-    switch (wc & (FWW_DL_DST | FWW_ETH_MCAST)) {
-    case 0:                             return no_wild;
-    case FWW_DL_DST:                    return addr_wild;
-    case FWW_ETH_MCAST:                 return mcast_wild;
-    case FWW_DL_DST | FWW_ETH_MCAST:    return all_wild;
-    }
-    NOT_REACHED();
-}
-
-/* Returns true if 'mask' is a valid wildcard bitmask for the Ethernet
- * destination address.  Valid bitmasks are either all-bits-0 or all-bits-1,
- * except that the multicast bit may differ from the rest of the bits.  So,
- * there are four possible valid bitmasks:
- *
- *  - 00:00:00:00:00:00
- *  - 01:00:00:00:00:00
- *  - fe:ff:ff:ff:ff:ff
- *  - ff:ff:ff:ff:ff:ff
- *
- * All other bitmasks are invalid. */
-bool
-flow_wildcards_is_dl_dst_mask_valid(const uint8_t mask[ETH_ADDR_LEN])
-{
-    switch (mask[0]) {
-    case 0x00:
-    case 0x01:
-        return (mask[1] | mask[2] | mask[3] | mask[4] | mask[5]) == 0x00;
-
-    case 0xfe:
-    case 0xff:
-        return (mask[1] & mask[2] & mask[3] & mask[4] & mask[5]) == 0xff;
-
-    default:
-        return false;
-    }
-}
-
-/* Returns 'wc' with the FWW_DL_DST and FWW_ETH_MCAST bits modified
- * appropriately to match 'mask'.
- *
- * This function will assert-fail if 'mask' is invalid.  Only 'mask' values
- * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */
-flow_wildcards_t
-flow_wildcards_set_dl_dst_mask(flow_wildcards_t wc,
-                               const uint8_t mask[ETH_ADDR_LEN])
-{
-    assert(flow_wildcards_is_dl_dst_mask_valid(mask));
-
-    switch (mask[0]) {
-    case 0x00:
-        return wc | FWW_DL_DST | FWW_ETH_MCAST;
-
-    case 0x01:
-        return (wc | FWW_DL_DST) & ~FWW_ETH_MCAST;
-
-    case 0xfe:
-        return (wc & ~FWW_DL_DST) | FWW_ETH_MCAST;
-
-    case 0xff:
-        return wc & ~(FWW_DL_DST | FWW_ETH_MCAST);
-
-    default:
-        NOT_REACHED();
-    }
-}
-
 /* Hashes 'flow' based on its L2 through L4 protocol information. */
 uint32_t
 flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
@@ -994,9 +966,9 @@ flow_hash_fields_valid(enum nx_hash_fields fields)
  *
  *      - Other values of 'vid' should not be used. */
 void
-flow_set_vlan_vid(struct flow *flow, ovs_be16 vid)
+flow_set_dl_vlan(struct flow *flow, ovs_be16 vid)
 {
-    if (vid == htons(OFP_VLAN_NONE)) {
+    if (vid == htons(OFP10_VLAN_NONE)) {
         flow->vlan_tci = htons(0);
     } else {
         vid &= htons(VLAN_VID_MASK);
@@ -1005,6 +977,17 @@ flow_set_vlan_vid(struct flow *flow, ovs_be16 vid)
     }
 }
 
+/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI). */
+void
+flow_set_vlan_vid(struct flow *flow, ovs_be16 vid)
+{
+    ovs_be16 mask = htons(VLAN_VID_MASK | VLAN_CFI);
+    flow->vlan_tci &= ~mask;
+    flow->vlan_tci |= vid & mask;
+}
+
 /* Sets the VLAN PCP that 'flow' matches to 'pcp', which should be in the
  * range 0...7.
  *
@@ -1024,7 +1007,7 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp)
  * 'flow'.
  *
  * (This is useful only for testing, obviously, and the packet isn't really
- * valid.  It hasn't got any checksums filled in, for one, and lots of fields
+ * valid. It hasn't got some checksums filled in, for one, and lots of fields
  * are just zeroed.) */
 void
 flow_compose(struct ofpbuf *b, const struct flow *flow)
@@ -1077,11 +1060,14 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
                 b->l4 = icmp = ofpbuf_put_zeros(b, sizeof *icmp);
                 icmp->icmp_type = ntohs(flow->tp_src);
                 icmp->icmp_code = ntohs(flow->tp_dst);
+                icmp->icmp_csum = csum(icmp, ICMP_HEADER_LEN);
             }
         }
 
+        ip = b->l3;
         ip->ip_tot_len = htons((uint8_t *) b->data + b->size
                                - (uint8_t *) b->l3);
+        ip->ip_csum = csum(ip, sizeof *ip);
     } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
         /* XXX */
     } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
index 41e6386..568e291 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 struct dpif_flow_stats;
 struct ds;
 struct flow_wildcards;
-struct ofp_match;
 struct ofpbuf;
 
 /* This sequence number should be incremented whenever anything involving flows
  * or the wildcarding of flows changes.  This will cause build assertion
  * failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 10
+#define FLOW_WC_SEQ 14
 
 #define FLOW_N_REGS 8
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -54,6 +53,7 @@ BUILD_ASSERT_DECL(FLOW_NW_FRAG_LATER == NX_IP_FRAG_LATER);
 
 struct flow {
     ovs_be64 tun_id;            /* Encapsulating tunnel ID. */
+    ovs_be64 metadata;          /* OpenFlow Metadata. */
     struct in6_addr ipv6_src;   /* IPv6 source address. */
     struct in6_addr ipv6_dst;   /* IPv6 destination address. */
     struct in6_addr nd_target;  /* IPv6 neighbor discovery (ND) target. */
@@ -78,29 +78,24 @@ struct flow {
     uint8_t reserved[2];        /* Reserved for 64-bit packing. */
 };
 
-/* Represents the metadata fields of struct flow.  The masks are used to
- * indicate which metadata fields are relevant in a given context.  Typically
- * they will be all 1 or all 0. */
+/* Represents the metadata fields of struct flow. */
 struct flow_metadata {
     ovs_be64 tun_id;                 /* Encapsulating tunnel ID. */
-    ovs_be64 tun_id_mask;            /* 1-bit in each significant tun_id bit.*/
-
+    ovs_be64 metadata;               /* OpenFlow 1.1+ metadata field. */
     uint32_t regs[FLOW_N_REGS];      /* Registers. */
-    uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
-
     uint16_t in_port;                /* OpenFlow port or zero. */
 };
 
 /* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
  * flow", followed by FLOW_PAD_SIZE bytes of padding. */
-#define FLOW_SIG_SIZE (110 + FLOW_N_REGS * 4)
+#define FLOW_SIG_SIZE (118 + FLOW_N_REGS * 4)
 #define FLOW_PAD_SIZE 2
 BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) == FLOW_SIG_SIZE - 1);
 BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nw_frag) == 1);
 BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 142 && FLOW_WC_SEQ == 10);
+BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 150 && FLOW_WC_SEQ == 14);
 
 void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id,
                   uint16_t in_port, struct flow *);
@@ -114,6 +109,7 @@ static inline int flow_compare_3way(const struct flow *, const struct flow *);
 static inline bool flow_equal(const struct flow *, const struct flow *);
 static inline size_t flow_hash(const struct flow *, uint32_t basis);
 
+void flow_set_dl_vlan(struct flow *, ovs_be16 vid);
 void flow_set_vlan_vid(struct flow *, ovs_be16 vid);
 void flow_set_vlan_pcp(struct flow *, uint8_t pcp);
 
@@ -145,26 +141,18 @@ flow_hash(const struct flow *flow, uint32_t basis)
 
 typedef unsigned int OVS_BITWISE flow_wildcards_t;
 
-/* Same values and meanings as corresponding OFPFW_* bits. */
+/* Same values and meanings as corresponding OFPFW10_* bits. */
 #define FWW_IN_PORT     ((OVS_FORCE flow_wildcards_t) (1 << 0))
-#define FWW_DL_SRC      ((OVS_FORCE flow_wildcards_t) (1 << 2))
-#define FWW_DL_DST      ((OVS_FORCE flow_wildcards_t) (1 << 3))
-                                              /* excluding the multicast bit */
 #define FWW_DL_TYPE     ((OVS_FORCE flow_wildcards_t) (1 << 4))
 #define FWW_NW_PROTO    ((OVS_FORCE flow_wildcards_t) (1 << 5))
-/* No corresponding OFPFW_* bits. */
-#define FWW_ETH_MCAST   ((OVS_FORCE flow_wildcards_t) (1 << 1))
-                                                       /* multicast bit only */
-#define FWW_NW_DSCP     ((OVS_FORCE flow_wildcards_t) (1 << 6))
-#define FWW_NW_ECN      ((OVS_FORCE flow_wildcards_t) (1 << 7))
-#define FWW_ARP_SHA     ((OVS_FORCE flow_wildcards_t) (1 << 8))
-#define FWW_ARP_THA     ((OVS_FORCE flow_wildcards_t) (1 << 9))
-#define FWW_IPV6_LABEL  ((OVS_FORCE flow_wildcards_t) (1 << 10))
-#define FWW_NW_TTL      ((OVS_FORCE flow_wildcards_t) (1 << 11))
-#define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 12)) - 1))
+/* No corresponding OFPFW10_* bits. */
+#define FWW_NW_DSCP     ((OVS_FORCE flow_wildcards_t) (1 << 1))
+#define FWW_NW_ECN      ((OVS_FORCE flow_wildcards_t) (1 << 2))
+#define FWW_NW_TTL      ((OVS_FORCE flow_wildcards_t) (1 << 3))
+#define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 6)) - 1))
 
 /* Remember to update FLOW_WC_SEQ when adding or removing FWW_*. */
-BUILD_ASSERT_DECL(FWW_ALL == ((1 << 12) - 1) && FLOW_WC_SEQ == 10);
+BUILD_ASSERT_DECL(FWW_ALL == ((1 << 6) - 1) && FLOW_WC_SEQ == 14);
 
 /* Information on wildcards for a flow, as a supplement to "struct flow".
  *
@@ -172,6 +160,7 @@ BUILD_ASSERT_DECL(FWW_ALL == ((1 << 12) - 1) && FLOW_WC_SEQ == 10);
  * the rest of the members. */
 struct flow_wildcards {
     ovs_be64 tun_id_mask;       /* 1-bit in each significant tun_id bit. */
+    ovs_be64 metadata_mask;     /* 1-bit in each significant metadata bit. */
     flow_wildcards_t wildcards; /* 1-bit in each FWW_* wildcarded field. */
     uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
     ovs_be32 nw_src_mask;       /* 1-bit in each significant nw_src bit. */
@@ -180,15 +169,20 @@ struct flow_wildcards {
     struct in6_addr ipv6_dst_mask; /* 1-bit in each signficant ipv6_dst bit. */
     struct in6_addr nd_target_mask; /* 1-bit in each significant
                                        nd_target bit. */
+    ovs_be32 ipv6_label_mask;   /* 1 bit in each significant ipv6_label bit. */
     ovs_be16 vlan_tci_mask;     /* 1-bit in each significant vlan_tci bit. */
     ovs_be16 tp_src_mask;       /* 1-bit in each significant tp_src bit. */
     ovs_be16 tp_dst_mask;       /* 1-bit in each significant tp_dst bit. */
     uint8_t nw_frag_mask;       /* 1-bit in each significant nw_frag bit. */
-    uint8_t zeros[5];           /* Padding field set to zero. */
+    uint8_t dl_src_mask[6];     /* 1-bit in each significant dl_src bit. */
+    uint8_t dl_dst_mask[6];     /* 1-bit in each significant dl_dst bit. */
+    uint8_t arp_sha_mask[6];    /* 1-bit in each significant dl_dst bit. */
+    uint8_t arp_tha_mask[6];    /* 1-bit in each significant dl_dst bit. */
+    uint8_t zeros[1];           /* Padding field set to zero. */
 };
 
 /* Remember to update FLOW_WC_SEQ when updating struct flow_wildcards. */
-BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 112 && FLOW_WC_SEQ == 10);
+BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 144 && FLOW_WC_SEQ == 14);
 
 void flow_wildcards_init_catchall(struct flow_wildcards *);
 void flow_wildcards_init_exact(struct flow_wildcards *);
index a77da7d..f7aa916 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 05799b2..ac6a63c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 77b7955..b2d547a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 8cfcf70..9326d79 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 5862b62..1971029 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 246fba2..2867bfa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -247,7 +247,7 @@ hmap_next_with_hash__(const struct hmap_node *node, size_t hash)
     while (node != NULL && node->hash != hash) {
         node = node->next;
     }
-    return (struct hmap_node *) node;
+    return CONST_CAST(struct hmap_node *, node);
 }
 
 /* Returns the first node in 'hmap' with the given 'hash', or a null pointer if
index bcdd7a8..1c324c4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 226255d..9b00a5d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index d514a90..f0b6456 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -296,14 +296,14 @@ struct json_array *
 json_array(const struct json *json)
 {
     assert(json->type == JSON_ARRAY);
-    return (struct json_array *) &json->u.array;
+    return CONST_CAST(struct json_array *, &json->u.array);
 }
 
 struct shash *
 json_object(const struct json *json)
 {
     assert(json->type == JSON_OBJECT);
-    return (struct shash *) json->u.object;
+    return CONST_CAST(struct shash *, json->u.object);
 }
 
 bool
index 5c32c06..cfe9457 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
index 4ebff10..5761369 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -793,6 +793,7 @@ jsonrpc_session_open_unreliably(struct jsonrpc *jsonrpc)
     reconnect_set_name(s->reconnect, jsonrpc_get_name(jsonrpc));
     reconnect_set_max_tries(s->reconnect, 0);
     reconnect_connected(s->reconnect, time_msec());
+    s->dscp = 0;
     s->rpc = jsonrpc;
     s->stream = NULL;
     s->pstream = NULL;
@@ -1058,5 +1059,8 @@ void
 jsonrpc_session_set_dscp(struct jsonrpc_session *s,
                          uint8_t dscp)
 {
-    s->dscp = dscp;
+    if (s->dscp != dscp) {
+        s->dscp = dscp;
+        jsonrpc_session_force_reconnect(s);
+    }
 }
index 16131d3..cd78170 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 9d6fb9b..374d915 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 Nicira Networks
+/* Copyright (c) 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.
@@ -362,6 +362,14 @@ lacp_slave_carrier_changed(const struct lacp *lacp, const void *slave_)
     }
 }
 
+static bool
+slave_may_enable__(struct slave *slave)
+{
+    /* The slave may be enabled if it's attached to an aggregator and its
+     * partner is synchronized.*/
+    return slave->attached && (slave->partner.state & LACP_STATE_SYNC);
+}
+
 /* This function should be called before enabling 'slave_' to send or receive
  * traffic.  If it returns false, 'slave_' should not enabled.  As a
  * convenience, returns true if 'lacp' is NULL. */
@@ -369,11 +377,7 @@ bool
 lacp_slave_may_enable(const struct lacp *lacp, const void *slave_)
 {
     if (lacp) {
-        struct slave *slave = slave_lookup(lacp, slave_);
-
-        /* The slave may be enabled if it's attached to an aggregator and its
-         * partner is synchronized.*/
-        return slave->attached && (slave->partner.state & LACP_STATE_SYNC);
+        return slave_may_enable__(slave_lookup(lacp, slave_));
     } else {
         return true;
     }
@@ -788,6 +792,8 @@ lacp_print_details(struct ds *ds, struct lacp *lacp)
                       slave->attached ? "attached" : "detached");
         ds_put_format(ds, "\tport_id: %u\n", slave->port_id);
         ds_put_format(ds, "\tport_priority: %u\n", slave->port_priority);
+        ds_put_format(ds, "\tmay_enable: %s\n", (slave_may_enable__(slave)
+                                                 ? "true" : "false"));
 
         ds_put_format(ds, "\n\tactor sys_id: " ETH_ADDR_FMT "\n",
                       ETH_ADDR_ARGS(actor.sys_id));
index 5e19826..624e368 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 42b3818..1fd3d5b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * 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.
index f6ee5c1..e74cd9d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * 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.
index 7f30f6e..28b8012 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 #include "dynamic-string.h"
 #include "meta-flow.h"
 #include "nx-match.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
@@ -46,19 +47,6 @@ get_be32(const void **pp)
     return value;
 }
 
-static uint64_t
-get_bits(int n_bits, const void **p)
-{
-    int n_segs = DIV_ROUND_UP(n_bits, 16);
-    uint64_t value;
-
-    value = 0;
-    while (n_segs-- > 0) {
-        value = (value << 16) | ntohs(get_be16(p));
-    }
-    return value;
-}
-
 static void
 get_subfield(int n_bits, const void **p, struct mf_subfield *sf)
 {
@@ -90,105 +78,85 @@ learn_min_len(uint16_t header)
     return min_len;
 }
 
-static enum ofperr
-learn_check_header(uint16_t header, size_t len)
+/* Converts 'nal' into a "struct ofpact_learn" and appends that struct to
+ * 'ofpacts'.  Returns 0 if successful, otherwise an OFPERR_*. */
+enum ofperr
+learn_from_openflow(const struct nx_action_learn *nal, struct ofpbuf *ofpacts)
 {
-    int src_type = header & NX_LEARN_SRC_MASK;
-    int dst_type = header & NX_LEARN_DST_MASK;
+    struct ofpact_learn *learn;
+    const void *p, *end;
 
-    /* Check for valid src and dst type combination. */
-    if (dst_type == NX_LEARN_DST_MATCH ||
-        dst_type == NX_LEARN_DST_LOAD ||
-        (dst_type == NX_LEARN_DST_OUTPUT &&
-         src_type == NX_LEARN_SRC_FIELD)) {
-        /* OK. */
-    } else {
+    if (nal->pad) {
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
-    /* Check that the arguments don't overrun the end of the action. */
-    if (len < learn_min_len(header)) {
-        return OFPERR_OFPBAC_BAD_LEN;
-    }
+    learn = ofpact_put_LEARN(ofpacts);
 
-    return 0;
-}
-
-/* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a
- * valid action on 'flow'. */
-enum ofperr
-learn_check(const struct nx_action_learn *learn, const struct flow *flow)
-{
-    struct cls_rule rule;
-    const void *p, *end;
-
-    cls_rule_init_catchall(&rule, 0);
+    learn->idle_timeout = ntohs(nal->idle_timeout);
+    learn->hard_timeout = ntohs(nal->hard_timeout);
+    learn->priority = ntohs(nal->priority);
+    learn->cookie = ntohll(nal->cookie);
+    learn->flags = ntohs(nal->flags);
+    learn->table_id = nal->table_id;
+    learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout);
+    learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout);
 
-    if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM)
-        || learn->pad
-        || learn->table_id == 0xff) {
+    if (learn->flags & ~OFPFF_SEND_FLOW_REM || learn->table_id == 0xff) {
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
-    end = (char *) learn + ntohs(learn->len);
-    for (p = learn + 1; p != end; ) {
+    end = (char *) nal + ntohs(nal->len);
+    for (p = nal + 1; p != end; ) {
+        struct ofpact_learn_spec *spec;
         uint16_t header = ntohs(get_be16(&p));
-        int n_bits = header & NX_LEARN_N_BITS_MASK;
-        int src_type = header & NX_LEARN_SRC_MASK;
-        int dst_type = header & NX_LEARN_DST_MASK;
-
-        enum ofperr error;
-        uint64_t value;
 
         if (!header) {
             break;
         }
 
-        error = learn_check_header(header, (char *) end - (char *) p);
-        if (error) {
-            return error;
-        }
+        spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
+        learn = ofpacts->l2;
+        learn->n_specs++;
 
-        /* Check the source. */
-        if (src_type == NX_LEARN_SRC_FIELD) {
-            struct mf_subfield src;
+        spec->src_type = header & NX_LEARN_SRC_MASK;
+        spec->dst_type = header & NX_LEARN_DST_MASK;
+        spec->n_bits = header & NX_LEARN_N_BITS_MASK;
 
-            get_subfield(n_bits, &p, &src);
-            error = mf_check_src(&src, flow);
-            if (error) {
-                return error;
-            }
-            value = 0;
+        /* Check for valid src and dst type combination. */
+        if (spec->dst_type == NX_LEARN_DST_MATCH ||
+            spec->dst_type == NX_LEARN_DST_LOAD ||
+            (spec->dst_type == NX_LEARN_DST_OUTPUT &&
+             spec->src_type == NX_LEARN_SRC_FIELD)) {
+            /* OK. */
         } else {
-            value = get_bits(n_bits, &p);
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
         }
 
-        /* Check the destination. */
-        if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
-            struct mf_subfield dst;
+        /* Check that the arguments don't overrun the end of the action. */
+        if ((char *) end - (char *) p < learn_min_len(header)) {
+            return OFPERR_OFPBAC_BAD_LEN;
+        }
 
-            get_subfield(n_bits, &p, &dst);
-            error = (dst_type == NX_LEARN_DST_LOAD
-                     ? mf_check_dst(&dst, &rule.flow)
-                     : mf_check_src(&dst, &rule.flow));
-            if (error) {
-                return error;
-            }
+        /* Get the source. */
+        if (spec->src_type == NX_LEARN_SRC_FIELD) {
+            get_subfield(spec->n_bits, &p, &spec->src);
+        } else {
+            int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
 
-            if (dst_type == NX_LEARN_DST_MATCH
-                && src_type == NX_LEARN_SRC_IMMEDIATE) {
-                if (n_bits <= 64) {
-                    mf_set_subfield(&dst, value, &rule);
-                } else {
-                    /* We're only setting subfields to allow us to check
-                     * prerequisites.  No prerequisite depends on the value of
-                     * a field that is wider than 64 bits.  So just skip
-                     * setting it entirely. */
-                    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
-                }
-            }
+            bitwise_copy(p, p_bytes, 0,
+                         &spec->src_imm, sizeof spec->src_imm, 0,
+                         spec->n_bits);
+            p = (const uint8_t *) p + p_bytes;
+        }
+
+        /* Get the destination. */
+        if (spec->dst_type == NX_LEARN_DST_MATCH ||
+            spec->dst_type == NX_LEARN_DST_LOAD) {
+            get_subfield(spec->n_bits, &p, &spec->dst);
         }
     }
+    ofpact_update_len(ofpacts, &learn->ofpact);
+
     if (!is_all_zeros(p, (char *) end - (char *) p)) {
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
@@ -196,97 +164,50 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
     return 0;
 }
 
-void
-learn_execute(const struct nx_action_learn *learn, const struct flow *flow,
-              struct ofputil_flow_mod *fm)
+/* Checks that 'learn' is a valid action on 'flow'.  Returns 0 if it is valid,
+ * otherwise an OFPERR_*. */
+enum ofperr
+learn_check(const struct ofpact_learn *learn, const struct flow *flow)
 {
-    const void *p, *end;
-    struct ofpbuf actions;
-
-    cls_rule_init_catchall(&fm->cr, ntohs(learn->priority));
-    fm->cookie = learn->cookie;
-    fm->cookie_mask = htonll(UINT64_MAX);
-    fm->table_id = learn->table_id;
-    fm->command = OFPFC_MODIFY_STRICT;
-    fm->idle_timeout = ntohs(learn->idle_timeout);
-    fm->hard_timeout = ntohs(learn->hard_timeout);
-    fm->buffer_id = UINT32_MAX;
-    fm->out_port = OFPP_NONE;
-    fm->flags = ntohs(learn->flags) & OFPFF_SEND_FLOW_REM;
-    fm->actions = NULL;
-    fm->n_actions = 0;
-
-    ofpbuf_init(&actions, 64);
-
-    if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
-        struct nx_action_fin_timeout *naft;
-
-        naft = ofputil_put_NXAST_FIN_TIMEOUT(&actions);
-        naft->fin_idle_timeout = learn->fin_idle_timeout;
-        naft->fin_hard_timeout = learn->fin_hard_timeout;
-    }
-
-    for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) {
-        uint16_t header = ntohs(get_be16(&p));
-        int n_bits = header & NX_LEARN_N_BITS_MASK;
-        int src_type = header & NX_LEARN_SRC_MASK;
-        int dst_type = header & NX_LEARN_DST_MASK;
-        union mf_subvalue value;
-
-        struct mf_subfield dst;
-        int chunk, ofs;
-
-        if (!header) {
-            break;
-        }
-
-        if (src_type == NX_LEARN_SRC_FIELD) {
-            struct mf_subfield src;
+    const struct ofpact_learn_spec *spec;
+    struct cls_rule rule;
 
-            get_subfield(n_bits, &p, &src);
-            mf_read_subfield(&src, flow, &value);
-        } else {
-            int p_bytes = 2 * DIV_ROUND_UP(n_bits, 16);
+    cls_rule_init_catchall(&rule, 0);
+    for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+        enum ofperr error;
 
-            memset(&value, 0, sizeof value);
-            bitwise_copy(p, p_bytes, 0,
-                         &value, sizeof value, 0,
-                         n_bits);
-            p = (const uint8_t *) p + p_bytes;
+        /* Check the source. */
+        if (spec->src_type == NX_LEARN_SRC_FIELD) {
+            error = mf_check_src(&spec->src, flow);
+            if (error) {
+                return error;
+            }
         }
 
-        switch (dst_type) {
+        /* Check the destination. */
+        switch (spec->dst_type) {
         case NX_LEARN_DST_MATCH:
-            get_subfield(n_bits, &p, &dst);
-            mf_write_subfield(&dst, &value, &fm->cr);
+            error = mf_check_src(&spec->dst, &rule.flow);
+            if (error) {
+                return error;
+            }
+
+            mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
             break;
 
         case NX_LEARN_DST_LOAD:
-            get_subfield(n_bits, &p, &dst);
-            for (ofs = 0; ofs < n_bits; ofs += chunk) {
-                struct nx_action_reg_load *load;
-
-                chunk = MIN(n_bits - ofs, 64);
-
-                load = ofputil_put_NXAST_REG_LOAD(&actions);
-                load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs + ofs, chunk);
-                load->dst = htonl(dst.field->nxm_header);
-                bitwise_copy(&value, sizeof value, ofs,
-                             &load->value, sizeof load->value, 0,
-                             chunk);
+            error = mf_check_dst(&spec->dst, &rule.flow);
+            if (error) {
+                return error;
             }
             break;
 
         case NX_LEARN_DST_OUTPUT:
-            if (n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) {
-                ofputil_put_OFPAT10_OUTPUT(&actions)->port = value.be16[7];
-            }
+            /* Nothing to do. */
             break;
         }
     }
-
-    fm->actions = ofpbuf_steal_data(&actions);
-    fm->n_actions = actions.size / sizeof(struct ofp_action_header);
+    return 0;
 }
 
 static void
@@ -313,19 +234,150 @@ put_u32(struct ofpbuf *b, uint32_t x)
     put_be32(b, htonl(x));
 }
 
-struct learn_spec {
-    int n_bits;
+/* Converts 'learn' into a "struct nx_action_learn" and appends that action to
+ * 'ofpacts'. */
+void
+learn_to_nxast(const struct ofpact_learn *learn, struct ofpbuf *openflow)
+{
+    const struct ofpact_learn_spec *spec;
+    struct nx_action_learn *nal;
+    size_t start_ofs;
+
+    start_ofs = openflow->size;
+    nal = ofputil_put_NXAST_LEARN(openflow);
+    nal->idle_timeout = htons(learn->idle_timeout);
+    nal->hard_timeout = htons(learn->hard_timeout);
+    nal->fin_idle_timeout = htons(learn->fin_idle_timeout);
+    nal->fin_hard_timeout = htons(learn->fin_hard_timeout);
+    nal->priority = htons(learn->priority);
+    nal->cookie = htonll(learn->cookie);
+    nal->flags = htons(learn->flags);
+    nal->table_id = learn->table_id;
+
+    for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+        put_u16(openflow, spec->n_bits | spec->dst_type | spec->src_type);
+
+        if (spec->src_type == NX_LEARN_SRC_FIELD) {
+            put_u32(openflow, spec->src.field->nxm_header);
+            put_u16(openflow, spec->src.ofs);
+        } else {
+            size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
+            uint8_t *bits = ofpbuf_put_zeros(openflow, n_dst_bytes);
+            bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0,
+                         bits, n_dst_bytes, 0,
+                         spec->n_bits);
+        }
+
+        if (spec->dst_type == NX_LEARN_DST_MATCH ||
+            spec->dst_type == NX_LEARN_DST_LOAD) {
+            put_u32(openflow, spec->dst.field->nxm_header);
+            put_u16(openflow, spec->dst.ofs);
+        }
+    }
+
+    if ((openflow->size - start_ofs) % 8) {
+        ofpbuf_put_zeros(openflow, 8 - (openflow->size - start_ofs) % 8);
+    }
+
+    nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal);
+    nal->len = htons(openflow->size - start_ofs);
+}
+
+/* Composes 'fm' so that executing it will implement 'learn' given that the
+ * packet being processed has 'flow' as its flow.
+ *
+ * Uses 'ofpacts' to store the flow mod's actions.  The caller must initialize
+ * 'ofpacts' and retains ownership of it.  'fm->ofpacts' will point into the
+ * 'ofpacts' buffer.
+ *
+ * The caller has to actually execute 'fm'. */
+void
+learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
+              struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts)
+{
+    const struct ofpact_learn_spec *spec;
 
-    int src_type;
-    struct mf_subfield src;
-    union mf_subvalue src_imm;
+    cls_rule_init_catchall(&fm->cr, learn->priority);
+    fm->cookie = htonll(0);
+    fm->cookie_mask = htonll(0);
+    fm->new_cookie = htonll(learn->cookie);
+    fm->table_id = learn->table_id;
+    fm->command = OFPFC_MODIFY_STRICT;
+    fm->idle_timeout = learn->idle_timeout;
+    fm->hard_timeout = learn->hard_timeout;
+    fm->buffer_id = UINT32_MAX;
+    fm->out_port = OFPP_NONE;
+    fm->flags = learn->flags;
+    fm->ofpacts = NULL;
+    fm->ofpacts_len = 0;
 
-    int dst_type;
-    struct mf_subfield dst;
-};
+    if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
+        struct ofpact_fin_timeout *oft;
+
+        oft = ofpact_put_FIN_TIMEOUT(ofpacts);
+        oft->fin_idle_timeout = learn->fin_idle_timeout;
+        oft->fin_hard_timeout = learn->fin_hard_timeout;
+    }
+
+    for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+        union mf_subvalue value;
+        int chunk, ofs;
+
+        if (spec->src_type == NX_LEARN_SRC_FIELD) {
+            mf_read_subfield(&spec->src, flow, &value);
+        } else {
+            value = spec->src_imm;
+        }
+
+        switch (spec->dst_type) {
+        case NX_LEARN_DST_MATCH:
+            mf_write_subfield(&spec->dst, &value, &fm->cr);
+            break;
+
+        case NX_LEARN_DST_LOAD:
+            for (ofs = 0; ofs < spec->n_bits; ofs += chunk) {
+                struct ofpact_reg_load *load;
+                ovs_be64 value_be;
+
+                chunk = MIN(spec->n_bits - ofs, 64);
+
+                load = ofpact_put_REG_LOAD(ofpacts);
+                load->dst.field = spec->dst.field;
+                load->dst.ofs = spec->dst.ofs + ofs;
+                load->dst.n_bits = chunk;
+
+                memset(&value_be, 0, sizeof value_be);
+                bitwise_copy(&value, sizeof value, ofs,
+                             &value_be, sizeof value_be, 0,
+                             chunk);
+                load->value = ntohll(value_be);
+            }
+            break;
+
+        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]);
+
+                if (port < OFPP_MAX
+                    || port == OFPP_IN_PORT
+                    || port == OFPP_FLOOD
+                    || port == OFPP_LOCAL
+                    || port == OFPP_ALL) {
+                    ofpact_put_OUTPUT(ofpacts)->port = port;
+                }
+            }
+            break;
+        }
+    }
+    ofpact_pad(ofpacts);
+
+    fm->ofpacts = ofpacts->data;
+    fm->ofpacts_len = ofpacts->size;
+}
 
 static void
-learn_parse_load_immediate(const char *s, struct learn_spec *spec)
+learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
 {
     const char *full_s = s;
     const char *arrow = strstr(s, "->");
@@ -376,9 +428,8 @@ learn_parse_load_immediate(const char *s, struct learn_spec *spec)
 
 static void
 learn_parse_spec(const char *orig, char *name, char *value,
-                 struct learn_spec *spec)
+                 struct ofpact_learn_spec *spec)
 {
-    memset(spec, 0, sizeof *spec);
     if (mf_from_name(name)) {
         const struct mf_field *dst = mf_from_name(name);
         union mf_value imm;
@@ -427,17 +478,15 @@ learn_parse_spec(const char *orig, char *name, char *value,
         if (value[strcspn(value, "[-")] == '-') {
             learn_parse_load_immediate(value, spec);
         } else {
-            struct nx_action_reg_move move;
+            struct ofpact_reg_move move;
 
             nxm_parse_reg_move(&move, value);
 
-            spec->n_bits = ntohs(move.n_bits);
+            spec->n_bits = move.src.n_bits;
             spec->src_type = NX_LEARN_SRC_FIELD;
-            nxm_decode_discrete(&spec->src,
-                                move.src, move.src_ofs, move.n_bits);
+            spec->src = move.src;
             spec->dst_type = NX_LEARN_DST_LOAD;
-            nxm_decode_discrete(&spec->dst,
-                                move.dst, move.dst_ofs, move.n_bits);
+            spec->dst = move.dst;
         }
     } else if (!strcmp(name, "output")) {
         if (mf_parse_subfield(&spec->src, value)[0] != '\0') {
@@ -454,8 +503,8 @@ learn_parse_spec(const char *orig, char *name, char *value,
 }
 
 /* Parses 'arg' as a set of arguments to the "learn" action and appends a
- * matching NXAST_LEARN action to 'b'.  The format parsed is described in
- * ovs-ofctl(8).
+ * matching OFPACT_LEARN action to 'ofpacts'.  ovs-ofctl(8) describes the
+ * format parsed.
  *
  * Prints an error on stderr and aborts the program if 'arg' syntax is invalid.
  *
@@ -465,29 +514,23 @@ learn_parse_spec(const char *orig, char *name, char *value,
  *
  * Modifies 'arg'. */
 void
-learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
+learn_parse(char *arg, const struct flow *flow, struct ofpbuf *ofpacts)
 {
     char *orig = xstrdup(arg);
     char *name, *value;
-    enum ofperr error;
-    size_t learn_ofs;
-    size_t len;
 
-    struct nx_action_learn *learn;
+    struct ofpact_learn *learn;
     struct cls_rule rule;
+    enum ofperr error;
 
-    learn_ofs = b->size;
-    learn = ofputil_put_NXAST_LEARN(b);
-    learn->idle_timeout = htons(OFP_FLOW_PERMANENT);
-    learn->hard_timeout = htons(OFP_FLOW_PERMANENT);
-    learn->priority = htons(OFP_DEFAULT_PRIORITY);
-    learn->cookie = htonll(0);
-    learn->flags = htons(0);
+    learn = ofpact_put_LEARN(ofpacts);
+    learn->idle_timeout = OFP_FLOW_PERMANENT;
+    learn->hard_timeout = OFP_FLOW_PERMANENT;
+    learn->priority = OFP_DEFAULT_PRIORITY;
     learn->table_id = 1;
 
     cls_rule_init_catchall(&rule, 0);
     while (ofputil_parse_key_value(&arg, &name, &value)) {
-        learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn);
         if (!strcmp(name, "table")) {
             learn->table_id = atoi(value);
             if (learn->table_id == 255) {
@@ -495,73 +538,50 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
                           orig);
             }
         } else if (!strcmp(name, "priority")) {
-            learn->priority = htons(atoi(value));
+            learn->priority = atoi(value);
         } else if (!strcmp(name, "idle_timeout")) {
-            learn->idle_timeout = htons(atoi(value));
+            learn->idle_timeout = atoi(value);
         } else if (!strcmp(name, "hard_timeout")) {
-            learn->hard_timeout = htons(atoi(value));
+            learn->hard_timeout = atoi(value);
         } else if (!strcmp(name, "fin_idle_timeout")) {
-            learn->fin_idle_timeout = htons(atoi(value));
+            learn->fin_idle_timeout = atoi(value);
         } else if (!strcmp(name, "fin_hard_timeout")) {
-            learn->fin_hard_timeout = htons(atoi(value));
+            learn->fin_hard_timeout = atoi(value);
         } else if (!strcmp(name, "cookie")) {
-            learn->cookie = htonll(strtoull(value, NULL, 0));
+            learn->cookie = strtoull(value, NULL, 0);
         } else {
-            struct learn_spec spec;
+            struct ofpact_learn_spec *spec;
+
+            spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
+            learn = ofpacts->l2;
+            learn->n_specs++;
 
-            learn_parse_spec(orig, name, value, &spec);
+            learn_parse_spec(orig, name, value, spec);
 
             /* Check prerequisites. */
-            if (spec.src_type == NX_LEARN_SRC_FIELD
-                && flow && !mf_are_prereqs_ok(spec.src.field, flow)) {
+            if (spec->src_type == NX_LEARN_SRC_FIELD
+                && flow && !mf_are_prereqs_ok(spec->src.field, flow)) {
                 ovs_fatal(0, "%s: cannot specify source field %s because "
                           "prerequisites are not satisfied",
-                          orig, spec.src.field->name);
+                          orig, spec->src.field->name);
             }
-            if ((spec.dst_type == NX_LEARN_DST_MATCH
-                 || spec.dst_type == NX_LEARN_DST_LOAD)
-                && !mf_are_prereqs_ok(spec.dst.field, &rule.flow)) {
+            if ((spec->dst_type == NX_LEARN_DST_MATCH
+                 || spec->dst_type == NX_LEARN_DST_LOAD)
+                && !mf_are_prereqs_ok(spec->dst.field, &rule.flow)) {
                 ovs_fatal(0, "%s: cannot specify destination field %s because "
                           "prerequisites are not satisfied",
-                          orig, spec.dst.field->name);
+                          orig, spec->dst.field->name);
             }
 
             /* Update 'rule' to allow for satisfying destination
              * prerequisites. */
-            if (spec.src_type == NX_LEARN_SRC_IMMEDIATE
-                && spec.dst_type == NX_LEARN_DST_MATCH) {
-                mf_write_subfield(&spec.dst, &spec.src_imm, &rule);
-            }
-
-            /* Output the flow_mod_spec. */
-            put_u16(b, spec.n_bits | spec.src_type | spec.dst_type);
-            if (spec.src_type == NX_LEARN_SRC_IMMEDIATE) {
-                int n_bytes = DIV_ROUND_UP(spec.n_bits, 16) * 2;
-                int ofs = sizeof spec.src_imm - n_bytes;
-                ofpbuf_put(b, &spec.src_imm.u8[ofs], n_bytes);
-            } else {
-                put_u32(b, spec.src.field->nxm_header);
-                put_u16(b, spec.src.ofs);
-            }
-            if (spec.dst_type == NX_LEARN_DST_MATCH ||
-                spec.dst_type == NX_LEARN_DST_LOAD) {
-                put_u32(b, spec.dst.field->nxm_header);
-                put_u16(b, spec.dst.ofs);
-            } else {
-                assert(spec.dst_type == NX_LEARN_DST_OUTPUT);
+            if (spec->src_type == NX_LEARN_SRC_IMMEDIATE
+                && spec->dst_type == NX_LEARN_DST_MATCH) {
+                mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
             }
         }
     }
-
-    put_u16(b, 0);
-
-    len = b->size - learn_ofs;
-    if (len % 8) {
-        ofpbuf_put_zeros(b, 8 - len % 8);
-    }
-
-    learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn);
-    learn->len = htons(b->size - learn_ofs);
+    ofpact_update_len(ofpacts, &learn->ofpact);
 
     /* In theory the above should have caught any errors, but... */
     if (flow) {
@@ -573,169 +593,106 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
     free(orig);
 }
 
+static void
+format_subvalue(const union mf_subvalue *subvalue, struct ds *s)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(subvalue->u8); i++) {
+        if (subvalue->u8[i]) {
+            ds_put_format(s, "0x%"PRIx8, subvalue->u8[i]);
+            for (i++; i < ARRAY_SIZE(subvalue->u8); i++) {
+                ds_put_format(s, "%02"PRIx8, subvalue->u8[i]);
+            }
+            return;
+        }
+    }
+    ds_put_char(s, '0');
+}
+
+/* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
+ * describes. */
 void
-learn_format(const struct nx_action_learn *learn, struct ds *s)
+learn_format(const struct ofpact_learn *learn, struct ds *s)
 {
+    const struct ofpact_learn_spec *spec;
     struct cls_rule rule;
-    const void *p, *end;
 
     cls_rule_init_catchall(&rule, 0);
 
     ds_put_format(s, "learn(table=%"PRIu8, learn->table_id);
-    if (learn->idle_timeout != htons(OFP_FLOW_PERMANENT)) {
-        ds_put_format(s, ",idle_timeout=%"PRIu16, ntohs(learn->idle_timeout));
+    if (learn->idle_timeout != OFP_FLOW_PERMANENT) {
+        ds_put_format(s, ",idle_timeout=%"PRIu16, learn->idle_timeout);
     }
-    if (learn->hard_timeout != htons(OFP_FLOW_PERMANENT)) {
-        ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout));
+    if (learn->hard_timeout != OFP_FLOW_PERMANENT) {
+        ds_put_format(s, ",hard_timeout=%"PRIu16, learn->hard_timeout);
     }
     if (learn->fin_idle_timeout) {
-        ds_put_format(s, ",fin_idle_timeout=%"PRIu16,
-                      ntohs(learn->fin_idle_timeout));
+        ds_put_format(s, ",fin_idle_timeout=%"PRIu16, learn->fin_idle_timeout);
     }
     if (learn->fin_hard_timeout) {
-        ds_put_format(s, ",fin_hard_timeout=%"PRIu16,
-                      ntohs(learn->fin_hard_timeout));
+        ds_put_format(s, ",fin_hard_timeout=%"PRIu16, learn->fin_hard_timeout);
     }
-    if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) {
-        ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority));
+    if (learn->priority != OFP_DEFAULT_PRIORITY) {
+        ds_put_format(s, ",priority=%"PRIu16, learn->priority);
     }
-    if (learn->flags & htons(OFPFF_SEND_FLOW_REM)) {
+    if (learn->flags & OFPFF_SEND_FLOW_REM) {
         ds_put_cstr(s, ",OFPFF_SEND_FLOW_REM");
     }
-    if (learn->flags & htons(~OFPFF_SEND_FLOW_REM)) {
-        ds_put_format(s, ",***flags=%"PRIu16"***",
-                      ntohs(learn->flags) & ~OFPFF_SEND_FLOW_REM);
-    }
-    if (learn->cookie != htonll(0)) {
-        ds_put_format(s, ",cookie=0x%"PRIx64, ntohll(learn->cookie));
+    if (learn->cookie != 0) {
+        ds_put_format(s, ",cookie=%#"PRIx64, learn->cookie);
     }
-    if (learn->pad != 0) {
-        ds_put_cstr(s, ",***nonzero pad***");
-    }
-
-    end = (char *) learn + ntohs(learn->len);
-    for (p = learn + 1; p != end; ) {
-        uint16_t header = ntohs(get_be16(&p));
-        int n_bits = header & NX_LEARN_N_BITS_MASK;
-
-        int src_type = header & NX_LEARN_SRC_MASK;
-        struct mf_subfield src;
-        const uint8_t *src_value;
-        int src_value_bytes;
-
-        int dst_type = header & NX_LEARN_DST_MASK;
-        struct mf_subfield dst;
-
-        enum ofperr error;
-        int i;
-
-        if (!header) {
-            break;
-        }
-
-        error = learn_check_header(header, (char *) end - (char *) p);
-        if (error == OFPERR_OFPBAC_BAD_ARGUMENT) {
-            ds_put_format(s, ",***bad flow_mod_spec header %"PRIx16"***)",
-                          header);
-            return;
-        } else if (error == OFPERR_OFPBAC_BAD_LEN) {
-            ds_put_format(s, ",***flow_mod_spec at offset %td is %u bytes "
-                          "long but only %td bytes are left***)",
-                          (char *) p - (char *) (learn + 1) - 2,
-                          learn_min_len(header) + 2,
-                          (char *) end - (char *) p + 2);
-            return;
-        }
-        assert(!error);
-
-        /* Get the source. */
-        if (src_type == NX_LEARN_SRC_FIELD) {
-            get_subfield(n_bits, &p, &src);
-            src_value_bytes = 0;
-            src_value = NULL;
-        } else {
-            src.field = NULL;
-            src.ofs = 0;
-            src.n_bits = 0;
-            src_value_bytes = 2 * DIV_ROUND_UP(n_bits, 16);
-            src_value = p;
-            p = (const void *) ((const uint8_t *) p + src_value_bytes);
-        }
-
-        /* Get the destination. */
-        if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
-            get_subfield(n_bits, &p, &dst);
-        } else {
-            dst.field = NULL;
-            dst.ofs = 0;
-            dst.n_bits = 0;
-        }
 
+    for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
         ds_put_char(s, ',');
 
-        switch (src_type | dst_type) {
+        switch (spec->src_type | spec->dst_type) {
         case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH:
-            if (dst.field && dst.ofs == 0 && n_bits == dst.field->n_bits) {
+            if (spec->dst.ofs == 0
+                && spec->dst.n_bits == spec->dst.field->n_bits) {
                 union mf_value value;
-                uint8_t *bytes = (uint8_t *) &value;
-
-                if (src_value_bytes > dst.field->n_bytes) {
-                    /* The destination field is an odd number of bytes, which
-                     * got rounded up to a multiple of 2 to be put into the
-                     * learning action.  Skip over the leading byte, which
-                     * should be zero anyway.  Otherwise the memcpy() below
-                     * will overrun the start of 'value'. */
-                    int diff = src_value_bytes - dst.field->n_bytes;
-                    src_value += diff;
-                    src_value_bytes -= diff;
-                }
 
                 memset(&value, 0, sizeof value);
-                memcpy(&bytes[dst.field->n_bytes - src_value_bytes],
-                       src_value, src_value_bytes);
-                ds_put_format(s, "%s=", dst.field->name);
-                mf_format(dst.field, &value, NULL, s);
+                bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0,
+                             &value, spec->dst.field->n_bytes, 0,
+                             spec->dst.field->n_bits);
+                ds_put_format(s, "%s=", spec->dst.field->name);
+                mf_format(spec->dst.field, &value, NULL, s);
             } else {
-                mf_format_subfield(&dst, s);
-                ds_put_cstr(s, "=0x");
-                for (i = 0; i < src_value_bytes; i++) {
-                    ds_put_format(s, "%02"PRIx8, src_value[i]);
-                }
+                mf_format_subfield(&spec->dst, s);
+                ds_put_char(s, '=');
+                format_subvalue(&spec->src_imm, s);
             }
             break;
 
         case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH:
-            mf_format_subfield(&dst, s);
-            if (src.field != dst.field || src.ofs != dst.ofs) {
+            mf_format_subfield(&spec->dst, s);
+            if (spec->src.field != spec->dst.field ||
+                spec->src.ofs != spec->dst.ofs) {
                 ds_put_char(s, '=');
-                mf_format_subfield(&src, s);
+                mf_format_subfield(&spec->src, s);
             }
             break;
 
         case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD:
-            ds_put_cstr(s, "load:0x");
-            for (i = 0; i < src_value_bytes; i++) {
-                ds_put_format(s, "%02"PRIx8, src_value[i]);
-            }
+            ds_put_format(s, "load:");
+            format_subvalue(&spec->src_imm, s);
             ds_put_cstr(s, "->");
-            mf_format_subfield(&dst, s);
+            mf_format_subfield(&spec->dst, s);
             break;
 
         case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD:
             ds_put_cstr(s, "load:");
-            mf_format_subfield(&src, s);
+            mf_format_subfield(&spec->src, s);
             ds_put_cstr(s, "->");
-            mf_format_subfield(&dst, s);
+            mf_format_subfield(&spec->dst, s);
             break;
 
         case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT:
             ds_put_cstr(s, "output:");
-            mf_format_subfield(&src, s);
+            mf_format_subfield(&spec->src, s);
             break;
         }
     }
-    if (!is_all_zeros(p, (char *) end - (char *) p)) {
-        ds_put_cstr(s, ",***nonzero trailer***");
-    }
     ds_put_char(s, ')');
 }
index b83bee2..adf597e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 struct ds;
 struct flow;
 struct ofpbuf;
+struct ofpact_learn;
 struct ofputil_flow_mod;
 struct nx_action_learn;
 
@@ -30,11 +31,15 @@ struct nx_action_learn;
  * See include/openflow/nicira-ext.h for NXAST_LEARN specification.
  */
 
-enum ofperr learn_check(const struct nx_action_learn *, const struct flow *);
-void learn_execute(const struct nx_action_learn *, const struct flow *,
-                   struct ofputil_flow_mod *);
+enum ofperr learn_from_openflow(const struct nx_action_learn *,
+                                struct ofpbuf *ofpacts);
+enum ofperr learn_check(const struct ofpact_learn *, const struct flow *);
+void learn_to_nxast(const struct ofpact_learn *, struct ofpbuf *openflow);
 
-void learn_parse(struct ofpbuf *, char *, const struct flow *);
-void learn_format(const struct nx_action_learn *, struct ds *);
+void learn_execute(const struct ofpact_learn *, const struct flow *,
+                   struct ofputil_flow_mod *, struct ofpbuf *ofpacts);
+
+void learn_parse(char *, const struct flow *, struct ofpbuf *ofpacts);
+void learn_format(const struct ofpact_learn *, struct ds *);
 
 #endif /* learn.h */
index 23d26e7..235e9d4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,7 +29,9 @@
 #include "hmap.h"
 #include "mac-learning.h"
 #include "ofpbuf.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
+#include "ofp-msgs.h"
 #include "ofp-parse.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
@@ -37,6 +39,7 @@
 #include "poll-loop.h"
 #include "rconn.h"
 #include "shash.h"
+#include "simap.h"
 #include "timeval.h"
 #include "vconn.h"
 #include "vlog.h"
@@ -49,14 +52,23 @@ struct lswitch_port {
     uint32_t queue_id;          /* OpenFlow queue number. */
 };
 
+enum lswitch_state {
+    S_CONNECTING,               /* Waiting for connection to complete. */
+    S_FEATURES_REPLY,           /* Waiting for features reply. */
+    S_SWITCHING,                /* Switching flows. */
+};
+
 struct lswitch {
+    struct rconn *rconn;
+    enum lswitch_state state;
+
     /* If nonnegative, the switch sets up flows that expire after the given
      * number of seconds (or never expire, if the value is OFP_FLOW_PERMANENT).
      * Otherwise, the switch processes every packet. */
     int max_idle;
 
+    enum ofputil_protocol protocol;
     unsigned long long int datapath_id;
-    time_t last_features_request;
     struct mac_learning *ml;    /* NULL to act as hub instead of switch. */
     struct flow_wildcards wc;   /* Wildcards to apply to flows. */
     bool action_normal;         /* Use OFPP_NORMAL? */
@@ -68,21 +80,29 @@ struct lswitch {
 
     /* Number of outgoing queued packets on the rconn. */
     struct rconn_packet_counter *queued;
+
+    /* If true, do not reply to any messages from the switch (for debugging
+     * fail-open mode). */
+    bool mute;
+
+    /* Optional "flow mod" requests to send to the switch at connection time,
+     * to set up the flow table. */
+    const struct ofputil_flow_mod *default_flows;
+    size_t n_default_flows;
 };
 
 /* 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);
 
-static void queue_tx(struct lswitch *, struct rconn *, struct ofpbuf *);
-static void send_features_request(struct lswitch *, struct rconn *);
+static void queue_tx(struct lswitch *, struct ofpbuf *);
+static void send_features_request(struct lswitch *);
 
+static void lswitch_process_packet(struct lswitch *, const struct ofpbuf *);
 static enum ofperr process_switch_features(struct lswitch *,
-                                           struct ofp_switch_features *);
-static void process_packet_in(struct lswitch *, struct rconn *,
-                              const struct ofp_packet_in *);
-static void process_echo_request(struct lswitch *, struct rconn *,
-                                 const struct ofp_header *);
+                                           struct ofp_header *);
+static void process_packet_in(struct lswitch *, const struct ofp_header *);
+static void process_echo_request(struct lswitch *, const struct ofp_header *);
 
 /* Creates and returns a new learning switch whose configuration is given by
  * 'cfg'.
@@ -92,64 +112,75 @@ struct lswitch *
 lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
 {
     struct lswitch *sw;
+    uint32_t ofpfw;
 
     sw = xzalloc(sizeof *sw);
+    sw->rconn = rconn;
+    sw->state = S_CONNECTING;
     sw->max_idle = cfg->max_idle;
     sw->datapath_id = 0;
-    sw->last_features_request = time_now() - 1;
     sw->ml = (cfg->mode == LSW_LEARN
               ? mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME)
               : NULL);
     sw->action_normal = cfg->mode == LSW_NORMAL;
 
-    flow_wildcards_init_exact(&sw->wc);
-    if (cfg->wildcards) {
-        uint32_t ofpfw;
-
-        if (cfg->wildcards == UINT32_MAX) {
-            /* Try to wildcard as many fields as possible, but we cannot
-             * wildcard all fields.  We need in_port to detect moves.  We need
-             * Ethernet source and dest and VLAN VID to do L2 learning. */
-            ofpfw = (OFPFW_DL_TYPE | OFPFW_DL_VLAN_PCP
-                     | OFPFW_NW_SRC_ALL | OFPFW_NW_DST_ALL
-                     | OFPFW_NW_TOS | OFPFW_NW_PROTO
-                     | OFPFW_TP_SRC | OFPFW_TP_DST);
-        } else {
-            ofpfw = cfg->wildcards;
-        }
+    switch (cfg->wildcards) {
+    case 0:
+        ofpfw = 0;
+        break;
+
+    case UINT32_MAX:
+        /* Try to wildcard as many fields as possible, but we cannot
+         * wildcard all fields.  We need in_port to detect moves.  We need
+         * Ethernet source and dest and VLAN VID to do L2 learning. */
+        ofpfw = (OFPFW10_DL_TYPE | OFPFW10_DL_VLAN_PCP
+                 | OFPFW10_NW_SRC_ALL | OFPFW10_NW_DST_ALL
+                 | OFPFW10_NW_TOS | OFPFW10_NW_PROTO
+                 | OFPFW10_TP_SRC | OFPFW10_TP_DST);
+        break;
 
-        ofputil_wildcard_from_openflow(ofpfw, &sw->wc);
+    default:
+        ofpfw = cfg->wildcards;
+        break;
     }
+    ofputil_wildcard_from_ofpfw10(ofpfw, &sw->wc);
 
     sw->default_queue = cfg->default_queue;
     hmap_init(&sw->queue_numbers);
     shash_init(&sw->queue_names);
     if (cfg->port_queues) {
-        struct shash_node *node;
+        struct simap_node *node;
 
-        SHASH_FOR_EACH (node, cfg->port_queues) {
+        SIMAP_FOR_EACH (node, cfg->port_queues) {
             struct lswitch_port *port = xmalloc(sizeof *port);
             hmap_node_nullify(&port->hmap_node);
-            port->queue_id = (uintptr_t) node->data;
+            port->queue_id = node->data;
             shash_add(&sw->queue_names, node->name, port);
         }
     }
 
+    sw->default_flows = cfg->default_flows;
+    sw->n_default_flows = cfg->n_default_flows;
+
     sw->queued = rconn_packet_counter_create();
-    send_features_request(sw, rconn);
 
-    if (cfg->default_flows) {
+    return sw;
+}
+
+static void
+lswitch_handshake(struct lswitch *sw)
+{
+    enum ofputil_protocol protocol;
+
+    send_features_request(sw);
+
+    protocol = ofputil_protocol_from_ofp_version(rconn_get_version(sw->rconn));
+    if (sw->default_flows) {
         enum ofputil_protocol usable_protocols;
-        enum ofputil_protocol protocol;
         struct ofpbuf *msg = NULL;
-        int ofp_version;
         int error = 0;
         size_t i;
 
-        /* Figure out the initial protocol on the connection. */
-        ofp_version = rconn_get_version(rconn);
-        protocol = ofputil_protocol_from_ofp_version(ofp_version);
-
         /* If the initial protocol isn't good enough for default_flows, then
          * pick one that will work and encode messages to set up that
          * protocol.
@@ -158,7 +189,7 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
          * flow format with the switch, but that would require an asynchronous
          * state machine.  This version ought to work fine in practice. */
         usable_protocols = ofputil_flow_mod_usable_protocols(
-            cfg->default_flows, cfg->n_default_flows);
+            sw->default_flows, sw->n_default_flows);
         if (!(protocol & usable_protocols)) {
             enum ofputil_protocol want = rightmost_1bit(usable_protocols);
             while (!error) {
@@ -166,22 +197,27 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
                 if (!msg) {
                     break;
                 }
-                error = rconn_send(rconn, msg, NULL);
+                error = rconn_send(sw->rconn, msg, NULL);
             }
         }
 
-        for (i = 0; !error && i < cfg->n_default_flows; i++) {
-            msg = ofputil_encode_flow_mod(&cfg->default_flows[i], protocol);
-            error = rconn_send(rconn, msg, NULL);
+        for (i = 0; !error && i < sw->n_default_flows; i++) {
+            msg = ofputil_encode_flow_mod(&sw->default_flows[i], protocol);
+            error = rconn_send(sw->rconn, msg, NULL);
         }
 
         if (error) {
             VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)",
-                         rconn_get_name(rconn), strerror(error));
+                         rconn_get_name(sw->rconn), strerror(error));
         }
     }
+    sw->protocol = protocol;
+}
 
-    return sw;
+bool
+lswitch_is_alive(const struct lswitch *sw)
+{
+    return rconn_is_alive(sw->rconn);
 }
 
 /* Destroys 'sw'. */
@@ -191,6 +227,7 @@ lswitch_destroy(struct lswitch *sw)
     if (sw) {
         struct lswitch_port *node, *next;
 
+        rconn_destroy(sw->rconn);
         HMAP_FOR_EACH_SAFE (node, next, hmap_node, &sw->queue_numbers) {
             hmap_remove(&sw->queue_numbers, &node->hmap_node);
             free(node);
@@ -207,9 +244,35 @@ lswitch_destroy(struct lswitch *sw)
 void
 lswitch_run(struct lswitch *sw)
 {
+    int i;
+
     if (sw->ml) {
         mac_learning_run(sw->ml, NULL);
     }
+
+    rconn_run(sw->rconn);
+
+    if (sw->state == S_CONNECTING) {
+        if (rconn_get_version(sw->rconn) != -1) {
+            lswitch_handshake(sw);
+            sw->state = S_FEATURES_REPLY;
+        }
+        return;
+    }
+
+    for (i = 0; i < 50; i++) {
+        struct ofpbuf *msg;
+
+        msg = rconn_recv(sw->rconn);
+        if (!msg) {
+            break;
+        }
+
+        if (!sw->mute) {
+            lswitch_process_packet(sw, msg);
+        }
+        ofpbuf_delete(msg);
+    }
 }
 
 void
@@ -218,87 +281,94 @@ lswitch_wait(struct lswitch *sw)
     if (sw->ml) {
         mac_learning_wait(sw->ml);
     }
+    rconn_run_wait(sw->rconn);
+    rconn_recv_wait(sw->rconn);
 }
 
 /* Processes 'msg', which should be an OpenFlow received on 'rconn', according
  * to the learning switch state in 'sw'.  The most likely result of processing
  * is that flow-setup and packet-out OpenFlow messages will be sent out on
  * 'rconn'.  */
-void
-lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
-                       const struct ofpbuf *msg)
+static void
+lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
 {
-    const struct ofp_header *oh = msg->data;
-    const struct ofputil_msg_type *type;
+    enum ofptype type;
+    struct ofpbuf b;
 
-    if (sw->datapath_id == 0
-        && oh->type != OFPT_ECHO_REQUEST
-        && oh->type != OFPT_FEATURES_REPLY) {
-        send_features_request(sw, rconn);
+    b = *msg;
+    if (ofptype_pull(&type, &b)) {
         return;
     }
 
-    ofputil_decode_msg_type(oh, &type);
-    switch (ofputil_msg_type_code(type)) {
-    case OFPUTIL_OFPT_ECHO_REQUEST:
-        process_echo_request(sw, rconn, msg->data);
+    if (sw->state == S_FEATURES_REPLY
+        && type != OFPTYPE_ECHO_REQUEST
+        && type != OFPTYPE_FEATURES_REPLY) {
+        return;
+    }
+
+    switch (type) {
+    case OFPTYPE_ECHO_REQUEST:
+        process_echo_request(sw, msg->data);
         break;
 
-    case OFPUTIL_OFPT_FEATURES_REPLY:
-        process_switch_features(sw, msg->data);
+    case OFPTYPE_FEATURES_REPLY:
+        if (sw->state == S_FEATURES_REPLY) {
+            if (!process_switch_features(sw, msg->data)) {
+                sw->state = S_SWITCHING;
+            } else {
+                rconn_disconnect(sw->rconn);
+            }
+        }
         break;
 
-    case OFPUTIL_OFPT_PACKET_IN:
-        process_packet_in(sw, rconn, msg->data);
+    case OFPTYPE_PACKET_IN:
+        process_packet_in(sw, msg->data);
         break;
 
-    case OFPUTIL_OFPT_FLOW_REMOVED:
+    case OFPTYPE_FLOW_REMOVED:
         /* Nothing to do. */
         break;
 
-    case OFPUTIL_MSG_INVALID:
-    case OFPUTIL_OFPT_HELLO:
-    case OFPUTIL_OFPT_ERROR:
-    case OFPUTIL_OFPT_ECHO_REPLY:
-    case OFPUTIL_OFPT_FEATURES_REQUEST:
-    case OFPUTIL_OFPT_GET_CONFIG_REQUEST:
-    case OFPUTIL_OFPT_GET_CONFIG_REPLY:
-    case OFPUTIL_OFPT_SET_CONFIG:
-    case OFPUTIL_OFPT_PORT_STATUS:
-    case OFPUTIL_OFPT_PACKET_OUT:
-    case OFPUTIL_OFPT_FLOW_MOD:
-    case OFPUTIL_OFPT_PORT_MOD:
-    case OFPUTIL_OFPT_BARRIER_REQUEST:
-    case OFPUTIL_OFPT_BARRIER_REPLY:
-    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
-    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
-    case OFPUTIL_OFPST_DESC_REQUEST:
-    case OFPUTIL_OFPST_FLOW_REQUEST:
-    case OFPUTIL_OFPST_AGGREGATE_REQUEST:
-    case OFPUTIL_OFPST_TABLE_REQUEST:
-    case OFPUTIL_OFPST_PORT_REQUEST:
-    case OFPUTIL_OFPST_QUEUE_REQUEST:
-    case OFPUTIL_OFPST_DESC_REPLY:
-    case OFPUTIL_OFPST_FLOW_REPLY:
-    case OFPUTIL_OFPST_QUEUE_REPLY:
-    case OFPUTIL_OFPST_PORT_REPLY:
-    case OFPUTIL_OFPST_TABLE_REPLY:
-    case OFPUTIL_OFPST_AGGREGATE_REPLY:
-    case OFPUTIL_NXT_ROLE_REQUEST:
-    case OFPUTIL_NXT_ROLE_REPLY:
-    case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
-    case OFPUTIL_NXT_SET_FLOW_FORMAT:
-    case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
-    case OFPUTIL_NXT_PACKET_IN:
-    case OFPUTIL_NXT_FLOW_MOD:
-    case OFPUTIL_NXT_FLOW_REMOVED:
-    case OFPUTIL_NXT_FLOW_AGE:
-    case OFPUTIL_NXT_SET_ASYNC_CONFIG:
-    case OFPUTIL_NXT_SET_CONTROLLER_ID:
-    case OFPUTIL_NXST_FLOW_REQUEST:
-    case OFPUTIL_NXST_AGGREGATE_REQUEST:
-    case OFPUTIL_NXST_FLOW_REPLY:
-    case OFPUTIL_NXST_AGGREGATE_REPLY:
+    case OFPTYPE_HELLO:
+    case OFPTYPE_ERROR:
+    case OFPTYPE_ECHO_REPLY:
+    case OFPTYPE_FEATURES_REQUEST:
+    case OFPTYPE_GET_CONFIG_REQUEST:
+    case OFPTYPE_GET_CONFIG_REPLY:
+    case OFPTYPE_SET_CONFIG:
+    case OFPTYPE_PORT_STATUS:
+    case OFPTYPE_PACKET_OUT:
+    case OFPTYPE_FLOW_MOD:
+    case OFPTYPE_PORT_MOD:
+    case OFPTYPE_BARRIER_REQUEST:
+    case OFPTYPE_BARRIER_REPLY:
+    case OFPTYPE_DESC_STATS_REQUEST:
+    case OFPTYPE_DESC_STATS_REPLY:
+    case OFPTYPE_FLOW_STATS_REQUEST:
+    case OFPTYPE_FLOW_STATS_REPLY:
+    case OFPTYPE_AGGREGATE_STATS_REQUEST:
+    case OFPTYPE_AGGREGATE_STATS_REPLY:
+    case OFPTYPE_TABLE_STATS_REQUEST:
+    case OFPTYPE_TABLE_STATS_REPLY:
+    case OFPTYPE_PORT_STATS_REQUEST:
+    case OFPTYPE_PORT_STATS_REPLY:
+    case OFPTYPE_QUEUE_STATS_REQUEST:
+    case OFPTYPE_QUEUE_STATS_REPLY:
+    case OFPTYPE_PORT_DESC_STATS_REQUEST:
+    case OFPTYPE_PORT_DESC_STATS_REPLY:
+    case OFPTYPE_ROLE_REQUEST:
+    case OFPTYPE_ROLE_REPLY:
+    case OFPTYPE_SET_FLOW_FORMAT:
+    case OFPTYPE_FLOW_MOD_TABLE_ID:
+    case OFPTYPE_SET_PACKET_IN_FORMAT:
+    case OFPTYPE_FLOW_AGE:
+    case OFPTYPE_SET_ASYNC_CONFIG:
+    case OFPTYPE_SET_CONTROLLER_ID:
+    case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+    case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
+    case OFPTYPE_FLOW_MONITOR_CANCEL:
+    case OFPTYPE_FLOW_MONITOR_PAUSED:
+    case OFPTYPE_FLOW_MONITOR_RESUMED:
     default:
         if (VLOG_IS_DBG_ENABLED()) {
             char *s = ofp_to_string(msg->data, msg->size, 2);
@@ -310,51 +380,50 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
 }
 \f
 static void
-send_features_request(struct lswitch *sw, struct rconn *rconn)
+send_features_request(struct lswitch *sw)
 {
-    time_t now = time_now();
-    if (now >= sw->last_features_request + 1) {
-        struct ofpbuf *b;
-        struct ofp_switch_config *osc;
+    struct ofpbuf *b;
+    struct ofp_switch_config *osc;
+    int ofp_version = rconn_get_version(sw->rconn);
 
-        /* Send OFPT_FEATURES_REQUEST. */
-        make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &b);
-        queue_tx(sw, rconn, b);
+    assert(ofp_version > 0 && ofp_version < 0xff);
 
-        /* Send OFPT_SET_CONFIG. */
-        osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &b);
-        osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
-        queue_tx(sw, rconn, b);
+    /* Send OFPT_FEATURES_REQUEST. */
+    b = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, ofp_version, 0);
+    queue_tx(sw, b);
 
-        sw->last_features_request = now;
-    }
+    /* Send OFPT_SET_CONFIG. */
+    b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, ofp_version, sizeof *osc);
+    osc = ofpbuf_put_zeros(b, sizeof *osc);
+    osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
+    queue_tx(sw, b);
 }
 
 static void
-queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b)
+queue_tx(struct lswitch *sw, struct ofpbuf *b)
 {
-    int retval = rconn_send_with_limit(rconn, b, sw->queued, 10);
+    int retval = rconn_send_with_limit(sw->rconn, b, sw->queued, 10);
     if (retval && retval != ENOTCONN) {
         if (retval == EAGAIN) {
             VLOG_INFO_RL(&rl, "%016llx: %s: tx queue overflow",
-                         sw->datapath_id, rconn_get_name(rconn));
+                         sw->datapath_id, rconn_get_name(sw->rconn));
         } else {
             VLOG_WARN_RL(&rl, "%016llx: %s: send: %s",
-                         sw->datapath_id, rconn_get_name(rconn),
+                         sw->datapath_id, rconn_get_name(sw->rconn),
                          strerror(retval));
         }
     }
 }
 
 static enum ofperr
-process_switch_features(struct lswitch *sw, struct ofp_switch_features *osf)
+process_switch_features(struct lswitch *sw, struct ofp_header *oh)
 {
     struct ofputil_switch_features features;
     struct ofputil_phy_port port;
     enum ofperr error;
     struct ofpbuf b;
 
-    error = ofputil_decode_switch_features(osf, &features, &b);
+    error = ofputil_decode_switch_features(oh, &features, &b);
     if (error) {
         VLOG_ERR("received invalid switch feature reply (%s)",
                  ofperr_to_string(error));
@@ -363,7 +432,7 @@ process_switch_features(struct lswitch *sw, struct ofp_switch_features *osf)
 
     sw->datapath_id = features.datapath_id;
 
-    while (!ofputil_pull_switch_features_port(&b, &port)) {
+    while (!ofputil_pull_phy_port(oh->version, &b, &port)) {
         struct lswitch_port *lp = shash_find_data(&sw->queue_names, port.name);
         if (lp && hmap_node_is_null(&lp->hmap_node)) {
             lp->port_no = port.port_no;
@@ -435,68 +504,58 @@ get_queue_id(const struct lswitch *sw, uint16_t in_port)
 }
 
 static void
-process_packet_in(struct lswitch *sw, struct rconn *rconn,
-                  const struct ofp_packet_in *opi)
+process_packet_in(struct lswitch *sw, const struct ofp_header *oh)
 {
-    uint16_t in_port = ntohs(opi->in_port);
+    struct ofputil_packet_in pi;
     uint32_t queue_id;
     uint16_t out_port;
 
-    struct ofp_action_header actions[2];
-    size_t actions_len;
+    uint64_t ofpacts_stub[64 / 8];
+    struct ofpbuf ofpacts;
 
     struct ofputil_packet_out po;
+    enum ofperr error;
 
-    size_t pkt_ofs, pkt_len;
     struct ofpbuf pkt;
     struct flow flow;
 
+    error = ofputil_decode_packet_in(&pi, oh);
+    if (error) {
+        VLOG_WARN_RL(&rl, "failed to decode packet-in: %s",
+                     ofperr_to_string(error));
+        return;
+    }
+
     /* Ignore packets sent via output to OFPP_CONTROLLER.  This library never
      * uses such an action.  You never know what experiments might be going on,
      * though, and it seems best not to interfere with them. */
-    if (opi->reason != OFPR_NO_MATCH) {
+    if (pi.reason != OFPR_NO_MATCH) {
         return;
     }
 
     /* Extract flow data from 'opi' into 'flow'. */
-    pkt_ofs = offsetof(struct ofp_packet_in, data);
-    pkt_len = ntohs(opi->header.length) - pkt_ofs;
-    ofpbuf_use_const(&pkt, opi->data, pkt_len);
-    flow_extract(&pkt, 0, 0, in_port, &flow);
+    ofpbuf_use_const(&pkt, pi.packet, pi.packet_len);
+    flow_extract(&pkt, 0, pi.fmd.tun_id, pi.fmd.in_port, &flow);
 
     /* Choose output port. */
     out_port = lswitch_choose_destination(sw, &flow);
 
     /* Make actions. */
-    queue_id = get_queue_id(sw, in_port);
+    queue_id = get_queue_id(sw, pi.fmd.in_port);
+    ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
     if (out_port == OFPP_NONE) {
-        actions_len = 0;
+        /* No actions. */
     } else if (queue_id == UINT32_MAX || out_port >= OFPP_MAX) {
-        struct ofp_action_output oao;
-
-        memset(&oao, 0, sizeof oao);
-        oao.type = htons(OFPAT10_OUTPUT);
-        oao.len = htons(sizeof oao);
-        oao.port = htons(out_port);
-
-        memcpy(actions, &oao, sizeof oao);
-        actions_len = sizeof oao;
+        ofpact_put_OUTPUT(&ofpacts)->port = out_port;
     } else {
-        struct ofp_action_enqueue oae;
-
-        memset(&oae, 0, sizeof oae);
-        oae.type = htons(OFPAT10_ENQUEUE);
-        oae.len = htons(sizeof oae);
-        oae.port = htons(out_port);
-        oae.queue_id = htonl(queue_id);
-
-        memcpy(actions, &oae, sizeof oae);
-        actions_len = sizeof oae;
+        struct ofpact_enqueue *enqueue = ofpact_put_ENQUEUE(&ofpacts);
+        enqueue->port = out_port;
+        enqueue->queue = queue_id;
     }
-    assert(actions_len <= sizeof actions);
+    ofpact_pad(&ofpacts);
 
     /* Prepare packet_out in case we need one. */
-    po.buffer_id = ntohl(opi->buffer_id);
+    po.buffer_id = pi.buffer_id;
     if (po.buffer_id == UINT32_MAX) {
         po.packet = pkt.data;
         po.packet_len = pkt.size;
@@ -504,39 +563,46 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn,
         po.packet = NULL;
         po.packet_len = 0;
     }
-    po.in_port = in_port;
-    po.actions = (union ofp_action *) actions;
-    po.n_actions = actions_len / sizeof *actions;
+    po.in_port = pi.fmd.in_port;
+    po.ofpacts = ofpacts.data;
+    po.ofpacts_len = ofpacts.size;
 
     /* Send the packet, and possibly the whole flow, to the output port. */
     if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
+        struct ofputil_flow_mod fm;
         struct ofpbuf *buffer;
-        struct cls_rule rule;
 
         /* The output port is known, or we always flood everything, so add a
          * new flow. */
-        cls_rule_init(&flow, &sw->wc, 0, &rule);
-        buffer = make_add_flow(&rule, ntohl(opi->buffer_id),
-                               sw->max_idle, actions_len);
-        ofpbuf_put(buffer, actions, actions_len);
-        queue_tx(sw, rconn, buffer);
+        memset(&fm, 0, sizeof fm);
+        cls_rule_init(&flow, &sw->wc, 0, &fm.cr);
+        ofputil_normalize_rule_quiet(&fm.cr);
+        fm.table_id = 0xff;
+        fm.command = OFPFC_ADD;
+        fm.idle_timeout = sw->max_idle;
+        fm.buffer_id = pi.buffer_id;
+        fm.out_port = OFPP_NONE;
+        fm.ofpacts = ofpacts.data;
+        fm.ofpacts_len = ofpacts.size;
+        buffer = ofputil_encode_flow_mod(&fm, sw->protocol);
+
+        queue_tx(sw, buffer);
 
         /* If the switch didn't buffer the packet, we need to send a copy. */
-        if (ntohl(opi->buffer_id) == UINT32_MAX && actions_len > 0) {
-            queue_tx(sw, rconn, ofputil_encode_packet_out(&po));
+        if (pi.buffer_id == UINT32_MAX && out_port != OFPP_NONE) {
+            queue_tx(sw, ofputil_encode_packet_out(&po, sw->protocol));
         }
     } else {
         /* We don't know that MAC, or we don't set up flows.  Send along the
          * packet without setting up a flow. */
-        if (ntohl(opi->buffer_id) != UINT32_MAX || actions_len > 0) {
-            queue_tx(sw, rconn, ofputil_encode_packet_out(&po));
+        if (pi.buffer_id != UINT32_MAX || out_port != OFPP_NONE) {
+            queue_tx(sw, ofputil_encode_packet_out(&po, sw->protocol));
         }
     }
 }
 
 static void
-process_echo_request(struct lswitch *sw, struct rconn *rconn,
-                     const struct ofp_header *rq)
+process_echo_request(struct lswitch *sw, const struct ofp_header *rq)
 {
-    queue_tx(sw, rconn, make_echo_reply(rq));
+    queue_tx(sw, make_echo_reply(rq));
 }
index 833b94d..dcfb5a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ struct lswitch_config {
     enum lswitch_mode mode;
 
     /* 0 to use exact-match flow entries,
-     * a OFPFW_* bitmask to enable specific wildcards,
+     * a OFPFW10_* bitmask to enable specific wildcards,
      * or UINT32_MAX to use the default wildcards (wildcarding as many fields
      * as possible.
      *
@@ -55,17 +55,21 @@ struct lswitch_config {
      * specifying a particular queue. */
     uint32_t default_queue;
 
-    /* Maps from a port name to a queue_id (cast to void *). */
-    const struct shash *port_queues;
+    /* Maps from a port name to a queue_id. */
+    const struct simap *port_queues;
+
+    /* If true, do not reply to any messages from the switch (for debugging
+     * fail-open mode). */
+    bool mute;
 };
 
 struct lswitch *lswitch_create(struct rconn *, const struct lswitch_config *);
+bool lswitch_is_alive(const struct lswitch *);
 void lswitch_set_queue(struct lswitch *sw, uint32_t queue);
 void lswitch_run(struct lswitch *);
 void lswitch_wait(struct lswitch *);
 void lswitch_destroy(struct lswitch *);
-void lswitch_process_packet(struct lswitch *, struct rconn *,
-                            const struct ofpbuf *);
 
+void lswitch_mute(struct lswitch *);
 
 #endif /* learning-switch.h */
index a236f8c..804a7e5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -136,7 +136,7 @@ list_pop_back(struct list *list)
 struct list *
 list_front(const struct list *list_)
 {
-    struct list *list = (struct list *) list_;
+    struct list *list = CONST_CAST(struct list *, list_);
 
     assert(!list_is_empty(list));
     return list->next;
@@ -147,7 +147,7 @@ list_front(const struct list *list_)
 struct list *
 list_back(const struct list *list_)
 {
-    struct list *list = (struct list *) list_;
+    struct list *list = CONST_CAST(struct list *, list_);
 
     assert(!list_is_empty(list));
     return list->prev;
index 91c3966..8ffa797 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 43ccaf9..db84aeb 100644 (file)
@@ -1,4 +1,4 @@
- /* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks
+ /* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -55,75 +55,64 @@ struct lockfile {
 static struct hmap lock_table = HMAP_INITIALIZER(&lock_table);
 
 static void lockfile_unhash(struct lockfile *);
-static int lockfile_try_lock(const char *name, bool block,
-                             struct lockfile **lockfilep);
+static int lockfile_try_lock(const char *name, struct lockfile **lockfilep);
 
 /* Returns the name of the lockfile that would be created for locking a file
- * named 'file_name'.  The caller is responsible for freeing the returned
- * name, with free(), when it is no longer needed. */
+ * named 'filename_'.  The caller is responsible for freeing the returned name,
+ * with free(), when it is no longer needed. */
 char *
-lockfile_name(const char *file_name)
+lockfile_name(const char *filename_)
 {
-    const char *slash = strrchr(file_name, '/');
-    return (slash
-            ? xasprintf("%.*s/.%s.~lock~",
-                        (int) (slash - file_name), file_name, slash + 1)
-            : xasprintf(".%s.~lock~", file_name));
+    char *filename;
+    const char *slash;
+    char *lockname;
+
+    /* If 'filename_' is a symlink, base the name of the lockfile on the
+     * symlink's target rather than the name of the symlink.  That way, if a
+     * file is symlinked, but there is no symlink for its lockfile, then there
+     * is only a single lockfile for both the source and the target of the
+     * symlink, not one for each. */
+    filename = follow_symlinks(filename_);
+    slash = strrchr(filename, '/');
+    lockname = (slash
+                ? xasprintf("%.*s/.%s.~lock~",
+                            (int) (slash - filename), filename, slash + 1)
+                : xasprintf(".%s.~lock~", filename));
+    free(filename);
+
+    return lockname;
 }
 
 /* Locks the configuration file against modification by other processes and
  * re-reads it from disk.
  *
- * The 'timeout' specifies the maximum number of milliseconds to wait for the
- * config file to become free.  Use 0 to avoid waiting or INT_MAX to wait
- * forever.
- *
  * Returns 0 on success, otherwise a positive errno value.  On success,
  * '*lockfilep' is set to point to a new "struct lockfile *" that may be
  * unlocked with lockfile_unlock().  On failure, '*lockfilep' is set to
- * NULL. */
+ * NULL.  Will not block if the lock cannot be immediately acquired. */
 int
-lockfile_lock(const char *file, int timeout, struct lockfile **lockfilep)
+lockfile_lock(const char *file, struct lockfile **lockfilep)
 {
     /* Only exclusive ("write") locks are supported.  This is not a problem
      * because the Open vSwitch code that currently uses lock files does so in
      * stylized ways such that any number of readers may access a file while it
      * is being written. */
-    long long int warn_elapsed = 1000;
-    long long int start, elapsed;
     char *lock_name;
     int error;
 
     COVERAGE_INC(lockfile_lock);
 
     lock_name = lockfile_name(file);
-    time_refresh();
-    start = time_msec();
-
-    do {
-        error = lockfile_try_lock(lock_name, timeout > 0, lockfilep);
-        time_refresh();
-        elapsed = time_msec() - start;
-        if (elapsed > warn_elapsed) {
-            warn_elapsed *= 2;
-            VLOG_WARN("%s: waiting for lock file, %lld ms elapsed",
-                      lock_name, elapsed);
-        }
-    } while (error == EINTR && (timeout == INT_MAX || elapsed < timeout));
-
-    if (error == EINTR) {
-        COVERAGE_INC(lockfile_timeout);
-        VLOG_WARN("%s: giving up on lock file after %lld ms",
-                  lock_name, elapsed);
-        error = ETIMEDOUT;
-    } else if (error) {
+
+    error = lockfile_try_lock(lock_name, lockfilep);
+
+    if (error) {
         COVERAGE_INC(lockfile_error);
         if (error == EACCES) {
             error = EAGAIN;
         }
-        VLOG_WARN("%s: failed to lock file "
-                  "(after %lld ms, with %d-ms timeout): %s",
-                  lock_name, elapsed, timeout, strerror(error));
+        VLOG_WARN("%s: failed to lock file: %s",
+                  lock_name, strerror(error));
     }
 
     free(lock_name);
@@ -212,7 +201,7 @@ lockfile_register(const char *name, dev_t device, ino_t inode, int fd)
 }
 
 static int
-lockfile_try_lock(const char *name, bool block, struct lockfile **lockfilep)
+lockfile_try_lock(const char *name, struct lockfile **lockfilep)
 {
     struct flock l;
     struct stat s;
@@ -221,41 +210,23 @@ lockfile_try_lock(const char *name, bool block, struct lockfile **lockfilep)
 
     *lockfilep = NULL;
 
-    /* Open the lock file, first creating it if necessary. */
-    for (;;) {
-        /* Check whether we've already got a lock on that file. */
-        if (!stat(name, &s)) {
-            if (lockfile_find(s.st_dev, s.st_ino)) {
-                return EDEADLK;
-            }
-        } else if (errno != ENOENT) {
-            VLOG_WARN("%s: failed to stat lock file: %s",
-                      name, strerror(errno));
-            return errno;
-        }
-
-        /* Try to open an existing lock file. */
-        fd = open(name, O_RDWR);
-        if (fd >= 0) {
-            break;
-        } else if (errno != ENOENT) {
-            VLOG_WARN("%s: failed to open lock file: %s",
-                      name, strerror(errno));
-            return errno;
-        }
-
-        /* Try to create a new lock file. */
-        VLOG_INFO("%s: lock file does not exist, creating", name);
-        fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
-        if (fd >= 0) {
-            break;
-        } else if (errno != EEXIST) {
-            VLOG_WARN("%s: failed to create lock file: %s",
-                      name, strerror(errno));
-            return errno;
+    /* Check whether we've already got a lock on that file. */
+    if (!stat(name, &s)) {
+        if (lockfile_find(s.st_dev, s.st_ino)) {
+            return EDEADLK;
         }
+    } else if (errno != ENOENT) {
+        VLOG_WARN("%s: failed to stat lock file: %s",
+                  name, strerror(errno));
+        return errno;
+    }
 
-        /* Someone else created the lock file.  Try again. */
+    /* Open the lock file. */
+    fd = open(name, O_RDWR | O_CREAT, 0600);
+    if (fd < 0) {
+        VLOG_WARN("%s: failed to open lock file: %s",
+                  name, strerror(errno));
+        return errno;
     }
 
     /* Get the inode and device number for the lock table. */
@@ -273,7 +244,7 @@ lockfile_try_lock(const char *name, bool block, struct lockfile **lockfilep)
     l.l_len = 0;
 
     time_disable_restart();
-    error = fcntl(fd, block ? F_SETLKW : F_SETLK, &l) == -1 ? errno : 0;
+    error = fcntl(fd, F_SETLK, &l) == -1 ? errno : 0;
     time_enable_restart();
 
     if (!error) {
index c52fa21..3e6b54c 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009 Nicira Networks
+/* Copyright (c) 2008, 2009 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,7 +19,7 @@
 struct lockfile;
 
 char *lockfile_name(const char *file);
-int lockfile_lock(const char *file, int timeout, struct lockfile **);
+int lockfile_lock(const char *file, struct lockfile **);
 void lockfile_unlock(struct lockfile *);
 void lockfile_postfork(void);
 
index 68fc621..3c541af 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index df8ea90..8f8fd45 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/lib/memory-unixctl.man b/lib/memory-unixctl.man
new file mode 100644 (file)
index 0000000..5b70297
--- /dev/null
@@ -0,0 +1,7 @@
+.SS "MEMORY COMMANDS"
+These commands report memory usage.
+.
+.IP "\fBmemory/show\fR"
+Displays some basic statistics about \fB\*(PN\fR's memory usage.
+\fB\*(PN\fR also logs this information soon after startup and
+periodically as its memory consumption grows.
diff --git a/lib/memory.c b/lib/memory.c
new file mode 100644 (file)
index 0000000..1babfe9
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "memory.h"
+#include <stdbool.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include "dynamic-string.h"
+#include "poll-loop.h"
+#include "simap.h"
+#include "timeval.h"
+#include "unixctl.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(memory);
+
+/* The number of milliseconds before the first report of daemon memory usage,
+ * and the number of milliseconds between checks for daemon memory growth.  */
+#define MEMORY_CHECK_INTERVAL (10 * 1000)
+
+/* When we should next check memory usage and possibly trigger a report. */
+static long long int next_check;
+
+/* The last time at which we reported memory usage, and the usage we reported
+ * at that time. */
+static long long int last_report;
+static unsigned long int last_reported_maxrss;
+
+/* Are we expecting a call to memory_report()? */
+static bool want_report;
+
+/* Unixctl connections waiting for responses. */
+static struct unixctl_conn **conns;
+static size_t n_conns;
+
+static void memory_init(void);
+
+/* Runs the memory monitor.
+ *
+ * The client should call memory_should_report() afterward. */
+void
+memory_run(void)
+{
+    struct rusage usage;
+    long long int now;
+
+    memory_init();
+
+    /* Time for a check? */
+    now = time_msec();
+    if (now < next_check) {
+        return;
+    }
+    next_check = now + MEMORY_CHECK_INTERVAL;
+
+    /* Time for a report? */
+    getrusage(RUSAGE_SELF, &usage);
+    if (!last_reported_maxrss) {
+        VLOG_INFO("%lu kB peak resident set size after %.1f seconds",
+                  (unsigned long int) usage.ru_maxrss,
+                  (now - time_boot_msec()) / 1000.0);
+    } else if (usage.ru_maxrss >= last_reported_maxrss * 1.5) {
+        VLOG_INFO("peak resident set size grew %.0f%% in last %.1f seconds, "
+                  "from %lu kB to %lu kB",
+                  ((double) usage.ru_maxrss / last_reported_maxrss - 1) * 100,
+                  (now - last_report) / 1000.0,
+                  last_reported_maxrss, (unsigned long int) usage.ru_maxrss);
+    } else {
+        return;
+    }
+
+    /* Request a report. */
+    want_report = true;
+    last_report = now;
+    last_reported_maxrss = usage.ru_maxrss;
+}
+
+/* Causes the poll loop to wake up if the memory monitor needs to run. */
+void
+memory_wait(void)
+{
+    if (memory_should_report()) {
+        poll_immediate_wake();
+    }
+}
+
+/* Returns true if the caller should log some information about memory usage
+ * (with memory_report()), false otherwise. */
+bool
+memory_should_report(void)
+{
+    return want_report || n_conns > 0;
+}
+
+static void
+compose_report(const struct simap *usage, struct ds *s)
+{
+    const struct simap_node **nodes = simap_sort(usage);
+    size_t n = simap_count(usage);
+    size_t i;
+
+    for (i = 0; i < n; i++) {
+        const struct simap_node *node = nodes[i];
+
+        ds_put_format(s, "%s:%u ", node->name, node->data);
+    }
+    ds_chomp(s, ' ');
+    free(nodes);
+}
+
+/* Logs the contents of 'usage', as a collection of name-count pairs.
+ *
+ * 'usage' should capture large-scale statistics that one might reasonably
+ * expect to correlate with memory usage.  For example, each OpenFlow flow
+ * requires some memory, so ovs-vswitchd includes the total number of flows in
+ * 'usage'. */
+void
+memory_report(const struct simap *usage)
+{
+    struct ds s;
+    size_t i;
+
+    ds_init(&s);
+    compose_report(usage, &s);
+
+    if (want_report) {
+        VLOG_INFO("%s", ds_cstr(&s));
+        want_report = false;
+    }
+    if (n_conns) {
+        for (i = 0; i < n_conns; i++) {
+            unixctl_command_reply(conns[i], ds_cstr(&s));
+        }
+        free(conns);
+        conns = NULL;
+        n_conns = 0;
+    }
+
+    ds_destroy(&s);
+}
+
+static void
+memory_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    conns = xrealloc(conns, (n_conns + 1) * sizeof *conns);
+    conns[n_conns++] = conn;
+}
+
+static void
+memory_init(void)
+{
+    static bool inited = false;
+
+    if (!inited) {
+        inited = true;
+        unixctl_command_register("memory/show", "", 0, 0,
+                                 memory_unixctl_show, NULL);
+
+        next_check = time_boot_msec() + MEMORY_CHECK_INTERVAL;
+    }
+}
diff --git a/lib/memory.h b/lib/memory.h
new file mode 100644 (file)
index 0000000..4edd956
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEMORY_H
+#define MEMORY_H 1
+
+/* Memory usage monitor.
+ *
+ * This is intended to be called as part of a daemon's main loop.  After some
+ * time to allow the daemon to allocate an initial memory usage, it logs some
+ * memory usage information (most of which must actually be provided by the
+ * client).  At intervals, if the daemon's memory usage has grown
+ * significantly, it again logs information.
+ *
+ * The monitor also has a unixctl interface.
+ *
+ * Intended usage in the program's main loop is like this:
+ *
+ * for (;;) {
+ *     memory_run();
+ *     if (memory_should_report()) {
+ *          struct simap usage;
+ *
+ *          simap_init(&usage);
+ *          ...fill in 'usage' with meaningful statistics...
+ *          memory_report(&usage);
+ *          simap_destroy(&usage);
+ *     }
+ *
+ *     ...
+ *
+ *     memory_wait();
+ *     poll_block();
+ * }
+ */
+
+#include <stdbool.h>
+
+struct simap;
+
+void memory_run(void);
+void memory_wait(void);
+
+bool memory_should_report(void);
+void memory_report(const struct simap *usage);
+
+#endif /* memory.h */
index 51db7b1..3713ca4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -54,7 +54,16 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         MFP_NONE,
         true,
         NXM_NX_TUN_ID, "NXM_NX_TUN_ID",
-        0, NULL,
+        NXM_NX_TUN_ID, "NXM_NX_TUN_ID",
+    }, {
+        MFF_METADATA, "metadata", NULL,
+        MF_FIELD_SIZES(be64),
+        MFM_FULLY, 0,
+        MFS_HEXADECIMAL,
+        MFP_NONE,
+        true,
+        OXM_OF_METADATA, "OXM_OF_METADATA",
+        OXM_OF_METADATA, "OXM_OF_METADATA",
     }, {
         MFF_IN_PORT, "in_port", NULL,
         MF_FIELD_SIZES(be16),
@@ -74,9 +83,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         MFS_HEXADECIMAL,                        \
         MFP_NONE,                               \
         true,                                   \
-        NXM_NX_REG(IDX),                        \
-        "NXM_NX_REG" #IDX,                      \
-        0, NULL,                                \
+        NXM_NX_REG(IDX), "NXM_NX_REG" #IDX,     \
+        NXM_NX_REG(IDX), "NXM_NX_REG" #IDX,     \
     }
 #if FLOW_N_REGS > 0
     REGISTER(0),
@@ -113,7 +121,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_ETH_SRC, "eth_src", "dl_src",
         MF_FIELD_SIZES(mac),
-        MFM_NONE, FWW_DL_SRC,
+        MFM_FULLY, 0,
         MFS_ETHERNET,
         MFP_NONE,
         true,
@@ -122,7 +130,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ETH_DST, "eth_dst", "dl_dst",
         MF_FIELD_SIZES(mac),
-        MFM_MCAST, 0,
+        MFM_FULLY, 0,
         MFS_ETHERNET,
         MFP_NONE,
         true,
@@ -147,24 +155,42 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         MFP_NONE,
         true,
         NXM_OF_VLAN_TCI, "NXM_OF_VLAN_TCI",
-        0, NULL,
+        NXM_OF_VLAN_TCI, "NXM_OF_VLAN_TCI",
     }, {
-        MFF_VLAN_VID, "dl_vlan", NULL,
+        MFF_DL_VLAN, "dl_vlan", NULL,
         sizeof(ovs_be16), 12,
         MFM_NONE, 0,
         MFS_DECIMAL,
         MFP_NONE,
         true,
         0, NULL,
+        0, NULL,
+    }, {
+        MFF_VLAN_VID, "vlan_vid", NULL,
+        sizeof(ovs_be16), 12,
+        MFM_FULLY, 0,
+        MFS_DECIMAL,
+        MFP_NONE,
+        true,
+        OXM_OF_VLAN_VID, "OXM_OF_VLAN_VID",
         OXM_OF_VLAN_VID, "OXM_OF_VLAN_VID",
     }, {
-        MFF_VLAN_PCP, "dl_vlan_pcp", NULL,
+        MFF_DL_VLAN_PCP, "dl_vlan_pcp", NULL,
         1, 3,
         MFM_NONE, 0,
         MFS_DECIMAL,
         MFP_NONE,
         true,
         0, NULL,
+        0, NULL,
+    }, {
+        MFF_VLAN_PCP, "vlan_pcp", NULL,
+        1, 3,
+        MFM_NONE, 0,
+        MFS_DECIMAL,
+        MFP_VLAN_VID,
+        true,
+        OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP",
         OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP",
     },
 
@@ -175,7 +201,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_IPV4_SRC, "ip_src", "nw_src",
         MF_FIELD_SIZES(be32),
-        MFM_CIDR, 0,
+        MFM_FULLY, 0,
         MFS_IPV4,
         MFP_IPV4,
         true,
@@ -184,7 +210,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_IPV4_DST, "ip_dst", "nw_dst",
         MF_FIELD_SIZES(be32),
-        MFM_CIDR, 0,
+        MFM_FULLY, 0,
         MFS_IPV4,
         MFP_IPV4,
         true,
@@ -195,7 +221,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_IPV6_SRC, "ipv6_src", NULL,
         MF_FIELD_SIZES(ipv6),
-        MFM_CIDR, 0,
+        MFM_FULLY, 0,
         MFS_IPV6,
         MFP_IPV6,
         true,
@@ -204,7 +230,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_IPV6_DST, "ipv6_dst", NULL,
         MF_FIELD_SIZES(ipv6),
-        MFM_CIDR, 0,
+        MFM_FULLY, 0,
         MFS_IPV6,
         MFP_IPV6,
         true,
@@ -214,7 +240,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_IPV6_LABEL, "ipv6_label", NULL,
         4, 20,
-        MFM_NONE, FWW_IPV6_LABEL,
+        MFM_FULLY, 0,
         MFS_HEXADECIMAL,
         MFP_IPV6,
         false,
@@ -257,7 +283,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         MFP_IP_ANY,
         true,
         NXM_NX_IP_TTL, "NXM_NX_IP_TTL",
-        0, NULL,
+        NXM_NX_IP_TTL, "NXM_NX_IP_TTL",
     }, {
         MFF_IP_FRAG, "ip_frag", NULL,
         1, 2,
@@ -266,7 +292,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         MFP_IP_ANY,
         false,
         NXM_NX_IP_FRAG, "NXM_NX_IP_FRAG",
-        0, NULL,
+        NXM_NX_IP_FRAG, "NXM_NX_IP_FRAG",
     },
 
     {
@@ -281,7 +307,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ARP_SPA, "arp_spa", NULL,
         MF_FIELD_SIZES(be32),
-        MFM_CIDR, 0,
+        MFM_FULLY, 0,
         MFS_IPV4,
         MFP_ARP,
         false,
@@ -290,7 +316,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ARP_TPA, "arp_tpa", NULL,
         MF_FIELD_SIZES(be32),
-        MFM_CIDR, 0,
+        MFM_FULLY, 0,
         MFS_IPV4,
         MFP_ARP,
         false,
@@ -299,7 +325,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ARP_SHA, "arp_sha", NULL,
         MF_FIELD_SIZES(mac),
-        MFM_NONE, FWW_ARP_SHA,
+        MFM_FULLY, 0,
         MFS_ETHERNET,
         MFP_ARP,
         false,
@@ -308,7 +334,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ARP_THA, "arp_tha", NULL,
         MF_FIELD_SIZES(mac),
-        MFM_NONE, FWW_ARP_THA,
+        MFM_FULLY, 0,
         MFS_ETHERNET,
         MFP_ARP,
         false,
@@ -407,7 +433,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     {
         MFF_ND_TARGET, "nd_target", NULL,
         MF_FIELD_SIZES(ipv6),
-        MFM_CIDR, 0,
+        MFM_FULLY, 0,
         MFS_IPV6,
         MFP_ND,
         false,
@@ -416,7 +442,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ND_SLL, "nd_sll", NULL,
         MF_FIELD_SIZES(mac),
-        MFM_NONE, FWW_ARP_SHA,
+        MFM_FULLY, 0,
         MFS_ETHERNET,
         MFP_ND_SOLICIT,
         false,
@@ -425,7 +451,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }, {
         MFF_ND_TLL, "nd_tll", NULL,
         MF_FIELD_SIZES(mac),
-        MFM_NONE, FWW_ARP_THA,
+        MFM_FULLY, 0,
         MFS_ETHERNET,
         MFP_ND_ADVERT,
         false,
@@ -434,18 +460,22 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     }
 };
 
+/* Maps an NXM or OXM header value to an mf_field. */
 struct nxm_field {
-    struct hmap_node hmap_node;
-    uint32_t nxm_header;
+    struct hmap_node hmap_node; /* In 'all_fields' hmap. */
+    uint32_t header;            /* NXM or OXM header value. */
     const struct mf_field *mf;
 };
 
-static struct hmap all_nxm_fields = HMAP_INITIALIZER(&all_nxm_fields);
+/* Contains 'struct nxm_field's. */
+static struct hmap all_fields = HMAP_INITIALIZER(&all_fields);
 
 /* Rate limit for parse errors.  These always indicate a bug in an OpenFlow
  * controller and so there's not much point in showing a lot of them. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
+const struct mf_field *mf_from_nxm_header__(uint32_t header);
+
 /* Returns the field with the given 'id'. */
 const struct mf_field *
 mf_from_id(enum mf_field_id id)
@@ -476,56 +506,57 @@ mf_from_name(const char *name)
 }
 
 static void
-add_nxm_field(uint32_t nxm_header, const struct mf_field *mf)
+add_nxm_field(uint32_t header, const struct mf_field *mf)
 {
     struct nxm_field *f;
 
     f = xmalloc(sizeof *f);
-    hmap_insert(&all_nxm_fields, &f->hmap_node, hash_int(nxm_header, 0));
-    f->nxm_header = nxm_header;
+    hmap_insert(&all_fields, &f->hmap_node, hash_int(header, 0));
+    f->header = header;
     f->mf = mf;
 }
 
 static void
-nxm_init(void)
+nxm_init_add_field(const struct mf_field *mf, uint32_t header)
 {
-    const struct mf_field *mf;
-
-    for (mf = mf_fields; mf < &mf_fields[MFF_N_IDS]; mf++) {
-        if (mf->nxm_header) {
-            add_nxm_field(mf->nxm_header, mf);
-            if (mf->maskable != MFM_NONE) {
-                add_nxm_field(NXM_MAKE_WILD_HEADER(mf->nxm_header), mf);
-            }
+    if (header) {
+        assert(!mf_from_nxm_header__(header));
+        add_nxm_field(header, mf);
+        if (mf->maskable != MFM_NONE) {
+            add_nxm_field(NXM_MAKE_WILD_HEADER(header), mf);
         }
     }
+}
+
+static void
+nxm_init(void)
+{
+    const struct mf_field *mf;
 
-#ifndef NDEBUG
-    /* Verify that the header values are unique. */
     for (mf = mf_fields; mf < &mf_fields[MFF_N_IDS]; mf++) {
-        if (mf->nxm_header) {
-            assert(mf_from_nxm_header(mf->nxm_header) == mf);
-            if (mf->maskable != MFM_NONE) {
-                assert(mf_from_nxm_header(NXM_MAKE_WILD_HEADER(mf->nxm_header))
-                       == mf);
-            }
+        nxm_init_add_field(mf, mf->nxm_header);
+        if (mf->oxm_header != mf->nxm_header) {
+            nxm_init_add_field(mf, mf->oxm_header);
         }
     }
-#endif
 }
 
 const struct mf_field *
 mf_from_nxm_header(uint32_t header)
 {
-    const struct nxm_field *f;
-
-    if (hmap_is_empty(&all_nxm_fields)) {
+    if (hmap_is_empty(&all_fields)) {
         nxm_init();
     }
+    return mf_from_nxm_header__(header);
+}
+
+const struct mf_field *
+mf_from_nxm_header__(uint32_t header)
+{
+    const struct nxm_field *f;
 
-    HMAP_FOR_EACH_IN_BUCKET (f, hmap_node, hash_int(header, 0),
-                             &all_nxm_fields) {
-        if (f->nxm_header == header) {
+    HMAP_FOR_EACH_IN_BUCKET (f, hmap_node, hash_int(header, 0), &all_fields) {
+        if (f->header == header) {
             return f->mf;
         }
     }
@@ -543,61 +574,43 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
 {
     switch (mf->id) {
     case MFF_IN_PORT:
-    case MFF_ETH_SRC:
     case MFF_ETH_TYPE:
     case MFF_IP_PROTO:
     case MFF_IP_DSCP:
     case MFF_IP_ECN:
     case MFF_IP_TTL:
-    case MFF_IPV6_LABEL:
     case MFF_ARP_OP:
-    case MFF_ARP_SHA:
-    case MFF_ARP_THA:
-    case MFF_ND_SLL:
-    case MFF_ND_TLL:
         assert(mf->fww_bit != 0);
         return (wc->wildcards & mf->fww_bit) != 0;
 
     case MFF_TUN_ID:
         return !wc->tun_id_mask;
+    case MFF_METADATA:
+        return !wc->metadata_mask;
 
-#if FLOW_N_REGS > 0
-    case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
-    case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
-    case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
-    case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
-    case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
-    case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
-    case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
-    case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+    CASE_MFF_REGS:
         return !wc->reg_masks[mf->id - MFF_REG0];
 
+    case MFF_ETH_SRC:
+        return eth_addr_is_zero(wc->dl_src_mask);
     case MFF_ETH_DST:
-        return ((wc->wildcards & (FWW_ETH_MCAST | FWW_DL_DST))
-                == (FWW_ETH_MCAST | FWW_DL_DST));
+        return eth_addr_is_zero(wc->dl_dst_mask);
+
+    case MFF_ARP_SHA:
+    case MFF_ND_SLL:
+        return eth_addr_is_zero(wc->arp_sha_mask);
+
+    case MFF_ARP_THA:
+    case MFF_ND_TLL:
+        return eth_addr_is_zero(wc->arp_tha_mask);
 
     case MFF_VLAN_TCI:
         return !wc->vlan_tci_mask;
-    case MFF_VLAN_VID:
+    case MFF_DL_VLAN:
         return !(wc->vlan_tci_mask & htons(VLAN_VID_MASK));
+    case MFF_VLAN_VID:
+        return !(wc->vlan_tci_mask & htons(VLAN_VID_MASK | VLAN_CFI));
+    case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
         return !(wc->vlan_tci_mask & htons(VLAN_PCP_MASK));
 
@@ -611,6 +624,9 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_IPV6_DST:
         return ipv6_mask_is_any(&wc->ipv6_dst_mask);
 
+    case MFF_IPV6_LABEL:
+        return !wc->ipv6_label_mask;
+
     case MFF_ND_TARGET:
         return ipv6_mask_is_any(&wc->nd_target_mask);
 
@@ -651,18 +667,12 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
 {
     switch (mf->id) {
     case MFF_IN_PORT:
-    case MFF_ETH_SRC:
     case MFF_ETH_TYPE:
     case MFF_IP_PROTO:
     case MFF_IP_DSCP:
     case MFF_IP_ECN:
     case MFF_IP_TTL:
-    case MFF_IPV6_LABEL:
     case MFF_ARP_OP:
-    case MFF_ARP_SHA:
-    case MFF_ARP_THA:
-    case MFF_ND_SLL:
-    case MFF_ND_TLL:
         assert(mf->fww_bit != 0);
         memset(mask, wc->wildcards & mf->fww_bit ? 0x00 : 0xff, mf->n_bytes);
         break;
@@ -670,48 +680,32 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
     case MFF_TUN_ID:
         mask->be64 = wc->tun_id_mask;
         break;
+    case MFF_METADATA:
+        mask->be64 = wc->metadata_mask;
+        break;
 
-#if FLOW_N_REGS > 0
-    case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
-    case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
-    case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
-    case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
-    case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
-    case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
-    case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
-    case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+    CASE_MFF_REGS:
         mask->be32 = htonl(wc->reg_masks[mf->id - MFF_REG0]);
         break;
 
     case MFF_ETH_DST:
-        memcpy(mask->mac, flow_wildcards_to_dl_dst_mask(wc->wildcards),
-               ETH_ADDR_LEN);
+        memcpy(mask->mac, wc->dl_dst_mask, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ETH_SRC:
+        memcpy(mask->mac, wc->dl_src_mask, ETH_ADDR_LEN);
         break;
 
     case MFF_VLAN_TCI:
         mask->be16 = wc->vlan_tci_mask;
         break;
-    case MFF_VLAN_VID:
+    case MFF_DL_VLAN:
         mask->be16 = wc->vlan_tci_mask & htons(VLAN_VID_MASK);
         break;
+    case MFF_VLAN_VID:
+        mask->be16 = wc->vlan_tci_mask & htons(VLAN_VID_MASK | VLAN_CFI);
+        break;
+    case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
         mask->u8 = vlan_tci_to_pcp(wc->vlan_tci_mask);
         break;
@@ -729,6 +723,9 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
     case MFF_IPV6_DST:
         mask->ipv6 = wc->ipv6_dst_mask;
         break;
+    case MFF_IPV6_LABEL:
+        mask->be32 = wc->ipv6_label_mask;
+        break;
 
     case MFF_ND_TARGET:
         mask->ipv6 = wc->nd_target_mask;
@@ -744,6 +741,14 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
     case MFF_ARP_TPA:
         mask->be32 = wc->nw_dst_mask;
         break;
+    case MFF_ARP_SHA:
+    case MFF_ND_SLL:
+        memcpy(mask->mac, wc->arp_sha_mask, ETH_ADDR_LEN);
+        break;
+    case MFF_ARP_THA:
+    case MFF_ND_TLL:
+        memcpy(mask->mac, wc->arp_tha_mask, ETH_ADDR_LEN);
+        break;
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
@@ -781,14 +786,6 @@ mf_is_mask_valid(const struct mf_field *mf, const union mf_value *mask)
 
     case MFM_FULLY:
         return true;
-
-    case MFM_CIDR:
-        return (mf->n_bytes == 4
-                ? ip_is_cidr(mask->be32)
-                : ipv6_is_cidr(&mask->ipv6));
-
-    case MFM_MCAST:
-        return flow_wildcards_is_dl_dst_mask_valid(mask->mac);
     }
 
     NOT_REACHED();
@@ -829,6 +826,8 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
         return flow->dl_type == htons(ETH_TYPE_IP);
     case MFP_IPV6:
         return flow->dl_type == htons(ETH_TYPE_IPV6);
+    case MFP_VLAN_VID:
+        return (flow->vlan_tci & htons(VLAN_CFI)) != 0;
     case MFP_IP_ANY:
         return is_ip_any(flow);
 
@@ -874,34 +873,9 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
 {
     switch (mf->id) {
     case MFF_TUN_ID:
+    case MFF_METADATA:
     case MFF_IN_PORT:
-#if FLOW_N_REGS > 0
-    case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
-    case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
-    case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
-    case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
-    case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
-    case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
-    case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
-    case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+    CASE_MFF_REGS:
     case MFF_ETH_SRC:
     case MFF_ETH_DST:
     case MFF_ETH_TYPE:
@@ -939,11 +913,14 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_ARP_OP:
         return !(value->be16 & htons(0xff00));
 
-    case MFF_VLAN_VID:
+    case MFF_DL_VLAN:
         return !(value->be16 & htons(VLAN_CFI | VLAN_PCP_MASK));
+    case MFF_VLAN_VID:
+        return !(value->be16 & htons(VLAN_PCP_MASK));
 
+    case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
-        return !(value->u8 & ~7);
+        return !(value->u8 & ~(VLAN_PCP_MASK >> VLAN_PCP_SHIFT));
 
     case MFF_IPV6_LABEL:
         return !(value->be32 & ~htonl(IPV6_LABEL_MASK));
@@ -964,38 +941,15 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
     case MFF_TUN_ID:
         value->be64 = flow->tun_id;
         break;
+    case MFF_METADATA:
+        value->be64 = flow->metadata;
+        break;
 
     case MFF_IN_PORT:
         value->be16 = htons(flow->in_port);
         break;
 
-#if FLOW_N_REGS > 0
-    case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
-    case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
-    case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
-    case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
-    case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
-    case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
-    case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
-    case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+    CASE_MFF_REGS:
         value->be32 = htonl(flow->regs[mf->id - MFF_REG0]);
         break;
 
@@ -1015,10 +969,14 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         value->be16 = flow->vlan_tci;
         break;
 
-    case MFF_VLAN_VID:
+    case MFF_DL_VLAN:
         value->be16 = flow->vlan_tci & htons(VLAN_VID_MASK);
         break;
+    case MFF_VLAN_VID:
+        value->be16 = flow->vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI);
+        break;
 
+    case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
         value->u8 = vlan_tci_to_pcp(flow->vlan_tci);
         break;
@@ -1086,17 +1044,11 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         break;
 
     case MFF_TCP_SRC:
-        value->be16 = flow->tp_src;
-        break;
-
-    case MFF_TCP_DST:
-        value->be16 = flow->tp_dst;
-        break;
-
     case MFF_UDP_SRC:
         value->be16 = flow->tp_src;
         break;
 
+    case MFF_TCP_DST:
     case MFF_UDP_DST:
         value->be16 = flow->tp_dst;
         break;
@@ -1132,42 +1084,17 @@ mf_set_value(const struct mf_field *mf,
     case MFF_TUN_ID:
         cls_rule_set_tun_id(rule, value->be64);
         break;
+    case MFF_METADATA:
+        cls_rule_set_metadata(rule, value->be64);
+        break;
 
     case MFF_IN_PORT:
         cls_rule_set_in_port(rule, ntohs(value->be16));
         break;
 
-#if FLOW_N_REGS > 0
-    case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
-    case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
-    case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
-    case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
-    case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
-    case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
-    case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
-    case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
-#if FLOW_N_REGS > 0
+    CASE_MFF_REGS:
         cls_rule_set_reg(rule, mf->id - MFF_REG0, ntohl(value->be32));
         break;
-#endif
 
     case MFF_ETH_SRC:
         cls_rule_set_dl_src(rule, value->mac);
@@ -1185,10 +1112,14 @@ mf_set_value(const struct mf_field *mf,
         cls_rule_set_dl_tci(rule, value->be16);
         break;
 
-    case MFF_VLAN_VID:
+    case MFF_DL_VLAN:
         cls_rule_set_dl_vlan(rule, value->be16);
         break;
+    case MFF_VLAN_VID:
+        cls_rule_set_vlan_vid(rule, value->be16);
+        break;
 
+    case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
         cls_rule_set_dl_vlan_pcp(rule, value->u8);
         break;
@@ -1256,17 +1187,11 @@ mf_set_value(const struct mf_field *mf,
         break;
 
     case MFF_TCP_SRC:
-        cls_rule_set_tp_src(rule, value->be16);
-        break;
-
-    case MFF_TCP_DST:
-        cls_rule_set_tp_dst(rule, value->be16);
-        break;
-
     case MFF_UDP_SRC:
         cls_rule_set_tp_src(rule, value->be16);
         break;
 
+    case MFF_TCP_DST:
     case MFF_UDP_DST:
         cls_rule_set_tp_dst(rule, value->be16);
         break;
@@ -1302,42 +1227,17 @@ mf_set_flow_value(const struct mf_field *mf,
     case MFF_TUN_ID:
         flow->tun_id = value->be64;
         break;
+    case MFF_METADATA:
+        flow->metadata = value->be64;
+        break;
 
     case MFF_IN_PORT:
         flow->in_port = ntohs(value->be16);
         break;
 
-#if FLOW_N_REGS > 0
-    case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
-    case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
-    case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
-    case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
-    case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
-    case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
-    case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
-    case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
-#if FLOW_N_REGS > 0
+    CASE_MFF_REGS:
         flow->regs[mf->id - MFF_REG0] = ntohl(value->be32);
         break;
-#endif
 
     case MFF_ETH_SRC:
         memcpy(flow->dl_src, value->mac, ETH_ADDR_LEN);
@@ -1355,10 +1255,14 @@ mf_set_flow_value(const struct mf_field *mf,
         flow->vlan_tci = value->be16;
         break;
 
+    case MFF_DL_VLAN:
+        flow_set_dl_vlan(flow, value->be16);
+        break;
     case MFF_VLAN_VID:
         flow_set_vlan_vid(flow, value->be16);
         break;
 
+    case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
         flow_set_vlan_pcp(flow, value->u8);
         break;
@@ -1457,6 +1361,19 @@ mf_set_flow_value(const struct mf_field *mf,
     }
 }
 
+/* Returns true if 'mf' has a zero value in 'flow', false if it is nonzero.
+ *
+ * The caller is responsible for ensuring that 'flow' meets 'mf''s
+ * prerequisites. */
+bool
+mf_is_zero(const struct mf_field *mf, const struct flow *flow)
+{
+    union mf_value value;
+
+    mf_get_value(mf, flow, &value);
+    return is_all_zeros((const uint8_t *) &value, mf->n_bytes);
+}
+
 /* Makes 'rule' wildcard field 'mf'.
  *
  * The caller is responsible for ensuring that 'rule' meets 'mf''s
@@ -1468,64 +1385,26 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
     case MFF_TUN_ID:
         cls_rule_set_tun_id_masked(rule, htonll(0), htonll(0));
         break;
+    case MFF_METADATA:
+        cls_rule_set_metadata_masked(rule, htonll(0), htonll(0));
 
     case MFF_IN_PORT:
         rule->wc.wildcards |= FWW_IN_PORT;
         rule->flow.in_port = 0;
         break;
 
-#if FLOW_N_REGS > 0
-    case MFF_REG0:
-        cls_rule_set_reg_masked(rule, 0, 0, 0);
-        break;
-#endif
-#if FLOW_N_REGS > 1
-    case MFF_REG1:
-        cls_rule_set_reg_masked(rule, 1, 0, 0);
-        break;
-#endif
-#if FLOW_N_REGS > 2
-    case MFF_REG2:
-        cls_rule_set_reg_masked(rule, 2, 0, 0);
-        break;
-#endif
-#if FLOW_N_REGS > 3
-    case MFF_REG3:
-        cls_rule_set_reg_masked(rule, 3, 0, 0);
-        break;
-#endif
-#if FLOW_N_REGS > 4
-    case MFF_REG4:
-        cls_rule_set_reg_masked(rule, 4, 0, 0);
-        break;
-#endif
-#if FLOW_N_REGS > 5
-    case MFF_REG5:
-        cls_rule_set_reg_masked(rule, 5, 0, 0);
-        break;
-#endif
-#if FLOW_N_REGS > 6
-    case MFF_REG6:
-        cls_rule_set_reg_masked(rule, 6, 0, 0);
-        break;
-#endif
-#if FLOW_N_REGS > 7
-    case MFF_REG7:
-        cls_rule_set_reg_masked(rule, 7, 0, 0);
+    CASE_MFF_REGS:
+        cls_rule_set_reg_masked(rule, mf->id - MFF_REG0, 0, 0);
         break;
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
 
     case MFF_ETH_SRC:
-        rule->wc.wildcards |= FWW_DL_SRC;
-        memset(rule->flow.dl_src, 0, sizeof rule->flow.dl_src);
+        memset(rule->flow.dl_src, 0, ETH_ADDR_LEN);
+        memset(rule->wc.dl_src_mask, 0, ETH_ADDR_LEN);
         break;
 
     case MFF_ETH_DST:
-        rule->wc.wildcards |= FWW_DL_DST | FWW_ETH_MCAST;
-        memset(rule->flow.dl_dst, 0, sizeof rule->flow.dl_dst);
+        memset(rule->flow.dl_dst, 0, ETH_ADDR_LEN);
+        memset(rule->wc.dl_dst_mask, 0, ETH_ADDR_LEN);
         break;
 
     case MFF_ETH_TYPE:
@@ -1537,10 +1416,12 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
         cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
         break;
 
+    case MFF_DL_VLAN:
     case MFF_VLAN_VID:
         cls_rule_set_any_vid(rule);
         break;
 
+    case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
         cls_rule_set_any_pcp(rule);
         break;
@@ -1566,7 +1447,7 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
         break;
 
     case MFF_IPV6_LABEL:
-        rule->wc.wildcards |= FWW_IPV6_LABEL;
+        rule->wc.ipv6_label_mask = 0;
         rule->flow.ipv6_label = 0;
         break;
 
@@ -1602,14 +1483,14 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
 
     case MFF_ARP_SHA:
     case MFF_ND_SLL:
-        rule->wc.wildcards |= FWW_ARP_SHA;
-        memset(rule->flow.arp_sha, 0, sizeof rule->flow.arp_sha);
+        memset(rule->flow.arp_sha, 0, ETH_ADDR_LEN);
+        memset(rule->wc.arp_sha_mask, 0, ETH_ADDR_LEN);
         break;
 
     case MFF_ARP_THA:
     case MFF_ND_TLL:
-        rule->wc.wildcards |= FWW_ARP_THA;
-        memset(rule->flow.arp_tha, 0, sizeof rule->flow.arp_tha);
+        memset(rule->flow.arp_tha, 0, ETH_ADDR_LEN);
+        memset(rule->wc.arp_tha_mask, 0, ETH_ADDR_LEN);
         break;
 
     case MFF_TCP_SRC:
@@ -1665,71 +1546,59 @@ mf_set(const struct mf_field *mf,
 
     switch (mf->id) {
     case MFF_IN_PORT:
-    case MFF_ETH_SRC:
     case MFF_ETH_TYPE:
-    case MFF_VLAN_VID:
+    case MFF_DL_VLAN:
+    case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
-    case MFF_IPV6_LABEL:
     case MFF_IP_PROTO:
     case MFF_IP_TTL:
     case MFF_IP_DSCP:
     case MFF_IP_ECN:
     case MFF_ARP_OP:
-    case MFF_ARP_SHA:
-    case MFF_ARP_THA:
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_TYPE:
     case MFF_ICMPV6_CODE:
-    case MFF_ND_SLL:
-    case MFF_ND_TLL:
         NOT_REACHED();
 
     case MFF_TUN_ID:
         cls_rule_set_tun_id_masked(rule, value->be64, mask->be64);
         break;
+    case MFF_METADATA:
+        cls_rule_set_metadata_masked(rule, value->be64, mask->be64);
+        break;
 
-#if FLOW_N_REGS > 0
-    case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
-    case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
-    case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
-    case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
-    case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
-    case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
-    case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
-    case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+    CASE_MFF_REGS:
         cls_rule_set_reg_masked(rule, mf->id - MFF_REG0,
                                 ntohl(value->be32), ntohl(mask->be32));
         break;
 
     case MFF_ETH_DST:
-        if (flow_wildcards_is_dl_dst_mask_valid(mask->mac)) {
-            cls_rule_set_dl_dst_masked(rule, value->mac, mask->mac);
-        }
+        cls_rule_set_dl_dst_masked(rule, value->mac, mask->mac);
+        break;
+
+    case MFF_ETH_SRC:
+        cls_rule_set_dl_src_masked(rule, value->mac, mask->mac);
+        break;
+
+    case MFF_ARP_SHA:
+    case MFF_ND_SLL:
+        cls_rule_set_arp_sha_masked(rule, value->mac, mask->mac);
+        break;
+
+    case MFF_ARP_THA:
+    case MFF_ND_TLL:
+        cls_rule_set_arp_tha_masked(rule, value->mac, mask->mac);
         break;
 
     case MFF_VLAN_TCI:
         cls_rule_set_dl_tci_masked(rule, value->be16, mask->be16);
         break;
 
+    case MFF_VLAN_VID:
+        cls_rule_set_vlan_vid_masked(rule, value->be16, mask->be16);
+        break;
+
     case MFF_IPV4_SRC:
         cls_rule_set_nw_src_masked(rule, value->be32, mask->be32);
         break;
@@ -1746,6 +1615,14 @@ mf_set(const struct mf_field *mf,
         cls_rule_set_ipv6_dst_masked(rule, &value->ipv6, &mask->ipv6);
         break;
 
+    case MFF_IPV6_LABEL:
+        if ((mask->be32 & htonl(IPV6_LABEL_MASK)) == htonl(IPV6_LABEL_MASK)) {
+            mf_set_value(mf, value, rule);
+        } else {
+            cls_rule_set_ipv6_label_masked(rule, value->be32, mask->be32);
+        }
+        break;
+
     case MFF_ND_TARGET:
         cls_rule_set_nd_target_masked(rule, &value->ipv6, &mask->ipv6);
         break;
@@ -1845,34 +1722,9 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
 
     switch (mf->id) {
     case MFF_TUN_ID:
+    case MFF_METADATA:
     case MFF_IN_PORT:
-#if FLOW_N_REGS > 0
-    case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
-    case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
-    case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
-    case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
-    case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
-    case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
-    case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
-    case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+    CASE_MFF_REGS:
     case MFF_ETH_SRC:
     case MFF_ETH_DST:
     case MFF_ETH_TYPE:
@@ -1920,10 +1772,14 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
         value->be16 &= htons(0xff);
         break;
 
-    case MFF_VLAN_VID:
+    case MFF_DL_VLAN:
         value->be16 &= htons(VLAN_VID_MASK);
         break;
+    case MFF_VLAN_VID:
+        value->be16 &= htons(VLAN_VID_MASK | VLAN_CFI);
+        break;
 
+    case MFF_DL_VLAN_PCP:
     case MFF_VLAN_PCP:
         value->u8 &= 0x07;
         break;
@@ -2047,12 +1903,14 @@ mf_from_ipv6_string(const struct mf_field *mf, const char *s,
 
     netmask = strtok_r(NULL, "/", &save_ptr);
     if (netmask) {
-        int prefix = atoi(netmask);
-        if (prefix <= 0 || prefix > 128) {
-            free(str);
-            return xasprintf("%s: prefix bits not between 1 and 128", s);
-        } else {
-            *mask = ipv6_create_mask(prefix);
+        if (inet_pton(AF_INET6, netmask, mask) != 1) {
+            int prefix = atoi(netmask);
+            if (prefix <= 0 || prefix > 128) {
+                free(str);
+                return xasprintf("%s: prefix bits not between 1 and 128", s);
+            } else {
+                *mask = ipv6_create_mask(prefix);
+            }
         }
     } else {
         *mask = in6addr_exact;
@@ -2260,10 +2118,7 @@ mf_format(const struct mf_field *mf,
         break;
 
     case MFS_ETHERNET:
-        ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(value->mac));
-        if (mask) {
-            ds_put_format(s, "/"ETH_ADDR_FMT, ETH_ADDR_ARGS(mask->mac));
-        }
+        eth_format_masked(value->mac, mask->mac, s);
         break;
 
     case MFS_IPV4:
@@ -2286,13 +2141,7 @@ mf_format(const struct mf_field *mf,
 \f
 /* Makes subfield 'sf' within 'rule' exactly match the 'sf->n_bits'
  * least-significant bits in 'x'.
- *
- * See mf_set_subfield() for an example.
- *
- * The difference between this function and mf_set_subfield() is that the
- * latter function can only handle subfields up to 64 bits wide, whereas this
- * one handles the general case.  On the other hand, mf_set_subfield() is
- * arguably easier to use. */
+ */
 void
 mf_write_subfield(const struct mf_subfield *sf, const union mf_subvalue *x,
                   struct cls_rule *rule)
@@ -2306,80 +2155,6 @@ mf_write_subfield(const struct mf_subfield *sf, const union mf_subvalue *x,
     mf_set(field, &value, &mask, rule);
 }
 
-/* Makes subfield 'sf' within 'rule' exactly match the 'sf->n_bits'
- * least-significant bits of 'x'.
- *
- * Example: suppose that 'sf->field' is originally the following 2-byte field
- * in 'rule':
- *
- *     value == 0xe00a == 2#1110000000001010
- *      mask == 0xfc3f == 2#1111110000111111
- *
- * The call mf_set_subfield(sf, 0x55, 8, 7, rule), where sf->ofs == 8 and
- * sf->n_bits == 7 would have the following effect (note that 0x55 is
- * 2#1010101):
- *
- *     value == 0xd50a == 2#1101010100001010
- *      mask == 0xff3f == 2#1111111100111111
- *                           ^^^^^^^ affected bits
- *
- * The caller is responsible for ensuring that the result will be a valid
- * wildcard pattern for 'sf->field'.  The caller is responsible for ensuring
- * that 'rule' meets 'sf->field''s prerequisites. */
-void
-mf_set_subfield(const struct mf_subfield *sf, uint64_t x,
-                struct cls_rule *rule)
-{
-    const struct mf_field *field = sf->field;
-    unsigned int n_bits = sf->n_bits;
-    unsigned int ofs = sf->ofs;
-
-    if (ofs == 0 && field->n_bytes * 8 == n_bits) {
-        union mf_value value;
-        int i;
-
-        for (i = field->n_bytes - 1; i >= 0; i--) {
-            ((uint8_t *) &value)[i] = x;
-            x >>= 8;
-        }
-        mf_set_value(field, &value, rule);
-    } else {
-        union mf_value value, mask;
-        uint8_t *vp = (uint8_t *) &value;
-        uint8_t *mp = (uint8_t *) &mask;
-
-        mf_get(field, rule, &value, &mask);
-        bitwise_put(x,          vp, field->n_bytes, ofs, n_bits);
-        bitwise_put(UINT64_MAX, mp, field->n_bytes, ofs, n_bits);
-        mf_set(field, &value, &mask, rule);
-    }
-}
-
-/* Similar to mf_set_subfield() but modifies only a flow, not a cls_rule. */
-void
-mf_set_subfield_value(const struct mf_subfield *sf, uint64_t x,
-                      struct flow *flow)
-{
-    const struct mf_field *field = sf->field;
-    unsigned int n_bits = sf->n_bits;
-    unsigned int ofs = sf->ofs;
-    union mf_value value;
-
-    if (ofs == 0 && field->n_bytes * 8 == n_bits) {
-        int i;
-
-        for (i = field->n_bytes - 1; i >= 0; i--) {
-            ((uint8_t *) &value)[i] = x;
-            x >>= 8;
-        }
-        mf_set_flow_value(field, &value, flow);
-    } else {
-        mf_get_value(field, flow, &value);
-        bitwise_put(x, &value, field->n_bytes, ofs, n_bits);
-        mf_set_flow_value(field, &value, flow);
-    }
-}
-
 /* Initializes 'x' to the value of 'sf' within 'flow'.  'sf' must be valid for
  * reading 'flow', e.g. as checked by mf_check_src(). */
 void
@@ -2452,6 +2227,11 @@ mf_parse_subfield_name(const char *name, int name_len, bool *wild)
             && mf->nxm_name[name_len] == '\0') {
             return mf;
         }
+        if (mf->oxm_name
+            && !strncmp(mf->oxm_name, name, name_len)
+            && mf->oxm_name[name_len] == '\0') {
+            return mf;
+        }
     }
 
     return NULL;
index 6340b3e..12a4baa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@ struct ds;
 enum mf_field_id {
     /* Metadata. */
     MFF_TUN_ID,                 /* be64 */
+    MFF_METADATA,               /* be64 */
     MFF_IN_PORT,                /* be16 */
 
 #if FLOW_N_REGS > 0
@@ -57,9 +58,6 @@ enum mf_field_id {
 #endif
 #if FLOW_N_REGS > 7
     MFF_REG7,                   /* be32 */
-#endif
-#if FLOW_N_REGS > 8
-#error
 #endif
 
     /* L2. */
@@ -68,8 +66,10 @@ enum mf_field_id {
     MFF_ETH_TYPE,               /* be16 */
 
     MFF_VLAN_TCI,               /* be16 */
-    MFF_VLAN_VID,               /* be16 */
-    MFF_VLAN_PCP,               /* u8 */
+    MFF_DL_VLAN,                /* be16 (OpenFlow 1.0 compatibility) */
+    MFF_VLAN_VID,               /* be16 (OpenFlow 1.2 compatibility) */
+    MFF_DL_VLAN_PCP,            /* u8 (OpenFlow 1.0 compatibility) */
+    MFF_VLAN_PCP,               /* be16 (OpenFlow 1.2 compatibility) */
 
     /* L3. */
     MFF_IPV4_SRC,               /* be32 */
@@ -112,6 +112,40 @@ enum mf_field_id {
     MFF_N_IDS
 };
 
+/* Use this macro as CASE_MFF_REGS: in a switch statement to choose all of the
+ * MFF_REGx cases. */
+#if FLOW_N_REGS == 1
+# define CASE_MFF_REGS                                          \
+    case MFF_REG0
+#elif FLOW_N_REGS == 2
+# define CASE_MFF_REGS                                          \
+    case MFF_REG0: case MFF_REG1
+#elif FLOW_N_REGS == 3
+# define CASE_MFF_REGS                                          \
+    case MFF_REG0: case MFF_REG1: case MFF_REG2
+#elif FLOW_N_REGS == 4
+# define CASE_MFF_REGS                                          \
+    case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3
+#elif FLOW_N_REGS == 5
+# define CASE_MFF_REGS                                          \
+    case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3: \
+    case MFF_REG4
+#elif FLOW_N_REGS == 6
+# define CASE_MFF_REGS                                          \
+    case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3: \
+    case MFF_REG4: case MFF_REG5
+#elif FLOW_N_REGS == 7
+# define CASE_MFF_REGS                                          \
+    case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3: \
+    case MFF_REG4: case MFF_REG5: case MFF_REG6
+#elif FLOW_N_REGS == 8
+# define CASE_MFF_REGS                                          \
+    case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3: \
+    case MFF_REG4: case MFF_REG5: case MFF_REG6: case MFF_REG7
+#else
+# error
+#endif
+
 /* Prerequisites for matching a field.
  *
  * A field may only be matched if the correct lower-level protocols are also
@@ -122,6 +156,7 @@ enum mf_prereqs {
 
     /* L2 requirements. */
     MFP_ARP,
+    MFP_VLAN_VID,
     MFP_IPV4,
     MFP_IPV6,
     MFP_IP_ANY,
@@ -144,8 +179,6 @@ enum mf_prereqs {
 enum mf_maskable {
     MFM_NONE,                   /* No sub-field masking. */
     MFM_FULLY,                  /* Every bit is individually maskable. */
-    MFM_CIDR,                   /* Contiguous low-order bits may be masked. */
-    MFM_MCAST                   /* Byte 0, bit 0 is separately maskable. */
 };
 
 /* How to format or parse a field's value. */
@@ -190,21 +223,30 @@ struct mf_field {
     enum mf_prereqs prereqs;
     bool writable;              /* May be written by actions? */
 
-    /* NXM properties.
+    /* NXM and OXM properties.
      *
-     * A few "mf_field"s don't correspond to NXM fields.  Those have 0 and
-     * NULL for the following members, respectively. */
-    uint32_t nxm_header;        /* An NXM_* constant (a few fields have 0). */
-    const char *nxm_name;       /* The "NXM_*" constant's name. */
-
-    /* OXM properties */
-    uint32_t oxm_header;        /* Field id in the OXM basic class,
-                                * an OXM_* constant.
-                                * Ignored if oxm_name is NULL */
-    const char *oxm_name;      /* The OXM_* constant's name,
-                                * NULL if the field is not present
-                                * in the OXM basic class */
-
+     * There are the following possibilities for these members for a given
+     * mf_field:
+     *
+     *   - Neither NXM nor OXM defines such a field: these members will all be
+     *     zero or NULL.
+     *
+     *   - NXM and OXM both define such a field: nxm_header and oxm_header will
+     *     both be nonzero and different, similarly for nxm_name and oxm_name.
+     *
+     *   - Only NXM or only OXM defines such a field: nxm_header and oxm_header
+     *     will both have the same value (either an OXM_* or NXM_* value) and
+     *     similarly for nxm_name and oxm_name.
+     *
+     * Thus, 'nxm_header' is the appropriate header to use when outputting an
+     * NXM formatted match, since it will be an NXM_* constant when possible
+     * for compatibility with OpenFlow implementations that expect that, with
+     * OXM_* constants used for fields that OXM adds.  Conversely, 'oxm_header'
+     * is the header to use when outputting an OXM formatted match. */
+    uint32_t nxm_header;        /* An NXM_* (or OXM_*) constant. */
+    const char *nxm_name;       /* The nxm_header constant's name. */
+    uint32_t oxm_header;        /* An OXM_* (or NXM_*) constant. */
+    const char *oxm_name;          /* The oxm_header constant's name */
 };
 
 /* The representation of a field's value. */
@@ -264,6 +306,7 @@ void mf_set_value(const struct mf_field *, const union mf_value *value,
                   struct cls_rule *);
 void mf_set_flow_value(const struct mf_field *, const union mf_value *value,
                        struct flow *);
+bool mf_is_zero(const struct mf_field *, const struct flow *);
 
 void mf_get(const struct mf_field *, const struct cls_rule *,
             union mf_value *value, union mf_value *mask);
@@ -278,10 +321,6 @@ void mf_random_value(const struct mf_field *, union mf_value *value);
 /* Subfields. */
 void mf_write_subfield(const struct mf_subfield *, const union mf_subvalue *,
                        struct cls_rule *);
-void mf_set_subfield(const struct mf_subfield *, uint64_t value,
-                     struct cls_rule *);
-void mf_set_subfield_value(const struct mf_subfield *, uint64_t value,
-                           struct flow *);
 
 void mf_read_subfield(const struct mf_subfield *, const struct flow *,
                       union mf_subvalue *);
index 46976fb..f6a1a0a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,8 +22,8 @@
 #include <sys/types.h>
 #include <netinet/in.h>
 #include "dynamic-string.h"
-#include "meta-flow.h"
 #include "nx-match.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
 #include "ofp-util.h"
 #include "openflow/nicira-ext.h"
@@ -34,37 +34,66 @@ VLOG_DEFINE_THIS_MODULE(multipath);
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 \f
-/* multipath_check(). */
+/* Converts 'nam' into 'mp'.  Returns 0 if successful, otherwise an
+ * OFPERR_*. */
 enum ofperr
-multipath_check(const struct nx_action_multipath *mp, const struct flow *flow)
+multipath_from_openflow(const struct nx_action_multipath *nam,
+                        struct ofpact_multipath *mp)
 {
-    uint32_t n_links = ntohs(mp->max_link) + 1;
+    uint32_t n_links = ntohs(nam->max_link) + 1;
     size_t min_n_bits = log_2_ceil(n_links);
-    struct mf_subfield dst;
-    enum ofperr error;
 
-    nxm_decode(&dst, mp->dst, mp->ofs_nbits);
-    error = mf_check_dst(&dst, flow);
-    if (error) {
-        return error;
-    }
-
-    if (!flow_hash_fields_valid(ntohs(mp->fields))) {
-        VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, ntohs(mp->fields));
-    } else if (mp->algorithm != htons(NX_MP_ALG_MODULO_N)
-               && mp->algorithm != htons(NX_MP_ALG_HASH_THRESHOLD)
-               && mp->algorithm != htons(NX_MP_ALG_HRW)
-               && mp->algorithm != htons(NX_MP_ALG_ITER_HASH)) {
-        VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16,
-                     ntohs(mp->algorithm));
-    } else if (dst.n_bits < min_n_bits) {
+    ofpact_init_MULTIPATH(mp);
+    mp->fields = ntohs(nam->fields);
+    mp->basis = ntohs(nam->basis);
+    mp->algorithm = ntohs(nam->algorithm);
+    mp->max_link = ntohs(nam->max_link);
+    mp->arg = ntohl(nam->arg);
+    mp->dst.field = mf_from_nxm_header(ntohl(nam->dst));
+    mp->dst.ofs = nxm_decode_ofs(nam->ofs_nbits);
+    mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits);
+
+    if (!flow_hash_fields_valid(mp->fields)) {
+        VLOG_WARN_RL(&rl, "unsupported fields %d", (int) mp->fields);
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
+    } else if (mp->algorithm != NX_MP_ALG_MODULO_N
+               && mp->algorithm != NX_MP_ALG_HASH_THRESHOLD
+               && mp->algorithm != NX_MP_ALG_HRW
+               && mp->algorithm != NX_MP_ALG_ITER_HASH) {
+        VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) mp->algorithm);
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
+    } else if (mp->dst.n_bits < min_n_bits) {
         VLOG_WARN_RL(&rl, "multipath action requires at least %zu bits for "
                      "%"PRIu32" links", min_n_bits, n_links);
-    } else {
-        return 0;
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
-    return OFPERR_OFPBAC_BAD_ARGUMENT;
+    return multipath_check(mp, NULL);
+}
+
+/* Checks that 'mp' is valid on flow.  Returns 0 if it is valid, otherwise an
+ * OFPERR_*. */
+enum ofperr
+multipath_check(const struct ofpact_multipath *mp,
+                const struct flow *flow)
+{
+    return mf_check_dst(&mp->dst, flow);
+}
+
+/* Converts 'mp' into an OpenFlow NXAST_MULTIPATH action, which it appends to
+ * 'openflow'. */
+void
+multipath_to_nxast(const struct ofpact_multipath *mp, struct ofpbuf *openflow)
+{
+    struct nx_action_multipath *nam = ofputil_put_NXAST_MULTIPATH(openflow);
+
+    nam->fields = htons(mp->fields);
+    nam->basis = htons(mp->basis);
+    nam->algorithm = htons(mp->algorithm);
+    nam->max_link = htons(mp->max_link);
+    nam->arg = htonl(mp->arg);
+    nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits);
+    nam->dst = htonl(mp->dst.field->nxm_header);
 }
 \f
 /* multipath_execute(). */
@@ -72,19 +101,17 @@ multipath_check(const struct nx_action_multipath *mp, const struct flow *flow)
 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'. */
 void
-multipath_execute(const struct nx_action_multipath *mp, struct flow *flow)
+multipath_execute(const struct ofpact_multipath *mp, struct flow *flow)
 {
     /* Calculate value to store. */
-    uint32_t hash = flow_hash_fields(flow, ntohs(mp->fields),
-                                     ntohs(mp->basis));
-    uint16_t link = multipath_algorithm(hash, ntohs(mp->algorithm),
-                                        ntohs(mp->max_link) + 1,
-                                        ntohl(mp->arg));
-    struct mf_subfield dst;
-
-    nxm_decode(&dst, mp->dst, mp->ofs_nbits);
-    mf_set_subfield_value(&dst, link, flow);
+    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);
+
+    nxm_reg_load(&mp->dst, link, flow);
 }
 
 static uint16_t
@@ -162,15 +189,17 @@ multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm,
     NOT_REACHED();
 }
 \f
-/* multipath_parse(). */
-
+/* Parses 's_' as a set of arguments to the "multipath" action and initializes
+ * 'mp' accordingly.  ovs-ofctl(8) describes the format parsed.
+ *
+ * Prints an error on stderr and aborts the program if 's_' syntax is
+ * invalid. */
 void
-multipath_parse(struct nx_action_multipath *mp, const char *s_)
+multipath_parse(struct ofpact_multipath *mp, const char *s_)
 {
     char *s = xstrdup(s_);
     char *save_ptr = NULL;
-    char *fields, *basis, *algorithm, *n_links_str, *arg, *dst_s;
-    struct mf_subfield dst;
+    char *fields, *basis, *algorithm, *n_links_str, *arg, *dst;
     int n_links;
 
     fields = strtok_r(s, ", ", &save_ptr);
@@ -178,28 +207,28 @@ multipath_parse(struct nx_action_multipath *mp, const char *s_)
     algorithm = strtok_r(NULL, ", ", &save_ptr);
     n_links_str = strtok_r(NULL, ", ", &save_ptr);
     arg = strtok_r(NULL, ", ", &save_ptr);
-    dst_s = strtok_r(NULL, ", ", &save_ptr);
-    if (!dst_s) {
+    dst = strtok_r(NULL, ", ", &save_ptr);
+    if (!dst) {
         ovs_fatal(0, "%s: not enough arguments to multipath action", s_);
     }
 
-    ofputil_init_NXAST_MULTIPATH(mp);
+    ofpact_init_MULTIPATH(mp);
     if (!strcasecmp(fields, "eth_src")) {
-        mp->fields = htons(NX_HASH_FIELDS_ETH_SRC);
+        mp->fields = NX_HASH_FIELDS_ETH_SRC;
     } else if (!strcasecmp(fields, "symmetric_l4")) {
-        mp->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4);
+        mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
     } else {
         ovs_fatal(0, "%s: unknown fields `%s'", s_, fields);
     }
-    mp->basis = htons(atoi(basis));
+    mp->basis = atoi(basis);
     if (!strcasecmp(algorithm, "modulo_n")) {
-        mp->algorithm = htons(NX_MP_ALG_MODULO_N);
+        mp->algorithm = NX_MP_ALG_MODULO_N;
     } else if (!strcasecmp(algorithm, "hash_threshold")) {
-        mp->algorithm = htons(NX_MP_ALG_HASH_THRESHOLD);
+        mp->algorithm = NX_MP_ALG_HASH_THRESHOLD;
     } else if (!strcasecmp(algorithm, "hrw")) {
-        mp->algorithm = htons(NX_MP_ALG_HRW);
+        mp->algorithm = NX_MP_ALG_HRW;
     } else if (!strcasecmp(algorithm, "iter_hash")) {
-        mp->algorithm = htons(NX_MP_ALG_ITER_HASH);
+        mp->algorithm = NX_MP_ALG_ITER_HASH;
     } else {
         ovs_fatal(0, "%s: unknown algorithm `%s'", s_, algorithm);
     }
@@ -208,34 +237,29 @@ multipath_parse(struct nx_action_multipath *mp, const char *s_)
         ovs_fatal(0, "%s: n_links %d is not in valid range 1 to 65536",
                   s_, n_links);
     }
-    mp->max_link = htons(n_links - 1);
-    mp->arg = htonl(atoi(arg));
+    mp->max_link = n_links - 1;
+    mp->arg = atoi(arg);
 
-    mf_parse_subfield(&dst, dst_s);
-    if (dst.n_bits < 16 && n_links > (1u << dst.n_bits)) {
+    mf_parse_subfield(&mp->dst, dst);
+    if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) {
         ovs_fatal(0, "%s: %d-bit destination field has %u possible values, "
                   "less than specified n_links %d",
-                  s_, dst.n_bits, 1u << dst.n_bits, n_links);
+                  s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links);
     }
-    mp->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
-    mp->dst = htonl(dst.field->nxm_header);
 
     free(s);
 }
 
+/* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8)
+ * describes. */
 void
-multipath_format(const struct nx_action_multipath *mp, struct ds *s)
+multipath_format(const struct ofpact_multipath *mp, struct ds *s)
 {
     const char *fields, *algorithm;
 
-    uint16_t mp_fields    = ntohs(mp->fields);
-    uint16_t mp_algorithm = ntohs(mp->algorithm);
-
-    struct mf_subfield dst;
-
-    fields = flow_hash_fields_to_str(mp_fields);
+    fields = flow_hash_fields_to_str(mp->fields);
 
-    switch ((enum nx_mp_algorithm) mp_algorithm) {
+    switch (mp->algorithm) {
     case NX_MP_ALG_MODULO_N:
         algorithm = "modulo_n";
         break;
@@ -253,9 +277,8 @@ multipath_format(const struct nx_action_multipath *mp, struct ds *s)
     }
 
     ds_put_format(s, "multipath(%s,%"PRIu16",%s,%d,%"PRIu16",",
-                  fields, ntohs(mp->basis), algorithm, ntohs(mp->max_link) + 1,
-                  ntohl(mp->arg));
-    nxm_decode(&dst, mp->dst, mp->ofs_nbits);
-    mf_format_subfield(&dst, s);
+                  fields, mp->basis, algorithm, mp->max_link + 1,
+                  mp->arg);
+    mf_format_subfield(&mp->dst, s);
     ds_put_char(s, ')');
 }
index 3c4ff45..1b5160d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 struct ds;
 struct flow;
 struct nx_action_multipath;
-struct nx_action_reg_move;
+struct ofpact_multipath;
+struct ofpbuf;
 
 /* NXAST_MULTIPATH helper functions.
  *
  * See include/openflow/nicira-ext.h for NXAST_MULTIPATH specification.
  */
 
-enum ofperr multipath_check(const struct nx_action_multipath *,
+enum ofperr multipath_from_openflow(const struct nx_action_multipath *,
+                                    struct ofpact_multipath *);
+enum ofperr multipath_check(const struct ofpact_multipath *,
                             const struct flow *);
-void multipath_execute(const struct nx_action_multipath *, struct flow *);
+void multipath_to_nxast(const struct ofpact_multipath *,
+                        struct ofpbuf *openflow);
 
-void multipath_parse(struct nx_action_multipath *, const char *);
-void multipath_format(const struct nx_action_multipath *, struct ds *);
+void multipath_execute(const struct ofpact_multipath *, struct flow *);
+
+void multipath_parse(struct ofpact_multipath *, const char *);
+void multipath_format(const struct ofpact_multipath *, struct ds *);
 
 #endif /* multipath.h */
diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
new file mode 100644 (file)
index 0000000..f8b1188
--- /dev/null
@@ -0,0 +1,1485 @@
+/*
+ * Copyright (c) 2011 Gaetano Catalli.
+ *
+ * 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 <stdlib.h>
+#include <config.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <ifaddrs.h>
+#include <pcap/pcap.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_tap.h>
+#include <netinet/in.h>
+#include <net/if_mib.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+
+#include "rtbsd.h"
+#include "coverage.h"
+#include "dynamic-string.h"
+#include "fatal-signal.h"
+#include "netdev-provider.h"
+#include "ofpbuf.h"
+#include "openflow/openflow.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "shash.h"
+#include "svec.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(netdev_bsd);
+
+\f
+/*
+ * This file implements objects to access interfaces.
+ * Externally, interfaces are represented by two structures:
+ *   + struct netdev_dev, representing a network device,
+ *     containing e.g. name and a refcount;
+ *     We can have private variables by embedding the
+ *     struct netdev_dev into our own structure
+ *     (e.g. netdev_dev_bsd)
+ *
+ *   + struct netdev, representing an instance of an open netdev_dev.
+ *     The structure contains a pointer to the 'struct netdev'
+ *     representing the device. Again, private information
+ *     such as file descriptor etc. are stored in our
+ *     own struct netdev_bsd which includes a struct netdev.
+ *
+ * Both 'struct netdev' and 'struct netdev_dev' are referenced
+ * in containers which hold pointers to the data structures.
+ * We can reach our own struct netdev_XXX_bsd by putting a
+ * struct netdev_XXX within our own struct, and using CONTAINER_OF
+ * to access the parent structure.
+ */
+struct netdev_bsd {
+    struct netdev netdev;
+
+    int netdev_fd;   /* Selectable file descriptor for the network device.
+                        This descriptor will be used for polling operations */
+
+    pcap_t *pcap_handle;  /* Packet capture descriptor for a system network
+                             device */
+};
+
+struct netdev_dev_bsd {
+    struct netdev_dev netdev_dev;
+    unsigned int cache_valid;
+    unsigned int change_seq;
+
+    int ifindex;
+    uint8_t etheraddr[ETH_ADDR_LEN];
+    struct in_addr in4;
+    struct in6_addr in6;
+    int mtu;
+    int carrier;
+
+    bool tap_opened;
+    int tap_fd;         /* TAP character device, if any */
+};
+
+
+enum {
+    VALID_IFINDEX = 1 << 0,
+    VALID_ETHERADDR = 1 << 1,
+    VALID_IN4 = 1 << 2,
+    VALID_IN6 = 1 << 3,
+    VALID_MTU = 1 << 4,
+    VALID_CARRIER = 1 << 5
+};
+
+/* An AF_INET socket (used for ioctl operations). */
+static int af_inet_sock = -1;
+
+#define PCAP_SNAPLEN 2048
+
+
+/*
+ * Notifier used to invalidate device informations in case of status change.
+ *
+ * It will be registered with a 'rtbsd_notifier_register()' when the first
+ * device will be created with the call of either 'netdev_bsd_tap_create()' or
+ * 'netdev_bsd_system_create()'.
+ *
+ * The callback associated with this notifier ('netdev_bsd_cache_cb()') will
+ * invalidate cached information about the device.
+ */
+static struct rtbsd_notifier netdev_bsd_cache_notifier;
+static int cache_notifier_refcount;
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+
+static int netdev_bsd_do_ioctl(const struct netdev *, struct ifreq *,
+                                 unsigned long cmd, const char *cmd_name);
+static void destroy_tap(int fd, const char *name);
+static int get_flags(const struct netdev *, int *flagsp);
+static int set_flags(struct netdev *, int flags);
+static int do_set_addr(struct netdev *netdev,
+                       int ioctl_nr, const char *ioctl_name,
+                       struct in_addr addr);
+static int get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]);
+static int set_etheraddr(const char *netdev_name, int hwaddr_family,
+                         int hwaddr_len, const uint8_t[ETH_ADDR_LEN]);
+static int get_ifindex(const struct netdev *, int *ifindexp);
+
+static int netdev_bsd_init(void);
+
+static bool
+is_netdev_bsd_class(const struct netdev_class *netdev_class)
+{
+    return netdev_class->init == netdev_bsd_init;
+}
+
+static struct netdev_bsd *
+netdev_bsd_cast(const struct netdev *netdev)
+{
+    assert(is_netdev_bsd_class(netdev_dev_get_class(netdev_get_dev(netdev))));
+    return CONTAINER_OF(netdev, struct netdev_bsd, netdev);
+}
+
+static struct netdev_dev_bsd *
+netdev_dev_bsd_cast(const struct netdev_dev *netdev_dev)
+{
+    assert(is_netdev_bsd_class(netdev_dev_get_class(netdev_dev)));
+    return CONTAINER_OF(netdev_dev, struct netdev_dev_bsd, netdev_dev);
+}
+
+/* Initialize the AF_INET socket used for ioctl operations */
+static int
+netdev_bsd_init(void)
+{
+    static int status = -1;
+
+    if (status >= 0) {  /* already initialized */
+        return status;
+    }
+
+    af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
+    status = af_inet_sock >= 0 ? 0 : errno;
+
+    if (status) {
+        VLOG_ERR("failed to create inet socket: %s", strerror(status));
+    }
+
+    return status;
+}
+
+/*
+ * Perform periodic work needed by netdev. In BSD netdevs it checks for any
+ * interface status changes, and eventually calls all the user callbacks.
+ */
+static void
+netdev_bsd_run(void)
+{
+    rtbsd_notifier_run();
+}
+
+/*
+ * Arranges for poll_block() to wake up if the "run" member function needs to
+ * be called.
+ */
+static void
+netdev_bsd_wait(void)
+{
+    rtbsd_notifier_wait();
+}
+
+static void
+netdev_dev_bsd_changed(struct netdev_dev_bsd *dev)
+{
+    dev->change_seq++;
+    if (!dev->change_seq) {
+        dev->change_seq++;
+    }
+}
+
+/* Invalidate cache in case of interface status change. */
+static void
+netdev_bsd_cache_cb(const struct rtbsd_change *change,
+                      void *aux OVS_UNUSED)
+{
+    struct netdev_dev_bsd *dev;
+
+    if (change) {
+        struct netdev_dev *base_dev = netdev_dev_from_name(change->if_name);
+
+        if (base_dev) {
+            const struct netdev_class *netdev_class =
+                                                netdev_dev_get_class(base_dev);
+
+            if (is_netdev_bsd_class(netdev_class)) {
+                dev = netdev_dev_bsd_cast(base_dev);
+                dev->cache_valid = 0;
+                netdev_dev_bsd_changed(dev);
+            }
+        }
+    } else {
+        /*
+         * XXX the API is lacking, we should be able to iterate on the list of
+         * netdevs without having to store the info in a temp shash.
+         */
+        struct shash device_shash;
+        struct shash_node *node;
+
+        shash_init(&device_shash);
+        netdev_dev_get_devices(&netdev_bsd_class, &device_shash);
+        SHASH_FOR_EACH (node, &device_shash) {
+            dev = node->data;
+            dev->cache_valid = 0;
+            netdev_dev_bsd_changed(dev);
+        }
+        shash_destroy(&device_shash);
+    }
+}
+
+static int
+cache_notifier_ref(void)
+{
+    int ret = 0;
+
+    if (!cache_notifier_refcount) {
+        ret = rtbsd_notifier_register(&netdev_bsd_cache_notifier,
+                                                netdev_bsd_cache_cb, NULL);
+        if (ret) {
+            return ret;
+        }
+    }
+    cache_notifier_refcount++;
+    return 0;
+}
+
+static int
+cache_notifier_unref(void)
+{
+    cache_notifier_refcount--;
+    if (cache_notifier_refcount == 0) {
+        rtbsd_notifier_unregister(&netdev_bsd_cache_notifier);
+    }
+    return 0;
+}
+
+/* Allocate a netdev_dev_bsd structure */
+static int
+netdev_bsd_create_system(const struct netdev_class *class, const char *name,
+                  struct netdev_dev **netdev_devp)
+{
+    struct netdev_dev_bsd *netdev_dev;
+    int error;
+
+    error = cache_notifier_ref();
+    if (error) {
+        return error;
+    }
+
+    netdev_dev = xzalloc(sizeof *netdev_dev);
+    netdev_dev->change_seq = 1;
+    netdev_dev_init(&netdev_dev->netdev_dev, name, class);
+    *netdev_devp = &netdev_dev->netdev_dev;
+
+    return 0;
+}
+
+/*
+ * Allocate a netdev_dev_bsd structure with 'tap' class.
+ */
+static int
+netdev_bsd_create_tap(const struct netdev_class *class, const char *name,
+                  struct netdev_dev **netdev_devp)
+{
+    struct netdev_dev_bsd *netdev_dev = NULL;
+    int error = 0;
+    struct ifreq ifr;
+
+    error = cache_notifier_ref();
+    if (error) {
+        goto error;
+    }
+
+    /* allocate the device structure and set the internal flag */
+    netdev_dev = xzalloc(sizeof *netdev_dev);
+
+    memset(&ifr, 0, sizeof(ifr));
+
+    /* Create a tap device by opening /dev/tap.  The TAPGIFNAME ioctl is used
+     * to retrieve the name of the tap device. */
+    netdev_dev->tap_fd = open("/dev/tap", O_RDWR);
+    netdev_dev->change_seq = 1;
+    if (netdev_dev->tap_fd < 0) {
+        error = errno;
+        VLOG_WARN("opening \"/dev/tap\" failed: %s", strerror(error));
+        goto error_undef_notifier;
+    }
+
+    /* Retrieve tap name (e.g. tap0) */
+    if (ioctl(netdev_dev->tap_fd, TAPGIFNAME, &ifr) == -1) {
+        /* XXX Need to destroy the device? */
+        error = errno;
+        goto error_undef_notifier;
+    }
+
+    /* Change the name of the tap device */
+    ifr.ifr_data = (void *)name;
+    if (ioctl(af_inet_sock, SIOCSIFNAME, &ifr) == -1) {
+        error = errno;
+        destroy_tap(netdev_dev->tap_fd, ifr.ifr_name);
+        goto error_undef_notifier;
+    }
+
+    /* set non-blocking. */
+    error = set_nonblocking(netdev_dev->tap_fd);
+    if (error) {
+        destroy_tap(netdev_dev->tap_fd, 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);
+    if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) {
+        error = errno;
+        destroy_tap(netdev_dev->tap_fd, name);
+        goto error_undef_notifier;
+    }
+
+    /* initialize the device structure and
+     * link the structure to its netdev */
+    netdev_dev_init(&netdev_dev->netdev_dev, name, class);
+    *netdev_devp = &netdev_dev->netdev_dev;
+
+    return 0;
+
+error_undef_notifier:
+    cache_notifier_unref();
+error:
+    free(netdev_dev);
+    return error;
+}
+
+static void
+netdev_bsd_destroy(struct netdev_dev *netdev_dev_)
+{
+    struct netdev_dev_bsd *netdev_dev = netdev_dev_bsd_cast(netdev_dev_);
+
+    cache_notifier_unref();
+
+    if (netdev_dev->tap_fd >= 0 &&
+            !strcmp(netdev_dev_get_type(netdev_dev_), "tap")) {
+        destroy_tap(netdev_dev->tap_fd, netdev_dev_get_name(netdev_dev_));
+    }
+    free(netdev_dev);
+}
+
+
+static int
+netdev_bsd_open_system(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
+{
+    struct netdev_dev_bsd *netdev_dev = netdev_dev_bsd_cast(netdev_dev_);
+    struct netdev_bsd *netdev;
+    int error;
+    enum netdev_flags flags;
+
+    /* Allocate network device. */
+    netdev = xcalloc(1, sizeof *netdev);
+    netdev->netdev_fd = -1;
+    netdev_init(&netdev->netdev, netdev_dev_);
+
+    /* Verify that the netdev really exists by attempting to read its flags */
+    error = netdev_get_flags(&netdev->netdev, &flags);
+    if (error == ENXIO) {
+        goto error;
+    }
+
+    /* The first user that opens a tap port(from dpif_create_and_open()) will
+     * receive the file descriptor associated with the tap device. Instead, the
+     * following users will open the tap device as a normal 'system' device. */
+    if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap") &&
+            !netdev_dev->tap_opened) {
+        netdev_dev->tap_opened = true;
+        netdev->netdev_fd = netdev_dev->tap_fd;
+    }
+
+    *netdevp = &netdev->netdev;
+    return 0;
+
+error:
+    netdev_uninit(&netdev->netdev, true);
+    return error;
+}
+
+
+
+/* Close a 'netdev'. */
+static void
+netdev_bsd_close(struct netdev *netdev_)
+{
+    struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
+    if (netdev->netdev_fd >= 0 && strcmp(netdev_get_type(netdev_), "tap")) {
+        pcap_close(netdev->pcap_handle);
+    }
+
+    free(netdev);
+}
+
+static int
+netdev_bsd_listen(struct netdev *netdev_)
+{
+    struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+    char errbuf[PCAP_ERRBUF_SIZE];
+    int error;
+    int fd = -1;
+    int one = 1;
+
+    if (netdev->netdev_fd >= 0) {
+        return 0;
+    }
+
+    /* open the pcap device. The device is opened in non-promiscuous mode
+     * because the interface flags are manually set by the caller. */
+    errbuf[0] = '\0';
+    netdev->pcap_handle = pcap_open_live(netdev_get_name(netdev_), PCAP_SNAPLEN,
+                                    0, 1000, errbuf);
+    if (netdev->pcap_handle == NULL) {
+        VLOG_ERR("%s: pcap_open_live failed: %s",
+               netdev_get_name(netdev_), errbuf);
+        error = EIO;
+        goto error;
+    } else if (errbuf[0] !=  '\0') {
+        VLOG_WARN("%s: pcap_open_live: %s",
+               netdev_get_name(netdev_), errbuf);
+    }
+
+    netdev_dev_bsd_changed(netdev_dev_bsd_cast(netdev_get_dev(netdev_)));
+
+    /* initialize netdev->netdev_fd */
+    fd = pcap_get_selectable_fd(netdev->pcap_handle);
+    if (fd == -1) {
+        error = errno;
+        goto error;
+    }
+
+    /* Set non-blocking mode. Also the BIOCIMMEDIATE ioctl must be called
+     * on the file descriptor returned by pcap_get_selectable_fd to achieve
+     * a real non-blocking behaviour.*/
+    error = pcap_setnonblock(netdev->pcap_handle, 1, errbuf);
+    if (error == -1) {
+        error = errno;
+        goto error;
+    }
+
+    /* This call assure that reads return immediately upon packet reception.
+     * Otherwise, a read will block until either the kernel buffer becomes
+     * full or a timeout occurs. */
+    if(ioctl(fd, BIOCIMMEDIATE, &one) < 0 ) {
+        VLOG_ERR("ioctl(BIOCIMMEDIATE) on %s device failed: %s",
+               netdev_get_name(netdev_), strerror(errno));
+        error = errno;
+        goto error;
+    }
+
+    /* Capture only incoming packets */
+    error = pcap_setdirection(netdev->pcap_handle, PCAP_D_IN);
+    if (error == -1) {
+        error = errno;
+        goto error;
+    }
+
+    netdev->netdev_fd = fd;
+    return 0;
+
+error:
+    if (fd >= 0) {
+        close(netdev->netdev_fd);
+    }
+    return error;
+}
+
+
+/* The recv callback of the netdev class returns the number of bytes of the
+ * received packet.
+ *
+ * This can be done by the pcap_next() function. Unfortunately pcap_next() does
+ * not make difference between a missing packet on the capture interface and
+ * an error during the file capture.  We can use the pcap_dispatch() function
+ * instead, which is able to distinguish between errors and null packet.
+ *
+ * To make pcap_dispatch() returns the number of bytes read from the interface
+ * we need to define the following callback and argument.
+ */
+struct pcap_arg {
+    void *data;
+    int size;
+    int retval;
+};
+
+/*
+ * This callback will be executed on every captured packet.
+ *
+ * If the packet captured by pcap_dispatch() does not fit the pcap buffer,
+ * pcap returns a truncated packet and we follow this behavior.
+ *
+ * The argument args->retval is the packet size in bytes.
+ */
+static void
+proc_pkt(u_char *args_, const struct pcap_pkthdr *hdr, const u_char *packet)
+{
+    struct pcap_arg *args = (struct pcap_arg *)args_;
+
+    if (args->size < hdr->len) {
+        VLOG_WARN_RL(&rl, "packet truncated");
+        args->retval = args->size;
+    } else {
+        args->retval = hdr->len;
+    }
+
+    /* copy the packet to our buffer */
+    memcpy(args->data, packet, args->retval);
+}
+
+/*
+ * This function attempts to receive a packet from the specified network
+ * device. It is assumed that the network device is a system device or a tap
+ * device opened as a system one. In this case the read operation is performed
+ * on the 'netdev' pcap descriptor.
+ */
+static int
+netdev_bsd_recv_system(struct netdev_bsd *netdev, void *data, size_t size)
+{
+    struct pcap_arg arg;
+    int ret;
+
+    if (netdev->netdev_fd < 0) {
+        return -EAGAIN;
+    }
+
+    /* prepare the pcap argument to store the packet */
+    arg.size = size;
+    arg.data = data;
+
+    for (;;) {
+        ret = pcap_dispatch(netdev->pcap_handle, 1, proc_pkt, (u_char *)&arg);
+
+        if (ret > 0) {
+            return arg.retval; /* arg.retval < 0 is handled in the caller */
+        }
+        if (ret == -1) {
+            if (errno == EINTR) {
+                 continue;
+            }
+        }
+
+        return -EAGAIN;
+    }
+}
+
+/*
+ * This function attempts to receive a packet from the specified network
+ * device. It is assumed that the network device is a tap device and the
+ * 'netdev_fd' member of the 'netdev' structure is initialized with the tap
+ * file descriptor.
+ */
+static int
+netdev_bsd_recv_tap(struct netdev_bsd *netdev, void *data, size_t size)
+{
+    if (netdev->netdev_fd < 0) {
+        return -EAGAIN;
+    }
+
+    for (;;) {
+        ssize_t retval = read(netdev->netdev_fd, data, size);
+        if (retval >= 0) {
+            return retval;
+        } else if (errno != EINTR) {
+            if (errno != EAGAIN) {
+                VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
+                             strerror(errno), netdev->netdev.netdev_dev->name);
+            }
+            return -errno;
+        }
+    }
+}
+
+
+/*
+ * According with the nature of the device a different function must be called.
+ * If the device is the bridge local port the 'netdev_bsd_recv_tap' function
+ * must be called, otherwise the 'netdev_bsd_recv_system' function is called.
+ *
+ * type!="tap"                                        --->  system device.
+ * type=="tap" && netdev_fd == tap_fd                 --->  internal tap device
+ * type=="tap" && netdev_fd != tap_fd                 --->  internal tap device
+ *                                                          opened as a system
+ *                                                          device.
+ */
+static int
+netdev_bsd_recv(struct netdev *netdev_, void* data, size_t size)
+{
+    struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+    struct netdev_dev_bsd * netdev_dev =
+        netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+    if (!strcmp(netdev_get_type(netdev_), "tap") &&
+            netdev->netdev_fd == netdev_dev->tap_fd) {
+        return netdev_bsd_recv_tap(netdev, data, size);
+    } else {
+        return netdev_bsd_recv_system(netdev, data, size);
+    }
+}
+
+
+/*
+ * Registers with the poll loop to wake up from the next call to poll_block()
+ * when a packet is ready to be received with netdev_recv() on 'netdev'.
+ */
+static void
+netdev_bsd_recv_wait(struct netdev *netdev_)
+{
+    struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
+    if (netdev->netdev_fd >= 0) {
+        poll_fd_wait(netdev->netdev_fd, POLLIN);
+    }
+}
+
+/* Discards all packets waiting to be received from 'netdev'. */
+static int
+netdev_bsd_drain(struct netdev *netdev_)
+{
+    struct ifreq ifr;
+    struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
+    strcpy(ifr.ifr_name, netdev_get_name(netdev_));
+    if (ioctl(netdev->netdev_fd, BIOCFLUSH, &ifr) == -1) {
+        VLOG_DBG_RL(&rl, "%s: ioctl(BIOCFLUSH) failed: %s",
+                    netdev_get_name(netdev_), strerror(errno));
+        return errno;
+    }
+    return 0;
+}
+
+/*
+ * Send a packet on the specified network device. The device could be either a
+ * system or a tap device.
+ */
+static int
+netdev_bsd_send(struct netdev *netdev_, const void *data, size_t size)
+{
+    struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+    struct netdev_dev_bsd * netdev_dev =
+        netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+    if (netdev->netdev_fd < 0) {
+        return EPIPE;
+    }
+
+    for (;;) {
+        ssize_t retval;
+        if (!strcmp(netdev_get_type(netdev_), "tap") &&
+                netdev_dev->tap_fd == netdev->netdev_fd) {
+            retval = write(netdev->netdev_fd, data, size);
+        } else {
+            retval = pcap_inject(netdev->pcap_handle, data, size);
+        }
+        if (retval < 0) {
+            if (errno == EINTR) {
+                continue;
+            } else if (errno != EAGAIN) {
+                VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
+                             netdev_get_name(netdev_), strerror(errno));
+            }
+            return errno;
+        } else if (retval != size) {
+            VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%zd bytes of "
+                         "%zu) on %s", retval, size,
+                         netdev_get_name(netdev_));
+           return EMSGSIZE;
+        } else {
+            return 0;
+        }
+    }
+}
+
+/*
+ * Registers with the poll loop to wake up from the next call to poll_block()
+ * when the packet transmission queue has sufficient room to transmit a packet
+ * with netdev_send().
+ */
+static void
+netdev_bsd_send_wait(struct netdev *netdev_)
+{
+    struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
+    if (netdev->netdev_fd < 0) { /* Nothing to do. */
+        return;
+    }
+
+    if (strcmp(netdev_get_type(netdev_), "tap")) {
+        poll_fd_wait(netdev->netdev_fd, POLLOUT);
+    } else {
+        /* TAP device always accepts packets. */
+        poll_immediate_wake();
+    }
+}
+
+/*
+ * Attempts to set 'netdev''s MAC address to 'mac'.  Returns 0 if successful,
+ * otherwise a positive errno value.
+ */
+static int
+netdev_bsd_set_etheraddr(struct netdev *netdev_,
+                           const uint8_t mac[ETH_ADDR_LEN])
+{
+    struct netdev_dev_bsd *netdev_dev =
+                                netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+    int error;
+
+    if (!(netdev_dev->cache_valid & VALID_ETHERADDR)
+        || !eth_addr_equals(netdev_dev->etheraddr, mac)) {
+        error = set_etheraddr(netdev_get_name(netdev_), AF_LINK, ETH_ADDR_LEN,
+                              mac);
+        if (!error) {
+            netdev_dev->cache_valid |= VALID_ETHERADDR;
+            memcpy(netdev_dev->etheraddr, mac, ETH_ADDR_LEN);
+            netdev_dev_bsd_changed(netdev_dev);
+        }
+    } else {
+        error = 0;
+    }
+    return error;
+}
+
+/*
+ * Returns a pointer to 'netdev''s MAC address.  The caller must not modify or
+ * free the returned buffer.
+ */
+static int
+netdev_bsd_get_etheraddr(const struct netdev *netdev_,
+                           uint8_t mac[ETH_ADDR_LEN])
+{
+    struct netdev_dev_bsd *netdev_dev =
+        netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+    if (!(netdev_dev->cache_valid & VALID_ETHERADDR)) {
+        int error = get_etheraddr(netdev_get_name(netdev_),
+                                  netdev_dev->etheraddr);
+        if (error) {
+            return error;
+        }
+        netdev_dev->cache_valid |= VALID_ETHERADDR;
+    }
+    memcpy(mac, netdev_dev->etheraddr, ETH_ADDR_LEN);
+
+    return 0;
+}
+
+/*
+ * Returns the maximum size of transmitted (and received) packets on 'netdev',
+ * in bytes, not including the hardware header; thus, this is typically 1500
+ * bytes for Ethernet devices.
+ */
+static int
+netdev_bsd_get_mtu(const struct netdev *netdev_, int *mtup)
+{
+    struct netdev_dev_bsd *netdev_dev =
+        netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+    if (!(netdev_dev->cache_valid & VALID_MTU)) {
+        struct ifreq ifr;
+        int error;
+
+        error = netdev_bsd_do_ioctl(netdev_, &ifr, SIOCGIFMTU, "SIOCGIFMTU");
+        if (error) {
+            return error;
+        }
+        netdev_dev->mtu = ifr.ifr_mtu;
+        netdev_dev->cache_valid |= VALID_MTU;
+    }
+
+    *mtup = netdev_dev->mtu;
+    return 0;
+}
+
+static int
+netdev_bsd_get_ifindex(const struct netdev *netdev)
+{
+    int ifindex, error;
+
+    error = get_ifindex(netdev, &ifindex);
+    return error ? -error : ifindex;
+}
+
+static int
+netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier)
+{
+    struct netdev_dev_bsd *netdev_dev =
+        netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+    if (!(netdev_dev->cache_valid & VALID_CARRIER)) {
+        struct ifmediareq ifmr;
+
+        memset(&ifmr, 0, sizeof(ifmr));
+        strncpy(ifmr.ifm_name, netdev_get_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));
+            return errno;
+        }
+
+        netdev_dev->carrier = (ifmr.ifm_status & IFM_ACTIVE) == IFM_ACTIVE;
+        netdev_dev->cache_valid |= VALID_CARRIER;
+
+        /* If the interface doesn't report whether the media is active,
+         * just assume it is active. */
+        if ((ifmr.ifm_status & IFM_AVALID) == 0) {
+            netdev_dev->carrier = true;
+        }
+    }
+    *carrier = netdev_dev->carrier;
+
+    return 0;
+}
+
+/* Retrieves current device stats for 'netdev'. */
+static int
+netdev_bsd_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
+{
+    int if_count, i;
+    int mib[6];
+    size_t len;
+    struct ifmibdata ifmd;
+
+
+    mib[0] = CTL_NET;
+    mib[1] = PF_LINK;
+    mib[2] = NETLINK_GENERIC;
+    mib[3] = IFMIB_SYSTEM;
+    mib[4] = IFMIB_IFCOUNT;
+
+    len = sizeof(if_count);
+
+    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));
+        return errno;
+    }
+
+    mib[5] = IFDATA_GENERAL;
+    mib[3] = IFMIB_IFDATA;
+    len = sizeof(ifmd);
+    for (i = 1; i <= if_count; i++) {
+        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));
+            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 = 0;
+            stats->multicast = ifmd.ifmd_data.ifi_imcasts;
+            stats->collisions = ifmd.ifmd_data.ifi_collisions;
+
+            stats->rx_length_errors = 0;
+            stats->rx_over_errors = 0;
+            stats->rx_crc_errors = 0;
+            stats->rx_frame_errors = 0;
+            stats->rx_fifo_errors = 0;
+            stats->rx_missed_errors = 0;
+
+            stats->tx_aborted_errors = 0;
+            stats->tx_carrier_errors = 0;
+            stats->tx_fifo_errors = 0;
+            stats->tx_heartbeat_errors = 0;
+            stats->tx_window_errors = 0;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static uint32_t
+netdev_bsd_parse_media(int media)
+{
+    uint32_t supported = 0;
+    bool half_duplex = media & IFM_HDX ? true : false;
+
+    switch (IFM_SUBTYPE(media)) {
+    case IFM_10_2:
+    case IFM_10_5:
+    case IFM_10_STP:
+    case IFM_10_T:
+        supported |= half_duplex ? NETDEV_F_10MB_HD : NETDEV_F_10MB_FD;
+        supported |= NETDEV_F_COPPER;
+        break;
+
+    case IFM_10_FL:
+        supported |= half_duplex ? NETDEV_F_10MB_HD : NETDEV_F_10MB_FD;
+        supported |= NETDEV_F_FIBER;
+        break;
+
+    case IFM_100_T2:
+    case IFM_100_T4:
+    case IFM_100_TX:
+    case IFM_100_VG:
+        supported |= half_duplex ? NETDEV_F_100MB_HD : NETDEV_F_100MB_FD;
+        supported |= NETDEV_F_COPPER;
+        break;
+
+    case IFM_100_FX:
+        supported |= half_duplex ? NETDEV_F_100MB_HD : NETDEV_F_100MB_FD;
+        supported |= NETDEV_F_FIBER;
+        break;
+
+    case IFM_1000_CX:
+    case IFM_1000_T:
+        supported |= half_duplex ? NETDEV_F_1GB_HD : NETDEV_F_1GB_FD;
+        supported |= NETDEV_F_COPPER;
+        break;
+
+    case IFM_1000_LX:
+    case IFM_1000_SX:
+        supported |= half_duplex ? NETDEV_F_1GB_HD : NETDEV_F_1GB_FD;
+        supported |= NETDEV_F_FIBER;
+        break;
+
+    case IFM_10G_CX4:
+        supported |= NETDEV_F_10GB_FD;
+        supported |= NETDEV_F_COPPER;
+        break;
+
+    case IFM_10G_LR:
+    case IFM_10G_SR:
+        supported |= NETDEV_F_10GB_FD;
+        supported |= NETDEV_F_FIBER;
+        break;
+
+    default:
+        return 0;
+    }
+
+    if (IFM_SUBTYPE(media) == IFM_AUTO) {
+        supported |= NETDEV_F_AUTONEG;
+    }
+    /*
+    if (media & IFM_ETH_FMASK) {
+        supported |= NETDEV_F_PAUSE;
+    }
+    */
+
+    return supported;
+}
+
+/*
+ * Stores the features supported by 'netdev' into each of '*current',
+ * '*advertised', '*supported', and '*peer' that are non-null.  Each value is a
+ * bitmap of "enum ofp_port_features" bits, in host byte order.  Returns 0 if
+ * successful, otherwise a positive errno value.  On failure, all of the
+ * passed-in values are set to 0.
+ */
+static int
+netdev_bsd_get_features(const struct netdev *netdev,
+                          enum netdev_features *current, uint32_t *advertised,
+                          enum netdev_features *supported, uint32_t *peer)
+{
+    struct ifmediareq ifmr;
+    int *media_list;
+    int i;
+    int error;
+
+
+    /* XXX Look into SIOCGIFCAP instead of SIOCGIFMEDIA */
+
+    memset(&ifmr, 0, sizeof(ifmr));
+    strncpy(ifmr.ifm_name, netdev_get_name(netdev), sizeof ifmr.ifm_name);
+
+    /* We make two SIOCGIFMEDIA ioctl calls.  The first to determine the
+     * number of supported modes, and a second with a buffer to retrieve
+     * them. */
+    if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+        VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
+                    netdev_get_name(netdev), strerror(errno));
+        return errno;
+    }
+
+    media_list = xcalloc(ifmr.ifm_count, sizeof(int));
+    ifmr.ifm_ulist = media_list;
+
+    if (!IFM_TYPE(ifmr.ifm_current) & IFM_ETHER) {
+        VLOG_DBG_RL(&rl, "%s: doesn't appear to be ethernet",
+                    netdev_get_name(netdev));
+        error = EINVAL;
+        goto cleanup;
+    }
+
+    if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+        VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
+                    netdev_get_name(netdev), strerror(errno));
+        error = errno;
+        goto cleanup;
+    }
+
+    /* Current settings. */
+    *current = netdev_bsd_parse_media(ifmr.ifm_active);
+
+    /* Advertised features. */
+    *advertised = netdev_bsd_parse_media(ifmr.ifm_current);
+
+    /* Supported features. */
+    *supported = 0;
+    for (i = 0; i < ifmr.ifm_count; i++) {
+        *supported |= netdev_bsd_parse_media(ifmr.ifm_ulist[i]);
+    }
+
+    /* Peer advertisements. */
+    *peer = 0;                  /* XXX */
+
+    error = 0;
+cleanup:
+    free(media_list);
+    return error;
+}
+
+/*
+ * If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if
+ * 'in4' is non-null) and returns true.  Otherwise, returns false.
+ */
+static int
+netdev_bsd_get_in4(const struct netdev *netdev_, struct in_addr *in4,
+                   struct in_addr *netmask)
+{
+    struct netdev_dev_bsd *netdev_dev =
+        netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+    if (!(netdev_dev->cache_valid & VALID_IN4)) {
+        const struct sockaddr_in *sin;
+        struct ifreq ifr;
+        int error;
+
+        ifr.ifr_addr.sa_family = AF_INET;
+        error = netdev_bsd_do_ioctl(netdev_, &ifr,
+                                      SIOCGIFADDR, "SIOCGIFADDR");
+        if (error) {
+            return error;
+        }
+
+        sin = (struct sockaddr_in *) &ifr.ifr_addr;
+        netdev_dev->in4 = sin->sin_addr;
+        netdev_dev->cache_valid |= VALID_IN4;
+        error = netdev_bsd_do_ioctl(netdev_, &ifr,
+                                      SIOCGIFNETMASK, "SIOCGIFNETMASK");
+        if (error) {
+            return error;
+        }
+        *netmask = ((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr;
+    }
+    *in4 = netdev_dev->in4;
+
+    return in4->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0;
+}
+
+/*
+ * Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask.  If
+ * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared.  Returns a
+ * positive errno value.
+ */
+static int
+netdev_bsd_set_in4(struct netdev *netdev_, struct in_addr addr,
+                     struct in_addr mask)
+{
+    struct netdev_dev_bsd *netdev_dev =
+        netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+    int error;
+
+    error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", addr);
+    if (!error) {
+        netdev_dev->cache_valid |= VALID_IN4;
+        netdev_dev->in4 = addr;
+        if (addr.s_addr != INADDR_ANY) {
+            error = do_set_addr(netdev_, SIOCSIFNETMASK,
+                                "SIOCSIFNETMASK", mask);
+        }
+        netdev_dev_bsd_changed(netdev_dev);
+    }
+    return error;
+}
+
+static int
+netdev_bsd_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
+{
+    struct netdev_dev_bsd *netdev_dev =
+                                netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+    if (!(netdev_dev->cache_valid & VALID_IN6)) {
+        struct ifaddrs *ifa, *head;
+        struct sockaddr_in6 *sin6;
+        const char *netdev_name = netdev_get_name(netdev_);
+
+        if (getifaddrs(&head) != 0) {
+            VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name,
+                    strerror(errno));
+            return errno;
+        }
+
+        for (ifa = head; ifa; ifa = ifa->ifa_next) {
+            if (ifa->ifa_addr->sa_family == AF_INET6 &&
+                    !strcmp(ifa->ifa_name, netdev_name)) {
+                sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+                if (sin6) {
+                    memcpy(&netdev_dev->in6, &sin6->sin6_addr, sin6->sin6_len);
+                    netdev_dev->cache_valid |= VALID_IN6;
+                    *in6 = netdev_dev->in6;
+                    freeifaddrs(head);
+                    return 0;
+                }
+            }
+        }
+        return EADDRNOTAVAIL;
+    }
+    *in6 = netdev_dev->in6;
+    return 0;
+}
+
+static void
+make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr)
+{
+    struct sockaddr_in sin;
+    memset(&sin, 0, sizeof sin);
+    sin.sin_family = AF_INET;
+    sin.sin_addr = addr;
+    sin.sin_port = 0;
+
+    memset(sa, 0, sizeof *sa);
+    memcpy(sa, &sin, sizeof sin);
+}
+
+static int
+do_set_addr(struct netdev *netdev,
+            int ioctl_nr, const char *ioctl_name, struct in_addr addr)
+{
+    struct ifreq ifr;
+    make_in4_sockaddr(&ifr.ifr_addr, addr);
+    return netdev_bsd_do_ioctl(netdev, &ifr, ioctl_nr, ioctl_name);
+}
+
+static int
+nd_to_iff_flags(enum netdev_flags nd)
+{
+    int iff = 0;
+    if (nd & NETDEV_UP) {
+        iff |= IFF_UP;
+    }
+    if (nd & NETDEV_PROMISC) {
+        iff |= IFF_PROMISC;
+        iff |= IFF_PPROMISC;
+    }
+    return iff;
+}
+
+static int
+iff_to_nd_flags(int iff)
+{
+    enum netdev_flags nd = 0;
+    if (iff & IFF_UP) {
+        nd |= NETDEV_UP;
+    }
+    if (iff & IFF_PROMISC) {
+        nd |= NETDEV_PROMISC;
+    }
+    return nd;
+}
+
+static int
+netdev_bsd_update_flags(struct netdev *netdev, enum netdev_flags off,
+                          enum netdev_flags on, enum netdev_flags *old_flagsp)
+{
+    int old_flags, new_flags;
+    int error;
+
+    error = get_flags(netdev, &old_flags);
+    if (!error) {
+        *old_flagsp = iff_to_nd_flags(old_flags);
+        new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
+        if (new_flags != old_flags) {
+            error = set_flags(netdev, new_flags);
+            netdev_dev_bsd_changed(netdev_dev_bsd_cast(netdev_get_dev(netdev)));
+        }
+    }
+    return error;
+}
+
+static unsigned int
+netdev_bsd_change_seq(const struct netdev *netdev)
+{
+    return netdev_dev_bsd_cast(netdev_get_dev(netdev))->change_seq;
+}
+
+
+const struct netdev_class netdev_bsd_class = {
+    "system",
+
+    netdev_bsd_init,
+    netdev_bsd_run,
+    netdev_bsd_wait,
+    netdev_bsd_create_system,
+    netdev_bsd_destroy,
+    NULL, /* get_config */
+    NULL, /* set_config */
+    netdev_bsd_open_system,
+    netdev_bsd_close,
+
+    netdev_bsd_listen,
+
+    netdev_bsd_recv,
+    netdev_bsd_recv_wait,
+    netdev_bsd_drain,
+
+    netdev_bsd_send,
+    netdev_bsd_send_wait,
+
+    netdev_bsd_set_etheraddr,
+    netdev_bsd_get_etheraddr,
+    netdev_bsd_get_mtu,
+    NULL, /* set_mtu */
+    netdev_bsd_get_ifindex,
+    netdev_bsd_get_carrier,
+    NULL, /* get_carrier_resets */
+    NULL, /* set_miimon_interval */
+    netdev_bsd_get_stats,
+    NULL, /* set_stats */
+
+    netdev_bsd_get_features,
+    NULL, /* set_advertisement */
+    NULL, /* set_policing */
+    NULL, /* get_qos_type */
+    NULL, /* get_qos_capabilities */
+    NULL, /* get_qos */
+    NULL, /* set_qos */
+    NULL, /* get_queue */
+    NULL, /* set_queue */
+    NULL, /* delete_queue */
+    NULL, /* get_queue_stats */
+    NULL, /* dump_queue */
+    NULL, /* dump_queue_stats */
+
+    netdev_bsd_get_in4,
+    netdev_bsd_set_in4,
+    netdev_bsd_get_in6,
+    NULL, /* add_router */
+    NULL, /* get_next_hop */
+    NULL, /* get_drv_info */
+    NULL, /* arp_lookup */
+
+    netdev_bsd_update_flags,
+
+    netdev_bsd_change_seq
+};
+
+const struct netdev_class netdev_tap_class = {
+    "tap",
+
+    netdev_bsd_init,
+    netdev_bsd_run,
+    netdev_bsd_wait,
+    netdev_bsd_create_tap,
+    netdev_bsd_destroy,
+    NULL, /* get_config */
+    NULL, /* set_config */
+    netdev_bsd_open_system,
+    netdev_bsd_close,
+
+    netdev_bsd_listen,
+
+    netdev_bsd_recv,
+    netdev_bsd_recv_wait,
+    netdev_bsd_drain,
+
+    netdev_bsd_send,
+    netdev_bsd_send_wait,
+
+    netdev_bsd_set_etheraddr,
+    netdev_bsd_get_etheraddr,
+    netdev_bsd_get_mtu,
+    NULL, /* set_mtu */
+    netdev_bsd_get_ifindex,
+    netdev_bsd_get_carrier,
+    NULL, /* get_carrier_resets */
+    NULL, /* set_miimon_interval */
+    netdev_bsd_get_stats,
+    NULL, /* set_stats */
+
+    netdev_bsd_get_features,
+    NULL, /* set_advertisement */
+    NULL, /* set_policing */
+    NULL, /* get_qos_type */
+    NULL, /* get_qos_capabilities */
+    NULL, /* get_qos */
+    NULL, /* set_qos */
+    NULL, /* get_queue */
+    NULL, /* set_queue */
+    NULL, /* delete_queue */
+    NULL, /* get_queue_stats */
+    NULL, /* dump_queue */
+    NULL, /* dump_queue_stats */
+
+    netdev_bsd_get_in4,
+    netdev_bsd_set_in4,
+    netdev_bsd_get_in6,
+    NULL, /* add_router */
+    NULL, /* get_next_hop */
+    NULL, /* get_drv_info */
+    NULL, /* arp_lookup */
+
+    netdev_bsd_update_flags,
+
+    netdev_bsd_change_seq
+};
+\f
+
+static void
+destroy_tap(int fd, const char *name)
+{
+    struct ifreq ifr;
+
+    close(fd);
+    strcpy(ifr.ifr_name, name);
+    /* XXX What to do if this call fails? */
+    ioctl(af_inet_sock, SIOCIFDESTROY, &ifr);
+}
+
+static int
+get_flags(const struct netdev *netdev, int *flags)
+{
+    struct ifreq ifr;
+    int error;
+
+    error = netdev_bsd_do_ioctl(netdev, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS");
+
+    *flags = 0xFFFF0000 & (ifr.ifr_flagshigh << 16);
+    *flags |= 0x0000FFFF & ifr.ifr_flags;
+
+    return error;
+}
+
+static int
+set_flags(struct netdev *netdev, int flags)
+{
+    struct ifreq ifr;
+
+    ifr.ifr_flags = 0x0000FFFF & flags;
+    ifr.ifr_flagshigh = (0xFFFF0000 & flags) >> 16;
+
+    return netdev_bsd_do_ioctl(netdev, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
+}
+
+static int
+get_ifindex(const struct netdev *netdev_, int *ifindexp)
+{
+    struct netdev_dev_bsd *netdev_dev =
+                                netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+    *ifindexp = 0;
+    if (!(netdev_dev->cache_valid & VALID_IFINDEX)) {
+        int ifindex = if_nametoindex(netdev_get_name(netdev_));
+        if (ifindex <= 0) {
+            return errno;
+        }
+        netdev_dev->cache_valid |= VALID_IFINDEX;
+        netdev_dev->ifindex = ifindex;
+    }
+    *ifindexp = netdev_dev->ifindex;
+    return 0;
+}
+
+static int
+get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
+{
+    struct ifaddrs *head;
+    struct ifaddrs *ifa;
+    struct sockaddr_dl *sdl;
+
+    if (getifaddrs(&head) != 0) {
+        VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name,
+                strerror(errno));
+        return errno;
+    }
+
+    for (ifa = head; ifa; ifa = ifa->ifa_next) {
+        if (ifa->ifa_addr->sa_family == AF_LINK) {
+            if (!strcmp(ifa->ifa_name, netdev_name)) {
+                sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+                if (sdl) {
+                    memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+                    freeifaddrs(head);
+                    return 0;
+                }
+            }
+        }
+    }
+
+    VLOG_ERR("could not find ethernet address for %s device", netdev_name);
+    freeifaddrs(head);
+    return ENODEV;
+}
+
+static int
+set_etheraddr(const char *netdev_name, int hwaddr_family,
+              int hwaddr_len, const uint8_t mac[ETH_ADDR_LEN])
+{
+    struct ifreq ifr;
+
+    memset(&ifr, 0, sizeof ifr);
+    strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
+    ifr.ifr_addr.sa_family = hwaddr_family;
+    ifr.ifr_addr.sa_len = hwaddr_len;
+    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));
+        return errno;
+    }
+    return 0;
+}
+
+static int
+netdev_bsd_do_ioctl(const struct netdev *netdev, struct ifreq *ifr,
+                    unsigned long cmd, const char *cmd_name)
+{
+    strncpy(ifr->ifr_name, netdev_get_name(netdev), sizeof ifr->ifr_name);
+    if (ioctl(af_inet_sock, cmd, ifr) == -1) {
+        VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s",
+                    netdev_get_name(netdev), cmd_name, strerror(errno));
+        return errno;
+    }
+    return 0;
+}
index 083a4dd..6aa4084 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -55,9 +55,14 @@ struct netdev_dummy {
 
 static struct shash dummy_netdev_devs = SHASH_INITIALIZER(&dummy_netdev_devs);
 
+static unixctl_cb_func netdev_dummy_set_admin_state;
 static int netdev_dummy_create(const struct netdev_class *, const char *,
                                struct netdev_dev **);
-static void netdev_dummy_poll_notify(const struct netdev *);
+static void netdev_dev_dummy_poll_notify(struct netdev_dev_dummy *);
+static int netdev_dev_dummy_update_flags(struct netdev_dev_dummy *,
+                                         enum netdev_flags off,
+                                         enum netdev_flags on,
+                                         enum netdev_flags *old_flagsp);
 
 static bool
 is_dummy_class(const struct netdev_class *class)
@@ -201,7 +206,7 @@ netdev_dummy_set_etheraddr(struct netdev *netdev,
 
     if (!eth_addr_equals(dev->hwaddr, mac)) {
         memcpy(dev->hwaddr, mac, ETH_ADDR_LEN);
-        netdev_dummy_poll_notify(netdev);
+        netdev_dev_dummy_poll_notify(dev);
     }
 
     return 0;
@@ -266,6 +271,14 @@ netdev_dummy_update_flags(struct netdev *netdev,
     struct netdev_dev_dummy *dev =
         netdev_dev_dummy_cast(netdev_get_dev(netdev));
 
+    return netdev_dev_dummy_update_flags(dev, off, on, old_flagsp);
+}
+
+static int
+netdev_dev_dummy_update_flags(struct netdev_dev_dummy *dev,
+                              enum netdev_flags off, enum netdev_flags on,
+                              enum netdev_flags *old_flagsp)
+{
     if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) {
         return EINVAL;
     }
@@ -274,7 +287,7 @@ netdev_dummy_update_flags(struct netdev *netdev,
     dev->flags |= on;
     dev->flags &= ~off;
     if (*old_flagsp != dev->flags) {
-        netdev_dummy_poll_notify(netdev);
+        netdev_dev_dummy_poll_notify(dev);
     }
     return 0;
 }
@@ -288,11 +301,8 @@ netdev_dummy_change_seq(const struct netdev *netdev)
 /* Helper functions. */
 
 static void
-netdev_dummy_poll_notify(const struct netdev *netdev)
+netdev_dev_dummy_poll_notify(struct netdev_dev_dummy *dev)
 {
-    struct netdev_dev_dummy *dev =
-        netdev_dev_dummy_cast(netdev_get_dev(netdev));
-
     dev->change_seq++;
     if (!dev->change_seq) {
         dev->change_seq++;
@@ -443,11 +453,62 @@ netdev_dummy_receive(struct unixctl_conn *conn,
     }
 }
 
+static void
+netdev_dev_dummy_set_admin_state(struct netdev_dev_dummy *dev,
+                                 bool admin_state)
+{
+    enum netdev_flags old_flags;
+
+    if (admin_state) {
+        netdev_dev_dummy_update_flags(dev, 0, NETDEV_UP, &old_flags);
+    } else {
+        netdev_dev_dummy_update_flags(dev, NETDEV_UP, 0, &old_flags);
+    }
+}
+
+static void
+netdev_dummy_set_admin_state(struct unixctl_conn *conn, int argc,
+                             const char *argv[], void *aux OVS_UNUSED)
+{
+    bool up;
+
+    if (!strcasecmp(argv[argc - 1], "up")) {
+        up = true;
+    } else if ( !strcasecmp(argv[argc - 1], "down")) {
+        up = false;
+    } else {
+        unixctl_command_reply_error(conn, "Invalid Admin State");
+        return;
+    }
+
+    if (argc > 2) {
+        struct netdev_dev_dummy *dummy_dev;
+
+        dummy_dev  = shash_find_data(&dummy_netdev_devs, argv[1]);
+        if (dummy_dev) {
+            netdev_dev_dummy_set_admin_state(dummy_dev, up);
+        } else {
+            unixctl_command_reply_error(conn, "Unknown Dummy Interface");
+            return;
+        }
+    } else {
+        struct shash_node *node;
+
+        SHASH_FOR_EACH (node, &dummy_netdev_devs) {
+            netdev_dev_dummy_set_admin_state(node->data, up);
+        }
+    }
+    unixctl_command_reply(conn, "OK");
+}
+
 void
 netdev_dummy_register(bool override)
 {
     unixctl_command_register("netdev-dummy/receive", "NAME PACKET|FLOW...",
                              2, INT_MAX, netdev_dummy_receive, NULL);
+    unixctl_command_register("netdev-dummy/set-admin-state",
+                             "[netdev] up|down", 1, 2,
+                             netdev_dummy_set_admin_state, NULL);
 
     if (override) {
         struct sset types;
index e5acfba..08937de 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -78,7 +78,8 @@ COVERAGE_DEFINE(netdev_arp_lookup);
 COVERAGE_DEFINE(netdev_get_ifindex);
 COVERAGE_DEFINE(netdev_get_hwaddr);
 COVERAGE_DEFINE(netdev_set_hwaddr);
-COVERAGE_DEFINE(netdev_ethtool);
+COVERAGE_DEFINE(netdev_get_ethtool);
+COVERAGE_DEFINE(netdev_set_ethtool);
 
 \f
 /* These were introduced in Linux 2.6.14, so they might be missing if we have
@@ -182,7 +183,7 @@ struct tc_ops {
      *
      * (This function is null for tc_ops_other, which cannot be installed.  For
      * other TC classes it should always be nonnull.) */
-    int (*tc_install)(struct netdev *netdev, const struct shash *details);
+    int (*tc_install)(struct netdev *netdev, const struct smap *details);
 
     /* Called when the netdev code determines (through a Netlink query) that
      * this TC class's qdisc is installed on 'netdev', but we didn't install
@@ -222,7 +223,7 @@ struct tc_ops {
      *
      * This function may be null if 'tc' is not configurable.
      */
-    int (*qdisc_get)(const struct netdev *netdev, struct shash *details);
+    int (*qdisc_get)(const struct netdev *netdev, struct smap *details);
 
     /* Reconfigures 'netdev->tc' according to 'details', performing any
      * required Netlink calls to complete the reconfiguration.
@@ -233,7 +234,7 @@ struct tc_ops {
      *
      * This function may be null if 'tc' is not configurable.
      */
-    int (*qdisc_set)(struct netdev *, const struct shash *details);
+    int (*qdisc_set)(struct netdev *, const struct smap *details);
 
     /* Retrieves details of 'queue' on 'netdev->tc' into 'details'.  'queue' is
      * one of the 'struct tc_queue's within 'netdev->tc->queues'.
@@ -249,7 +250,7 @@ struct tc_ops {
      * This function may be null if 'tc' does not have queues ('n_queues' is
      * 0). */
     int (*class_get)(const struct netdev *netdev, const struct tc_queue *queue,
-                     struct shash *details);
+                     struct smap *details);
 
     /* Configures or reconfigures 'queue_id' on 'netdev->tc' according to
      * 'details', perfoming any required Netlink calls to complete the
@@ -263,7 +264,7 @@ struct tc_ops {
      * This function may be null if 'tc' does not have queues or its queues are
      * not configurable. */
     int (*class_set)(struct netdev *, unsigned int queue_id,
-                     const struct shash *details);
+                     const struct smap *details);
 
     /* Deletes 'queue' from 'netdev->tc'.  'queue' is one of the 'struct
      * tc_queue's within 'netdev->tc->queues'.
@@ -507,6 +508,7 @@ netdev_linux_get_drvinfo(struct netdev_dev_linux *netdev_dev)
         return 0;
     }
 
+    COVERAGE_INC(netdev_get_ethtool);
     memset(&netdev_dev->drvinfo, 0, sizeof netdev_dev->drvinfo);
     error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name,
                                     (struct ethtool_cmd *)&netdev_dev->drvinfo,
@@ -955,7 +957,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
             sll.sll_family = AF_PACKET;
             sll.sll_ifindex = ifindex;
 
-            iov.iov_base = (void *) data;
+            iov.iov_base = CONST_CAST(void *, data);
             iov.iov_len = size;
 
             msg.msg_name = &sll;
@@ -1205,6 +1207,7 @@ netdev_linux_get_miimon(const char *name, bool *miimon)
         VLOG_DBG_RL(&rl, "%s: failed to query MII, falling back to ethtool",
                     name);
 
+        COVERAGE_INC(netdev_get_ethtool);
         memset(&ecmd, 0, sizeof ecmd);
         error = netdev_linux_do_ethtool(name, &ecmd, ETHTOOL_GLINK,
                                         "ETHTOOL_GLINK");
@@ -1498,6 +1501,7 @@ netdev_linux_read_features(struct netdev_dev_linux *netdev_dev)
         return;
     }
 
+    COVERAGE_INC(netdev_get_ethtool);
     memset(&ecmd, 0, sizeof ecmd);
     error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name, &ecmd,
                                     ETHTOOL_GSET, "ETHTOOL_GSET");
@@ -1654,6 +1658,7 @@ netdev_linux_set_advertisements(struct netdev *netdev,
     struct ethtool_cmd ecmd;
     int error;
 
+    COVERAGE_INC(netdev_get_ethtool);
     memset(&ecmd, 0, sizeof ecmd);
     error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
                                     ETHTOOL_GSET, "ETHTOOL_GSET");
@@ -1698,6 +1703,7 @@ netdev_linux_set_advertisements(struct netdev *netdev,
     if (advertise & NETDEV_F_PAUSE_ASYM) {
         ecmd.advertising |= ADVERTISED_Asym_Pause;
     }
+    COVERAGE_INC(netdev_set_ethtool);
     return netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
                                    ETHTOOL_SSET, "ETHTOOL_SSET");
 }
@@ -1892,7 +1898,7 @@ netdev_linux_get_qos_capabilities(const struct netdev *netdev OVS_UNUSED,
 
 static int
 netdev_linux_get_qos(const struct netdev *netdev,
-                     const char **typep, struct shash *details)
+                     const char **typep, struct smap *details)
 {
     struct netdev_dev_linux *netdev_dev =
                                 netdev_dev_linux_cast(netdev_get_dev(netdev));
@@ -1911,7 +1917,7 @@ netdev_linux_get_qos(const struct netdev *netdev,
 
 static int
 netdev_linux_set_qos(struct netdev *netdev,
-                     const char *type, const struct shash *details)
+                     const char *type, const struct smap *details)
 {
     struct netdev_dev_linux *netdev_dev =
                                 netdev_dev_linux_cast(netdev_get_dev(netdev));
@@ -1948,7 +1954,7 @@ netdev_linux_set_qos(struct netdev *netdev,
 
 static int
 netdev_linux_get_queue(const struct netdev *netdev,
-                       unsigned int queue_id, struct shash *details)
+                       unsigned int queue_id, struct smap *details)
 {
     struct netdev_dev_linux *netdev_dev =
                                 netdev_dev_linux_cast(netdev_get_dev(netdev));
@@ -1967,7 +1973,7 @@ netdev_linux_get_queue(const struct netdev *netdev,
 
 static int
 netdev_linux_set_queue(struct netdev *netdev,
-                       unsigned int queue_id, const struct shash *details)
+                       unsigned int queue_id, const struct smap *details)
 {
     struct netdev_dev_linux *netdev_dev =
                                 netdev_dev_linux_cast(netdev_get_dev(netdev));
@@ -2049,7 +2055,7 @@ netdev_linux_dump_queues(const struct netdev *netdev,
     struct netdev_dev_linux *netdev_dev =
                                 netdev_dev_linux_cast(netdev_get_dev(netdev));
     struct tc_queue *queue, *next_queue;
-    struct shash details;
+    struct smap details;
     int last_error;
     int error;
 
@@ -2061,10 +2067,10 @@ netdev_linux_dump_queues(const struct netdev *netdev,
     }
 
     last_error = 0;
-    shash_init(&details);
+    smap_init(&details);
     HMAP_FOR_EACH_SAFE (queue, next_queue, hmap_node,
                         &netdev_dev->tc->queues) {
-        shash_clear(&details);
+        smap_clear(&details);
 
         error = netdev_dev->tc->ops->class_get(netdev, queue, &details);
         if (!error) {
@@ -2073,7 +2079,7 @@ netdev_linux_dump_queues(const struct netdev *netdev,
             last_error = error;
         }
     }
-    shash_destroy(&details);
+    smap_destroy(&details);
 
     return last_error;
 }
@@ -2318,7 +2324,7 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop,
 }
 
 static int
-netdev_linux_get_drv_info(const struct netdev *netdev, struct shash *sh)
+netdev_linux_get_drv_info(const struct netdev *netdev, struct smap *smap)
 {
     int error;
     struct netdev_dev_linux *netdev_dev =
@@ -2326,17 +2332,18 @@ netdev_linux_get_drv_info(const struct netdev *netdev, struct shash *sh)
 
     error = netdev_linux_get_drvinfo(netdev_dev);
     if (!error) {
-        shash_add(sh, "driver_name", xstrdup(netdev_dev->drvinfo.driver));
-        shash_add(sh, "driver_version", xstrdup(netdev_dev->drvinfo.version));
-        shash_add(sh, "firmware_version", xstrdup(netdev_dev->drvinfo.fw_version));
+        smap_add(smap, "driver_name", netdev_dev->drvinfo.driver);
+        smap_add(smap, "driver_version", netdev_dev->drvinfo.version);
+        smap_add(smap, "firmware_version", netdev_dev->drvinfo.fw_version);
     }
     return error;
 }
 
 static int
-netdev_internal_get_drv_info(const struct netdev *netdev OVS_UNUSED, struct shash *sh)
+netdev_internal_get_drv_info(const struct netdev *netdev OVS_UNUSED,
+                             struct smap *smap)
 {
-    shash_add(sh, "driver_name", xstrdup("openvswitch"));
+    smap_add(smap, "driver_name", "openvswitch");
     return 0;
 }
 
@@ -2418,8 +2425,8 @@ netdev_linux_update_flags(struct netdev *netdev, enum netdev_flags off,
 }
 
 static int
-netdev_tap_pl_update_flags(struct netdev *netdev, enum netdev_flags off,
-                          enum netdev_flags on, enum netdev_flags *old_flagsp)
+netdev_tap_pl_update_flags(struct netdev *netdev OVS_UNUSED, enum netdev_flags off OVS_UNUSED,
+                          enum netdev_flags on OVS_UNUSED, enum netdev_flags *old_flagsp OVS_UNUSED)
 {
     return 0;
 }
@@ -2719,11 +2726,11 @@ htb_parse_tcmsg__(struct ofpbuf *tcmsg, unsigned int *queue_id,
 
 static void
 htb_parse_qdisc_details__(struct netdev *netdev,
-                          const struct shash *details, struct htb_class *hc)
+                          const struct smap *details, struct htb_class *hc)
 {
     const char *max_rate_s;
 
-    max_rate_s = shash_find_data(details, "max-rate");
+    max_rate_s = smap_get(details, "max-rate");
     hc->max_rate = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0;
     if (!hc->max_rate) {
         enum netdev_features current;
@@ -2738,13 +2745,13 @@ htb_parse_qdisc_details__(struct netdev *netdev,
 
 static int
 htb_parse_class_details__(struct netdev *netdev,
-                          const struct shash *details, struct htb_class *hc)
+                          const struct smap *details, struct htb_class *hc)
 {
     const struct htb *htb = htb_get__(netdev);
-    const char *min_rate_s = shash_find_data(details, "min-rate");
-    const char *max_rate_s = shash_find_data(details, "max-rate");
-    const char *burst_s = shash_find_data(details, "burst");
-    const char *priority_s = shash_find_data(details, "priority");
+    const char *min_rate_s = smap_get(details, "min-rate");
+    const char *max_rate_s = smap_get(details, "max-rate");
+    const char *burst_s = smap_get(details, "burst");
+    const char *priority_s = smap_get(details, "priority");
     int mtu, error;
 
     error = netdev_get_mtu(netdev, &mtu);
@@ -2802,7 +2809,7 @@ htb_query_class__(const struct netdev *netdev, unsigned int handle,
 }
 
 static int
-htb_tc_install(struct netdev *netdev, const struct shash *details)
+htb_tc_install(struct netdev *netdev, const struct smap *details)
 {
     int error;
 
@@ -2894,15 +2901,15 @@ htb_tc_destroy(struct tc *tc)
 }
 
 static int
-htb_qdisc_get(const struct netdev *netdev, struct shash *details)
+htb_qdisc_get(const struct netdev *netdev, struct smap *details)
 {
     const struct htb *htb = htb_get__(netdev);
-    shash_add(details, "max-rate", xasprintf("%llu", 8ULL * htb->max_rate));
+    smap_add_format(details, "max-rate", "%llu", 8ULL * htb->max_rate);
     return 0;
 }
 
 static int
-htb_qdisc_set(struct netdev *netdev, const struct shash *details)
+htb_qdisc_set(struct netdev *netdev, const struct smap *details)
 {
     struct htb_class hc;
     int error;
@@ -2918,24 +2925,24 @@ htb_qdisc_set(struct netdev *netdev, const struct shash *details)
 
 static int
 htb_class_get(const struct netdev *netdev OVS_UNUSED,
-              const struct tc_queue *queue, struct shash *details)
+              const struct tc_queue *queue, struct smap *details)
 {
     const struct htb_class *hc = htb_class_cast__(queue);
 
-    shash_add(details, "min-rate", xasprintf("%llu", 8ULL * hc->min_rate));
+    smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate);
     if (hc->min_rate != hc->max_rate) {
-        shash_add(details, "max-rate", xasprintf("%llu", 8ULL * hc->max_rate));
+        smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate);
     }
-    shash_add(details, "burst", xasprintf("%llu", 8ULL * hc->burst));
+    smap_add_format(details, "burst", "%llu", 8ULL * hc->burst);
     if (hc->priority) {
-        shash_add(details, "priority", xasprintf("%u", hc->priority));
+        smap_add_format(details, "priority", "%u", hc->priority);
     }
     return 0;
 }
 
 static int
 htb_class_set(struct netdev *netdev, unsigned int queue_id,
-              const struct shash *details)
+              const struct smap *details)
 {
     struct htb_class hc;
     int error;
@@ -3195,13 +3202,13 @@ hfsc_query_class__(const struct netdev *netdev, unsigned int handle,
 }
 
 static void
-hfsc_parse_qdisc_details__(struct netdev *netdev, const struct shash *details,
+hfsc_parse_qdisc_details__(struct netdev *netdev, const struct smap *details,
                            struct hfsc_class *class)
 {
     uint32_t max_rate;
     const char *max_rate_s;
 
-    max_rate_s = shash_find_data(details, "max-rate");
+    max_rate_s = smap_get(details, "max-rate");
     max_rate   = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0;
 
     if (!max_rate) {
@@ -3217,7 +3224,7 @@ hfsc_parse_qdisc_details__(struct netdev *netdev, const struct shash *details,
 
 static int
 hfsc_parse_class_details__(struct netdev *netdev,
-                           const struct shash *details,
+                           const struct smap *details,
                            struct hfsc_class * class)
 {
     const struct hfsc *hfsc;
@@ -3225,8 +3232,8 @@ hfsc_parse_class_details__(struct netdev *netdev,
     const char *min_rate_s, *max_rate_s;
 
     hfsc       = hfsc_get__(netdev);
-    min_rate_s = shash_find_data(details, "min-rate");
-    max_rate_s = shash_find_data(details, "max-rate");
+    min_rate_s = smap_get(details, "min-rate");
+    max_rate_s = smap_get(details, "max-rate");
 
     min_rate = min_rate_s ? strtoull(min_rate_s, NULL, 10) / 8 : 0;
     min_rate = MAX(min_rate, 1);
@@ -3327,7 +3334,7 @@ hfsc_setup_class__(struct netdev *netdev, unsigned int handle,
 }
 
 static int
-hfsc_tc_install(struct netdev *netdev, const struct shash *details)
+hfsc_tc_install(struct netdev *netdev, const struct smap *details)
 {
     int error;
     struct hfsc_class class;
@@ -3395,16 +3402,16 @@ hfsc_tc_destroy(struct tc *tc)
 }
 
 static int
-hfsc_qdisc_get(const struct netdev *netdev, struct shash *details)
+hfsc_qdisc_get(const struct netdev *netdev, struct smap *details)
 {
     const struct hfsc *hfsc;
     hfsc = hfsc_get__(netdev);
-    shash_add(details, "max-rate", xasprintf("%llu", 8ULL * hfsc->max_rate));
+    smap_add_format(details, "max-rate", "%llu", 8ULL * hfsc->max_rate);
     return 0;
 }
 
 static int
-hfsc_qdisc_set(struct netdev *netdev, const struct shash *details)
+hfsc_qdisc_set(struct netdev *netdev, const struct smap *details)
 {
     int error;
     struct hfsc_class class;
@@ -3422,21 +3429,21 @@ hfsc_qdisc_set(struct netdev *netdev, const struct shash *details)
 
 static int
 hfsc_class_get(const struct netdev *netdev OVS_UNUSED,
-              const struct tc_queue *queue, struct shash *details)
+              const struct tc_queue *queue, struct smap *details)
 {
     const struct hfsc_class *hc;
 
     hc = hfsc_class_cast__(queue);
-    shash_add(details, "min-rate", xasprintf("%llu", 8ULL * hc->min_rate));
+    smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate);
     if (hc->min_rate != hc->max_rate) {
-        shash_add(details, "max-rate", xasprintf("%llu", 8ULL * hc->max_rate));
+        smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate);
     }
     return 0;
 }
 
 static int
 hfsc_class_set(struct netdev *netdev, unsigned int queue_id,
-               const struct shash *details)
+               const struct smap *details)
 {
     int error;
     struct hfsc_class class;
@@ -3541,7 +3548,7 @@ default_install__(struct netdev *netdev)
 
 static int
 default_tc_install(struct netdev *netdev,
-                   const struct shash *details OVS_UNUSED)
+                   const struct smap *details OVS_UNUSED)
 {
     default_install__(netdev);
     return 0;
@@ -4167,7 +4174,7 @@ tc_query_qdisc(const struct netdev *netdev)
     }
 
     /* Instantiate it. */
-    load_error = ops->tc_load((struct netdev *) netdev, qdisc);
+    load_error = ops->tc_load(CONST_CAST(struct netdev *, netdev), qdisc);
     assert((load_error == 0) == (netdev_dev->tc != NULL));
     ofpbuf_delete(qdisc);
 
@@ -4264,6 +4271,7 @@ netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
     uint32_t new_flags;
     int error;
 
+    COVERAGE_INC(netdev_get_ethtool);
     memset(&evalue, 0, sizeof evalue);
     error = netdev_linux_do_ethtool(netdev_name,
                                     (struct ethtool_cmd *)&evalue,
@@ -4272,6 +4280,7 @@ netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
         return error;
     }
 
+    COVERAGE_INC(netdev_set_ethtool);
     evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0);
     error = netdev_linux_do_ethtool(netdev_name,
                                     (struct ethtool_cmd *)&evalue,
@@ -4280,6 +4289,7 @@ netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
         return error;
     }
 
+    COVERAGE_INC(netdev_get_ethtool);
     memset(&evalue, 0, sizeof evalue);
     error = netdev_linux_do_ethtool(netdev_name,
                                     (struct ethtool_cmd *)&evalue,
@@ -4555,7 +4565,6 @@ netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd,
     ifr.ifr_data = (caddr_t) ecmd;
 
     ecmd->cmd = cmd;
-    COVERAGE_INC(netdev_ethtool);
     if (ioctl(af_inet_sock, SIOCETHTOOL, &ifr) == 0) {
         return 0;
     } else {
index 21a2db2..e404e46 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 9a3217e..f56a0ce 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
 #include "netdev.h"
 #include "list.h"
 #include "shash.h"
+#include "smap.h"
 
 #ifdef  __cplusplus
 extern "C" {
@@ -126,17 +127,17 @@ struct netdev_class {
     void (*destroy)(struct netdev_dev *netdev_dev);
 
     /* Fetches the device 'netdev_dev''s configuration, storing it in 'args'.
-     * The caller owns 'args' and pre-initializes it to an empty shash.
+     * The caller owns 'args' and pre-initializes it to an empty smap.
      *
      * If this netdev class does not have any configuration options, this may
      * be a null pointer. */
-    int (*get_config)(struct netdev_dev *netdev_dev, struct shash *args);
+    int (*get_config)(struct netdev_dev *netdev_dev, struct smap *args);
 
     /* Changes the device 'netdev_dev''s configuration to 'args'.
      *
      * If this netdev class does not support configuration, this may be a null
      * pointer. */
-    int (*set_config)(struct netdev_dev *netdev_dev, const struct shash *args);
+    int (*set_config)(struct netdev_dev *netdev_dev, const struct smap *args);
 
     /* Attempts to open a network device.  On success, sets 'netdevp'
      * to the new network device. */
@@ -231,7 +232,10 @@ struct netdev_class {
     /* Sets 'netdev''s Ethernet address to 'mac' */
     int (*set_etheraddr)(struct netdev *netdev, const uint8_t mac[6]);
 
-    /* Retrieves 'netdev''s Ethernet address into 'mac'. */
+    /* Retrieves 'netdev''s Ethernet address into 'mac'.
+     *
+     * This address will be advertised as 'netdev''s MAC address through the
+     * OpenFlow protocol, among other uses. */
     int (*get_etheraddr)(const struct netdev *netdev, uint8_t mac[6]);
 
     /* Retrieves 'netdev''s MTU into '*mtup'.
@@ -378,7 +382,7 @@ struct netdev_class {
      *
      * May be NULL if 'netdev' does not support QoS at all. */
     int (*get_qos)(const struct netdev *netdev,
-                   const char **typep, struct shash *details);
+                   const char **typep, struct smap *details);
 
     /* Attempts to reconfigure QoS on 'netdev', changing the form of QoS to
      * 'type' with details of configuration from 'details'.
@@ -398,7 +402,7 @@ struct netdev_class {
      *
      * May be NULL if 'netdev' does not support QoS at all. */
     int (*set_qos)(struct netdev *netdev,
-                   const char *type, const struct shash *details);
+                   const char *type, const struct smap *details);
 
     /* Queries 'netdev' for information about the queue numbered 'queue_id'.
      * If successful, adds that information as string key-value pairs to
@@ -417,7 +421,7 @@ struct netdev_class {
      * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
      */
     int (*get_queue)(const struct netdev *netdev,
-                     unsigned int queue_id, struct shash *details);
+                     unsigned int queue_id, struct smap *details);
 
     /* Configures the queue numbered 'queue_id' on 'netdev' with the key-value
      * string pairs in 'details'.  The contents of 'details' should be
@@ -437,7 +441,7 @@ struct netdev_class {
      *
      * May be NULL if 'netdev' does not support QoS at all. */
     int (*set_queue)(struct netdev *netdev,
-                     unsigned int queue_id, const struct shash *details);
+                     unsigned int queue_id, const struct smap *details);
 
     /* Attempts to delete the queue numbered 'queue_id' from 'netdev'.
      *
@@ -472,7 +476,7 @@ struct netdev_class {
      */
     int (*dump_queues)(const struct netdev *netdev,
                        void (*cb)(unsigned int queue_id,
-                                  const struct shash *details,
+                                  const struct smap *details,
                                   void *aux),
                        void *aux);
 
@@ -547,12 +551,11 @@ struct netdev_class {
      * representing netdev type specific information.  For more information see
      * ovs-vswitchd.conf.db(5).
      *
-     * The data of 'sh' are heap allocated strings which the caller is
-     * responsible for deallocating.
+     * The caller is responsible for destroying 'smap' and its data.
      *
      * This function may be set to null if it would always return EOPNOTSUPP
      * anyhow. */
-    int (*get_drv_info)(const struct netdev *netdev, struct shash *sh);
+    int (*get_drv_info)(const struct netdev *netdev, struct smap *smap);
 
     /* Looks up the ARP table entry for 'ip' on 'netdev' and stores the
      * corresponding MAC address in 'mac'.  A return value of ENXIO, in
@@ -593,6 +596,9 @@ 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__
+extern const struct netdev_class netdev_bsd_class;
+#endif
 extern const struct netdev_class netdev_tap_pl_class;
 
 extern const struct netdev_class netdev_tunnel_class;
index 6fe6d68..d2318db 100644 (file)
@@ -164,15 +164,15 @@ netdev_tunnel_close(struct netdev *netdev_)
 }
 
 static int
-netdev_tunnel_get_config(struct netdev_dev *dev_, struct shash *args)
+netdev_tunnel_get_config(struct netdev_dev *dev_, struct smap *args)
 {
     struct netdev_dev_tunnel *netdev_dev = netdev_dev_tunnel_cast(dev_);
 
     if (netdev_dev->valid_remote_ip)
-       shash_add(args, "remote_ip",
+       smap_add(args, "remote_ip",
            xasprintf(IP_FMT, IP_ARGS(&netdev_dev->remote_addr.sin_addr)));
     if (netdev_dev->valid_remote_port)
-        shash_add(args, "remote_port",
+        smap_add(args, "remote_port",
            xasprintf("%"PRIu16, ntohs(netdev_dev->remote_addr.sin_port)));
     return 0;
 }
@@ -196,13 +196,13 @@ netdev_tunnel_connect(struct netdev_dev_tunnel *dev)
 }
 
 static int
-netdev_tunnel_set_config(struct netdev_dev *dev_, const struct shash *args)
+netdev_tunnel_set_config(struct netdev_dev *dev_, const struct smap *args)
 {
     struct netdev_dev_tunnel *netdev_dev = netdev_dev_tunnel_cast(dev_);
     struct shash_node *node;
 
     VLOG_DBG("tunnel_set_config(%s)", netdev_dev_get_name(dev_));
-    SHASH_FOR_EACH(node, args) {
+    SMAP_FOR_EACH(node, args) {
         VLOG_DBG("arg: %s->%s", node->name, (char*)node->data);
        if (!strcmp(node->name, "remote_ip")) {
            struct in_addr addr;
index 6a6cd51..db5c3db 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -65,10 +65,10 @@ struct vport_class {
     enum ovs_vport_type type;
     struct netdev_class netdev_class;
     int (*parse_config)(const char *name, const char *type,
-                        const struct shash *args, struct ofpbuf *options);
+                        const struct smap *args, struct ofpbuf *options);
     int (*unparse_config)(const char *name, const char *type,
                           const struct nlattr *options, size_t options_len,
-                          struct shash *args);
+                          struct smap *args);
 };
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
@@ -223,7 +223,7 @@ netdev_vport_close(struct netdev *netdev_)
 }
 
 static int
-netdev_vport_get_config(struct netdev_dev *dev_, struct shash *args)
+netdev_vport_get_config(struct netdev_dev *dev_, struct smap *args)
 {
     const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
     const struct vport_class *vport_class = vport_class_cast(netdev_class);
@@ -260,7 +260,7 @@ netdev_vport_get_config(struct netdev_dev *dev_, struct shash *args)
 }
 
 static int
-netdev_vport_set_config(struct netdev_dev *dev_, const struct shash *args)
+netdev_vport_set_config(struct netdev_dev *dev_, const struct smap *args)
 {
     const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
     const struct vport_class *vport_class = vport_class_cast(netdev_class);
@@ -459,19 +459,18 @@ netdev_vport_set_stats(struct netdev *netdev, const struct netdev_stats *stats)
 }
 
 static int
-netdev_vport_get_drv_info(const struct netdev *netdev, struct shash *sh)
+netdev_vport_get_drv_info(const struct netdev *netdev, struct smap *smap)
 {
     const char *iface = netdev_vport_get_tnl_iface(netdev);
 
     if (iface) {
         struct netdev *egress_netdev;
 
-        shash_add(sh, "tunnel_egress_iface", xstrdup(iface));
+        smap_add(smap, "tunnel_egress_iface", iface);
 
         if (!netdev_open(iface, "system", &egress_netdev)) {
-            shash_add(sh, "tunnel_egress_iface_carrier",
-                      xstrdup(netdev_get_carrier(egress_netdev)
-                              ? "up" : "down"));
+            smap_add(smap, "tunnel_egress_iface_carrier",
+                     netdev_get_carrier(egress_netdev) ? "up" : "down");
             netdev_close(egress_netdev);
         }
     }
@@ -551,14 +550,14 @@ netdev_vport_poll_notify(const struct netdev *netdev)
 /* Code specific to individual vport types. */
 
 static void
-set_key(const struct shash *args, const char *name, uint16_t type,
+set_key(const struct smap *args, const char *name, uint16_t type,
         struct ofpbuf *options)
 {
     const char *s;
 
-    s = shash_find_data(args, name);
+    s = smap_get(args, name);
     if (!s) {
-        s = shash_find_data(args, "key");
+        s = smap_get(args, "key");
         if (!s) {
             s = "0";
         }
@@ -573,11 +572,11 @@ set_key(const struct shash *args, const char *name, uint16_t type,
 
 static int
 parse_tunnel_config(const char *name, const char *type,
-                    const struct shash *args, struct ofpbuf *options)
+                    const struct smap *args, struct ofpbuf *options)
 {
     bool is_gre = false;
     bool is_ipsec = false;
-    struct shash_node *node;
+    struct smap_node *node;
     bool ipsec_mech_set = false;
     ovs_be32 daddr = htonl(0);
     ovs_be32 saddr = htonl(0);
@@ -593,55 +592,62 @@ parse_tunnel_config(const char *name, const char *type,
         flags &= ~TNL_F_HDR_CACHE;
     }
 
-    SHASH_FOR_EACH (node, args) {
-        if (!strcmp(node->name, "remote_ip")) {
+    SMAP_FOR_EACH (node, args) {
+        if (!strcmp(node->key, "remote_ip")) {
             struct in_addr in_addr;
-            if (lookup_ip(node->data, &in_addr)) {
+            if (lookup_ip(node->value, &in_addr)) {
                 VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
             } else {
                 daddr = in_addr.s_addr;
             }
-        } else if (!strcmp(node->name, "local_ip")) {
+        } else if (!strcmp(node->key, "local_ip")) {
             struct in_addr in_addr;
-            if (lookup_ip(node->data, &in_addr)) {
+            if (lookup_ip(node->value, &in_addr)) {
                 VLOG_WARN("%s: bad %s 'local_ip'", name, type);
             } else {
                 saddr = in_addr.s_addr;
             }
-        } else if (!strcmp(node->name, "tos")) {
-            if (!strcmp(node->data, "inherit")) {
+        } else if (!strcmp(node->key, "tos")) {
+            if (!strcmp(node->value, "inherit")) {
                 flags |= TNL_F_TOS_INHERIT;
             } else {
-                nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, atoi(node->data));
+                char *endptr;
+                int tos;
+                tos = strtol(node->value, &endptr, 0);
+                if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) {
+                    nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, tos);
+                } else {
+                    VLOG_WARN("%s: invalid TOS %s", name, node->value);
+                }
             }
-        } else if (!strcmp(node->name, "ttl")) {
-            if (!strcmp(node->data, "inherit")) {
+        } else if (!strcmp(node->key, "ttl")) {
+            if (!strcmp(node->value, "inherit")) {
                 flags |= TNL_F_TTL_INHERIT;
             } else {
-                nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->data));
+                nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->value));
             }
-        } else if (!strcmp(node->name, "csum") && is_gre) {
-            if (!strcmp(node->data, "true")) {
+        } else if (!strcmp(node->key, "csum") && is_gre) {
+            if (!strcmp(node->value, "true")) {
                 flags |= TNL_F_CSUM;
             }
-        } else if (!strcmp(node->name, "df_inherit")) {
-            if (!strcmp(node->data, "true")) {
+        } else if (!strcmp(node->key, "df_inherit")) {
+            if (!strcmp(node->value, "true")) {
                 flags |= TNL_F_DF_INHERIT;
             }
-        } else if (!strcmp(node->name, "df_default")) {
-            if (!strcmp(node->data, "false")) {
+        } else if (!strcmp(node->key, "df_default")) {
+            if (!strcmp(node->value, "false")) {
                 flags &= ~TNL_F_DF_DEFAULT;
             }
-        } else if (!strcmp(node->name, "pmtud")) {
-            if (!strcmp(node->data, "false")) {
+        } else if (!strcmp(node->key, "pmtud")) {
+            if (!strcmp(node->value, "false")) {
                 flags &= ~TNL_F_PMTUD;
             }
-        } else if (!strcmp(node->name, "header_cache")) {
-            if (!strcmp(node->data, "false")) {
+        } else if (!strcmp(node->key, "header_cache")) {
+            if (!strcmp(node->value, "false")) {
                 flags &= ~TNL_F_HDR_CACHE;
             }
-        } else if (!strcmp(node->name, "peer_cert") && is_ipsec) {
-            if (shash_find(args, "certificate")) {
+        } else if (!strcmp(node->key, "peer_cert") && is_ipsec) {
+            if (smap_get(args, "certificate")) {
                 ipsec_mech_set = true;
             } else {
                 const char *use_ssl_cert;
@@ -652,7 +658,7 @@ parse_tunnel_config(const char *name, const char *type,
                  * will like be removed when multiple SSL configurations
                  * are supported by OVS.
                  */
-                use_ssl_cert = shash_find_data(args, "use_ssl_cert");
+                use_ssl_cert = smap_get(args, "use_ssl_cert");
                 if (!use_ssl_cert || strcmp(use_ssl_cert, "true")) {
                     VLOG_ERR("%s: 'peer_cert' requires 'certificate' argument",
                              name);
@@ -660,34 +666,38 @@ parse_tunnel_config(const char *name, const char *type,
                 }
                 ipsec_mech_set = true;
             }
-        } else if (!strcmp(node->name, "psk") && is_ipsec) {
+        } else if (!strcmp(node->key, "psk") && is_ipsec) {
             ipsec_mech_set = true;
         } else if (is_ipsec
-                && (!strcmp(node->name, "certificate")
-                    || !strcmp(node->name, "private_key")
-                    || !strcmp(node->name, "use_ssl_cert"))) {
+                && (!strcmp(node->key, "certificate")
+                    || !strcmp(node->key, "private_key")
+                    || !strcmp(node->key, "use_ssl_cert"))) {
             /* Ignore options not used by the netdev. */
-        } else if (!strcmp(node->name, "key") ||
-                   !strcmp(node->name, "in_key") ||
-                   !strcmp(node->name, "out_key")) {
+        } else if (!strcmp(node->key, "key") ||
+                   !strcmp(node->key, "in_key") ||
+                   !strcmp(node->key, "out_key")) {
             /* Handled separately below. */
         } else {
-            VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->name);
+            VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->key);
         }
     }
 
     if (is_ipsec) {
-        char *file_name = xasprintf("%s/%s", ovs_rundir(),
-                "ovs-monitor-ipsec.pid");
-        pid_t pid = read_pidfile(file_name);
-        free(file_name);
+        static pid_t pid = 0;
+        if (pid <= 0) {
+            char *file_name = xasprintf("%s/%s", ovs_rundir(),
+                                        "ovs-monitor-ipsec.pid");
+            pid = read_pidfile(file_name);
+            free(file_name);
+        }
+
         if (pid < 0) {
             VLOG_ERR("%s: IPsec requires the ovs-monitor-ipsec daemon",
                      name);
             return EINVAL;
         }
 
-        if (shash_find(args, "peer_cert") && shash_find(args, "psk")) {
+        if (smap_get(args, "peer_cert") && smap_get(args, "psk")) {
             VLOG_ERR("%s: cannot define both 'peer_cert' and 'psk'", name);
             return EINVAL;
         }
@@ -754,7 +764,7 @@ get_be64_or_zero(const struct nlattr *a)
 static int
 unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
                       const struct nlattr *options, size_t options_len,
-                      struct shash *args)
+                      struct smap *args)
 {
     struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
     ovs_be32 daddr;
@@ -773,11 +783,11 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
     }
 
     daddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
-    shash_add(args, "remote_ip", xasprintf(IP_FMT, IP_ARGS(&daddr)));
+    smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(&daddr));
 
     if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
         ovs_be32 saddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_SRC_IPV4]);
-        shash_add(args, "local_ip", xasprintf(IP_FMT, IP_ARGS(&saddr)));
+        smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(&saddr));
     }
 
     if (!a[OVS_TUNNEL_ATTR_IN_KEY] && !a[OVS_TUNNEL_ATTR_OUT_KEY]) {
@@ -787,18 +797,18 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
         uint64_t out_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_OUT_KEY]);
 
         if (in_key && in_key == out_key) {
-            shash_add(args, "key", xasprintf("%"PRIu64, in_key));
+            smap_add_format(args, "key", "%"PRIu64, in_key);
         } else {
             if (!a[OVS_TUNNEL_ATTR_IN_KEY]) {
                 smap_add(args, "in_key", "flow");
             } else if (in_key) {
-                shash_add(args, "in_key", xasprintf("%"PRIu64, in_key));
+                smap_add_format(args, "in_key", "%"PRIu64, in_key);
             }
 
             if (!a[OVS_TUNNEL_ATTR_OUT_KEY]) {
                 smap_add(args, "out_key", "flow");
             } else if (out_key) {
-                shash_add(args, "out_key", xasprintf("%"PRIu64, out_key));
+                smap_add_format(args, "out_key", "%"PRIu64, out_key);
             }
         }
     }
@@ -807,14 +817,14 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
         smap_add(args, "tos", "inherit");
     } else if (a[OVS_TUNNEL_ATTR_TTL]) {
         int ttl = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TTL]);
-        shash_add(args, "tos", xasprintf("%d", ttl));
+        smap_add_format(args, "tos", "%d", ttl);
     }
 
     if (flags & TNL_F_TOS_INHERIT) {
         smap_add(args, "tos", "inherit");
     } else if (a[OVS_TUNNEL_ATTR_TOS]) {
         int tos = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TOS]);
-        shash_add(args, "tos", xasprintf("%d", tos));
+        smap_add_format(args, "tos", "0x%x", tos);
     }
 
     if (flags & TNL_F_CSUM) {
@@ -835,17 +845,17 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
 
 static int
 parse_patch_config(const char *name, const char *type OVS_UNUSED,
-                   const struct shash *args, struct ofpbuf *options)
+                   const struct smap *args, struct ofpbuf *options)
 {
     const char *peer;
 
-    peer = shash_find_data(args, "peer");
+    peer = smap_get(args, "peer");
     if (!peer) {
         VLOG_ERR("%s: patch type requires valid 'peer' argument", name);
         return EINVAL;
     }
 
-    if (shash_count(args) > 1) {
+    if (smap_count(args) > 1) {
         VLOG_ERR("%s: patch type takes only a 'peer' argument", name);
         return EINVAL;
     }
@@ -868,7 +878,7 @@ parse_patch_config(const char *name, const char *type OVS_UNUSED,
 static int
 unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
                      const struct nlattr *options, size_t options_len,
-                     struct shash *args)
+                     struct smap *args)
 {
     static const struct nl_policy ovs_patch_policy[] = {
         [OVS_PATCH_ATTR_PEER] = { .type = NL_A_STRING,
index 2df6d55..d96a318 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 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.
index 9445bdf..2b4dec4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@
 #include "packets.h"
 #include "poll-loop.h"
 #include "shash.h"
+#include "smap.h"
 #include "sset.h"
 #include "svec.h"
 #include "vlog.h"
@@ -80,6 +81,10 @@ netdev_initialize(void)
         netdev_register_provider(&netdev_tap_class);
        netdev_register_provider(&netdev_tap_pl_class);
         netdev_vport_register();
+#endif
+#ifdef __FreeBSD__
+        netdev_register_provider(&netdev_tap_class);
+        netdev_register_provider(&netdev_bsd_class);
 #endif
        netdev_register_provider(&netdev_tunnel_class);
     }
@@ -245,15 +250,15 @@ netdev_open(const char *name, const char *type, struct netdev **netdevp)
 /* Reconfigures the device 'netdev' with 'args'.  'args' may be empty
  * or NULL if none are needed. */
 int
-netdev_set_config(struct netdev *netdev, const struct shash *args)
+netdev_set_config(struct netdev *netdev, const struct smap *args)
 {
     struct netdev_dev *netdev_dev = netdev_get_dev(netdev);
 
     if (netdev_dev->netdev_class->set_config) {
-        struct shash no_args = SHASH_INITIALIZER(&no_args);
+        struct smap no_args = SMAP_INITIALIZER(&no_args);
         return netdev_dev->netdev_class->set_config(netdev_dev,
                                                     args ? args : &no_args);
-    } else if (args && !shash_is_empty(args)) {
+    } else if (args && !smap_is_empty(args)) {
         VLOG_WARN("%s: arguments provided to device that is not configurable",
                   netdev_get_name(netdev));
     }
@@ -262,23 +267,23 @@ netdev_set_config(struct netdev *netdev, const struct shash *args)
 }
 
 /* Returns the current configuration for 'netdev' in 'args'.  The caller must
- * have already initialized 'args' with shash_init().  Returns 0 on success, in
+ * have already initialized 'args' with smap_init().  Returns 0 on success, in
  * which case 'args' will be filled with 'netdev''s configuration.  On failure
  * returns a positive errno value, in which case 'args' will be empty.
  *
  * The caller owns 'args' and its contents and must eventually free them with
- * shash_destroy_free_data(). */
+ * smap_destroy(). */
 int
-netdev_get_config(const struct netdev *netdev, struct shash *args)
+netdev_get_config(const struct netdev *netdev, struct smap *args)
 {
     struct netdev_dev *netdev_dev = netdev_get_dev(netdev);
     int error;
 
-    shash_clear_free_data(args);
+    smap_clear(args);
     if (netdev_dev->netdev_class->get_config) {
         error = netdev_dev->netdev_class->get_config(netdev_dev, args);
         if (error) {
-            shash_clear_free_data(args);
+            smap_clear(args);
         }
     } else {
         error = 0;
@@ -761,18 +766,18 @@ netdev_get_next_hop(const struct netdev *netdev,
     return error;
 }
 
-/* Populates 'sh' with status information.
+/* Populates 'smap' with status information.
  *
- * Populates 'sh' with 'netdev' specific status information.  This information
- * may be used to populate the status column of the Interface table as defined
- * in ovs-vswitchd.conf.db(5). */
+ * Populates 'smap' with 'netdev' specific status information.  This
+ * information may be used to populate the status column of the Interface table
+ * as defined in ovs-vswitchd.conf.db(5). */
 int
-netdev_get_drv_info(const struct netdev *netdev, struct shash *sh)
+netdev_get_drv_info(const struct netdev *netdev, struct smap *smap)
 {
     struct netdev_dev *dev = netdev_get_dev(netdev);
 
     return (dev->netdev_class->get_drv_info
-            ? dev->netdev_class->get_drv_info(netdev, sh)
+            ? dev->netdev_class->get_drv_info(netdev, smap)
             : EOPNOTSUPP);
 }
 
@@ -844,7 +849,7 @@ do_update_flags(struct netdev *netdev, enum netdev_flags off,
 int
 netdev_get_flags(const struct netdev *netdev_, enum netdev_flags *flagsp)
 {
-    struct netdev *netdev = (struct netdev *) netdev_;
+    struct netdev *netdev = CONST_CAST(struct netdev *, netdev_);
     return do_update_flags(netdev, 0, 0, flagsp, false);
 }
 
@@ -1067,10 +1072,9 @@ netdev_get_n_queues(const struct netdev *netdev,
  *
  * A '*typep' of "" indicates that QoS is currently disabled on 'netdev'.
  *
- * The caller must initialize 'details' as an empty shash (e.g. with
- * shash_init()) before calling this function.  The caller must free 'details',
- * including 'data' members, when it is no longer needed (e.g. with
- * shash_destroy_free_data()).
+ * The caller must initialize 'details' as an empty smap (e.g. with
+ * smap_init()) before calling this function.  The caller must free 'details'
+ * when it is no longer needed (e.g. with smap_destroy()).
  *
  * The caller must not modify or free '*typep'.
  *
@@ -1080,7 +1084,7 @@ netdev_get_n_queues(const struct netdev *netdev,
  * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). */
 int
 netdev_get_qos(const struct netdev *netdev,
-               const char **typep, struct shash *details)
+               const char **typep, struct smap *details)
 {
     const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class;
     int retval;
@@ -1089,7 +1093,7 @@ netdev_get_qos(const struct netdev *netdev,
         retval = class->get_qos(netdev, typep, details);
         if (retval) {
             *typep = NULL;
-            shash_clear_free_data(details);
+            smap_clear(details);
         }
         return retval;
     } else {
@@ -1119,7 +1123,7 @@ netdev_get_qos(const struct netdev *netdev,
  * details. */
 int
 netdev_set_qos(struct netdev *netdev,
-               const char *type, const struct shash *details)
+               const char *type, const struct smap *details)
 {
     const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class;
 
@@ -1129,7 +1133,7 @@ netdev_set_qos(struct netdev *netdev,
 
     if (class->set_qos) {
         if (!details) {
-            static struct shash empty = SHASH_INITIALIZER(&empty);
+            static struct smap empty = SMAP_INITIALIZER(&empty);
             details = &empty;
         }
         return class->set_qos(netdev, type, details);
@@ -1149,12 +1153,12 @@ netdev_set_qos(struct netdev *netdev,
  * given 'type' in the "other_config" column in the "Queue" table in
  * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
  *
- * The caller must initialize 'details' (e.g. with shash_init()) before calling
- * this function.  The caller must free 'details', including 'data' members,
- * when it is no longer needed (e.g. with shash_destroy_free_data()). */
+ * The caller must initialize 'details' (e.g. with smap_init()) before calling
+ * this function.  The caller must free 'details' when it is no longer needed
+ * (e.g. with smap_destroy()). */
 int
 netdev_get_queue(const struct netdev *netdev,
-                 unsigned int queue_id, struct shash *details)
+                 unsigned int queue_id, struct smap *details)
 {
     const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class;
     int retval;
@@ -1163,7 +1167,7 @@ netdev_get_queue(const struct netdev *netdev,
               ? class->get_queue(netdev, queue_id, details)
               : EOPNOTSUPP);
     if (retval) {
-        shash_clear_free_data(details);
+        smap_clear(details);
     }
     return retval;
 }
@@ -1182,7 +1186,7 @@ netdev_get_queue(const struct netdev *netdev,
  * it. */
 int
 netdev_set_queue(struct netdev *netdev,
-                 unsigned int queue_id, const struct shash *details)
+                 unsigned int queue_id, const struct smap *details)
 {
     const struct netdev_class *class = netdev_get_dev(netdev)->netdev_class;
     return (class->set_queue
index bff1101..d2cc8b5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ extern "C" {
 struct ofpbuf;
 struct in_addr;
 struct in6_addr;
-struct shash;
+struct smap;
 struct sset;
 
 enum netdev_flags {
@@ -93,8 +93,8 @@ bool netdev_is_open(const char *name);
 void netdev_parse_name(const char *netdev_name, char **name, char **type);
 
 /* Options. */
-int netdev_set_config(struct netdev *, const struct shash *args);
-int netdev_get_config(const struct netdev *, struct shash *);
+int netdev_set_config(struct netdev *, const struct smap *args);
+int netdev_get_config(const struct netdev *, struct smap *);
 
 /* Basic properties. */
 const char *netdev_get_name(const struct netdev *);
@@ -159,7 +159,7 @@ int netdev_get_in6(const struct netdev *, struct in6_addr *);
 int netdev_add_router(struct netdev *, struct in_addr router);
 int netdev_get_next_hop(const struct netdev *, const struct in_addr *host,
                         struct in_addr *next_hop, char **);
-int netdev_get_drv_info(const struct netdev *, struct shash *sh);
+int netdev_get_drv_info(const struct netdev *, struct smap *);
 int netdev_arp_lookup(const struct netdev *, ovs_be32 ip, uint8_t mac[6]);
 
 int netdev_get_flags(const struct netdev *, enum netdev_flags *);
@@ -195,20 +195,20 @@ int netdev_get_n_queues(const struct netdev *,
                         const char *type, unsigned int *n_queuesp);
 
 int netdev_get_qos(const struct netdev *,
-                   const char **typep, struct shash *details);
+                   const char **typep, struct smap *details);
 int netdev_set_qos(struct netdev *,
-                   const char *type, const struct shash *details);
+                   const char *type, const struct smap *details);
 
 int netdev_get_queue(const struct netdev *,
-                     unsigned int queue_id, struct shash *details);
+                     unsigned int queue_id, struct smap *details);
 int netdev_set_queue(struct netdev *,
-                     unsigned int queue_id, const struct shash *details);
+                     unsigned int queue_id, const struct smap *details);
 int netdev_delete_queue(struct netdev *, unsigned int queue_id);
 int netdev_get_queue_stats(const struct netdev *, unsigned int queue_id,
                            struct netdev_queue_stats *);
 
 typedef void netdev_dump_queues_cb(unsigned int queue_id,
-                                   const struct shash *details, void *aux);
+                                   const struct smap *details, void *aux);
 int netdev_dump_queues(const struct netdev *,
                        netdev_dump_queues_cb *, void *aux);
 
index 49202d9..321f64c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 7ef3d6c..8f6daff 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 26aa671..4bd90f1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 3f4ce35..3009fc5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2010, 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.
index a46319f..49a8493 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -253,13 +253,14 @@ nl_sock_leave_mcgroup(struct nl_sock *sock, unsigned int multicast_group)
 }
 
 static int
-nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg, bool wait)
+nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg,
+               uint32_t nlmsg_seq, bool wait)
 {
     struct nlmsghdr *nlmsg = nl_msg_nlmsghdr(msg);
     int error;
 
     nlmsg->nlmsg_len = msg->size;
-    nlmsg->nlmsg_seq = nl_sock_allocate_seq(sock, 1);
+    nlmsg->nlmsg_seq = nlmsg_seq;
     nlmsg->nlmsg_pid = sock->pid;
     do {
         int retval;
@@ -274,20 +275,39 @@ nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg, bool wait)
 }
 
 /* Tries to send 'msg', which must contain a Netlink message, to the kernel on
- * 'sock'.  nlmsg_len in 'msg' will be finalized to match msg->size, and
- * nlmsg_pid will be set to 'sock''s pid, before the message is sent.
+ * 'sock'.  nlmsg_len in 'msg' will be finalized to match msg->size, nlmsg_pid
+ * will be set to 'sock''s pid, and nlmsg_seq will be initialized to a fresh
+ * sequence number, before the message is sent.
  *
  * Returns 0 if successful, otherwise a positive errno value.  If
  * 'wait' is true, then the send will wait until buffer space is ready;
  * otherwise, returns EAGAIN if the 'sock' send buffer is full. */
 int
 nl_sock_send(struct nl_sock *sock, const struct ofpbuf *msg, bool wait)
+{
+    return nl_sock_send_seq(sock, msg, nl_sock_allocate_seq(sock, 1), wait);
+}
+
+/* Tries to send 'msg', which must contain a Netlink message, to the kernel on
+ * 'sock'.  nlmsg_len in 'msg' will be finalized to match msg->size, nlmsg_pid
+ * will be set to 'sock''s pid, and nlmsg_seq will be initialized to
+ * 'nlmsg_seq', before the message is sent.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.  If
+ * 'wait' is true, then the send will wait until buffer space is ready;
+ * otherwise, returns EAGAIN if the 'sock' send buffer is full.
+ *
+ * This function is suitable for sending a reply to a request that was received
+ * with sequence number 'nlmsg_seq'.  Otherwise, use nl_sock_send() instead. */
+int
+nl_sock_send_seq(struct nl_sock *sock, const struct ofpbuf *msg,
+                 uint32_t nlmsg_seq, bool wait)
 {
     int error = nl_sock_cow__(sock);
     if (error) {
         return error;
     }
-    return nl_sock_send__(sock, msg, wait);
+    return nl_sock_send__(sock, msg, nlmsg_seq, wait);
 }
 
 /* This stress option is useful for testing that OVS properly tolerates
@@ -663,7 +683,7 @@ nl_sock_transact(struct nl_sock *sock, const struct ofpbuf *request,
     struct nl_transaction *transactionp;
     struct nl_transaction transaction;
 
-    transaction.request = (struct ofpbuf *) request;
+    transaction.request = CONST_CAST(struct ofpbuf *, request);
     transaction.reply = replyp ? ofpbuf_new(1024) : NULL;
     transactionp = &transaction;
 
@@ -770,7 +790,8 @@ nl_dump_start(struct nl_dump *dump,
     }
 
     nl_msg_nlmsghdr(request)->nlmsg_flags |= NLM_F_DUMP | NLM_F_ACK;
-    dump->status = nl_sock_send__(sock, request, true);
+    dump->status = nl_sock_send__(sock, request, nl_sock_allocate_seq(sock, 1),
+                                  true);
     dump->seq = nl_msg_nlmsghdr(request)->nlmsg_seq;
 }
 
index 0232616..78dd7b2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -52,6 +52,8 @@ int nl_sock_join_mcgroup(struct nl_sock *, unsigned int multicast_group);
 int nl_sock_leave_mcgroup(struct nl_sock *, unsigned int multicast_group);
 
 int nl_sock_send(struct nl_sock *, const struct ofpbuf *, bool wait);
+int nl_sock_send_seq(struct nl_sock *, const struct ofpbuf *,
+                     uint32_t nlmsg_seq, bool wait);
 int nl_sock_recv(struct nl_sock *, struct ofpbuf *, bool wait);
 int nl_sock_transact(struct nl_sock *, const struct ofpbuf *request,
                      struct ofpbuf **replyp);
index 6e0701c..5414239 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 7eced42..a5c129a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 91dd7fc..16c1674 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
 #include "classifier.h"
 #include "dynamic-string.h"
 #include "meta-flow.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
@@ -66,7 +67,8 @@ nx_entry_ok(const void *p, unsigned int match_len)
 
     if (match_len < 4) {
         if (match_len) {
-            VLOG_DBG_RL(&rl, "nx_match ends with partial nxm_header");
+            VLOG_DBG_RL(&rl, "nx_match ends with partial (%u-byte) nxm_header",
+                        match_len);
         }
         return 0;
     }
@@ -89,27 +91,22 @@ nx_entry_ok(const void *p, unsigned int match_len)
 }
 
 static enum ofperr
-nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
-                uint16_t priority, struct cls_rule *rule,
-                ovs_be64 *cookie, ovs_be64 *cookie_mask)
+nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
+            uint16_t priority, struct cls_rule *rule,
+            ovs_be64 *cookie, ovs_be64 *cookie_mask)
 {
     uint32_t header;
-    uint8_t *p;
 
     assert((cookie != NULL) == (cookie_mask != NULL));
 
-    p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
-    if (!p) {
-        VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a "
-                    "multiple of 8, is longer than space in message (max "
-                    "length %zu)", match_len, b->size);
-        return OFPERR_OFPBMC_BAD_LEN;
-    }
-
     cls_rule_init_catchall(rule, priority);
     if (cookie) {
         *cookie = *cookie_mask = htonll(0);
     }
+    if (!match_len) {
+        return 0;
+    }
+
     for (;
          (header = nx_entry_ok(p, match_len)) != 0;
          p += 4 + NXM_LENGTH(header), match_len -= 4 + NXM_LENGTH(header)) {
@@ -127,7 +124,7 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
             error = OFPERR_OFPBMC_BAD_PREREQ;
         } else if (!mf_is_all_wild(mf, &rule->wc)) {
             error = OFPERR_OFPBMC_DUP_FIELD;
-        } else {
+        } else if (header != OXM_OF_IN_PORT) {
             unsigned int width = mf->n_bytes;
             union mf_value value;
 
@@ -148,6 +145,17 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
                     mf_set(mf, &value, &mask, rule);
                 }
             }
+        } 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) {
+                cls_rule_set_in_port(rule, port);
+            }
         }
 
         /* Check if the match is for a cookie rather than a classifier rule. */
@@ -181,6 +189,27 @@ nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
     return match_len ? OFPERR_OFPBMC_BAD_LEN : 0;
 }
 
+static enum ofperr
+nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
+                uint16_t priority, struct cls_rule *rule,
+                ovs_be64 *cookie, ovs_be64 *cookie_mask)
+{
+    uint8_t *p = NULL;
+
+    if (match_len) {
+        p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
+        if (!p) {
+            VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a "
+                        "multiple of 8, is longer than space in message (max "
+                        "length %zu)", match_len, b->size);
+            return OFPERR_OFPBMC_BAD_LEN;
+        }
+    }
+
+    return nx_pull_raw(p, match_len, strict, priority, rule,
+                       cookie, cookie_mask);
+}
+
 /* Parses the nx_match formatted match description in 'b' with length
  * 'match_len'.  The results are stored in 'rule', which is initialized with
  * 'priority'.  If 'cookie' and 'cookie_mask' contain valid pointers, then the
@@ -209,6 +238,61 @@ nx_pull_match_loose(struct ofpbuf *b, unsigned int match_len,
     return nx_pull_match__(b, match_len, false, priority, rule, cookie,
                            cookie_mask);
 }
+
+static enum ofperr
+oxm_pull_match__(struct ofpbuf *b, bool strict,
+                 uint16_t priority, struct cls_rule *rule)
+{
+    struct ofp11_match_header *omh = b->data;
+    uint8_t *p;
+    uint16_t match_len;
+
+    if (b->size < sizeof *omh) {
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    match_len = ntohs(omh->length);
+    if (match_len < sizeof *omh) {
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    if (omh->type != htons(OFPMT_OXM)) {
+        return OFPERR_OFPBMC_BAD_TYPE;
+    }
+
+    p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
+    if (!p) {
+        VLOG_DBG_RL(&rl, "oxm length %u, rounded up to a "
+                    "multiple of 8, is longer than space in message (max "
+                    "length %zu)", match_len, b->size);
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
+
+    return nx_pull_raw(p + sizeof *omh, match_len - sizeof *omh,
+                       strict, priority, rule, NULL, NULL);
+}
+
+/* Parses the oxm formatted match description preceeded by a struct
+ * ofp11_match in 'b' with length 'match_len'.  The results are stored in
+ * 'rule', which is initialized with 'priority'.
+ *
+ * Fails with an error when encountering unknown OXM headers.
+ *
+ * Returns 0 if successful, otherwise an OpenFlow error code. */
+enum ofperr
+oxm_pull_match(struct ofpbuf *b, uint16_t priority, struct cls_rule *rule)
+{
+    return oxm_pull_match__(b, true, priority, rule);
+}
+
+/* Behaves the same as oxm_pull_match() with one exception.  Skips over unknown
+ * PXM headers instead of failing with an error when they are encountered. */
+enum ofperr
+oxm_pull_match_loose(struct ofpbuf *b, uint16_t priority,
+                     struct cls_rule *rule)
+{
+    return oxm_pull_match__(b, false, priority, rule);
+}
 \f
 /* nx_put_match() and helpers.
  *
@@ -354,20 +438,18 @@ nxm_put_eth(struct ofpbuf *b, uint32_t header,
 }
 
 static void
-nxm_put_eth_dst(struct ofpbuf *b,
-                flow_wildcards_t wc, const uint8_t value[ETH_ADDR_LEN])
+nxm_put_eth_masked(struct ofpbuf *b, uint32_t header,
+                   const uint8_t value[ETH_ADDR_LEN],
+                   const uint8_t mask[ETH_ADDR_LEN])
 {
-    switch (wc & (FWW_DL_DST | FWW_ETH_MCAST)) {
-    case FWW_DL_DST | FWW_ETH_MCAST:
-        break;
-    default:
-        nxm_put_header(b, NXM_OF_ETH_DST_W);
-        ofpbuf_put(b, value, ETH_ADDR_LEN);
-        ofpbuf_put(b, flow_wildcards_to_dl_dst_mask(wc), ETH_ADDR_LEN);
-        break;
-    case 0:
-        nxm_put_eth(b, NXM_OF_ETH_DST, value);
-        break;
+    if (!eth_addr_is_zero(mask)) {
+        if (eth_mask_is_exact(mask)) {
+            nxm_put_eth(b, header, value);
+        } else {
+            nxm_put_header(b, NXM_MAKE_WILD_HEADER(header));
+            ofpbuf_put(b, value, ETH_ADDR_LEN);
+            ofpbuf_put(b, mask, ETH_ADDR_LEN);
+        }
     }
 }
 
@@ -410,7 +492,8 @@ nxm_put_frag(struct ofpbuf *b, const struct cls_rule *cr)
 
 static void
 nxm_put_ip(struct ofpbuf *b, const struct cls_rule *cr,
-           uint8_t icmp_proto, uint32_t icmp_type, uint32_t icmp_code)
+           uint8_t icmp_proto, uint32_t icmp_type, uint32_t icmp_code,
+           bool oxm)
 {
     const flow_wildcards_t wc = cr->wc.wildcards;
     const struct flow *flow = &cr->flow;
@@ -418,26 +501,32 @@ nxm_put_ip(struct ofpbuf *b, const struct cls_rule *cr,
     nxm_put_frag(b, cr);
 
     if (!(wc & FWW_NW_DSCP)) {
-        nxm_put_8(b, NXM_OF_IP_TOS, flow->nw_tos & IP_DSCP_MASK);
+        nxm_put_8(b, oxm ? OXM_OF_IP_DSCP : NXM_OF_IP_TOS,
+                  flow->nw_tos & IP_DSCP_MASK);
     }
 
     if (!(wc & FWW_NW_ECN)) {
-        nxm_put_8(b, NXM_NX_IP_ECN, flow->nw_tos & IP_ECN_MASK);
+        nxm_put_8(b, oxm ? OXM_OF_IP_ECN : NXM_NX_IP_ECN,
+                  flow->nw_tos & IP_ECN_MASK);
     }
 
-    if (!(wc & FWW_NW_TTL)) {
+    if (!oxm && !(wc & FWW_NW_TTL)) {
         nxm_put_8(b, NXM_NX_IP_TTL, flow->nw_ttl);
     }
 
     if (!(wc & FWW_NW_PROTO)) {
-        nxm_put_8(b, NXM_OF_IP_PROTO, flow->nw_proto);
+        nxm_put_8(b, oxm ? OXM_OF_IP_PROTO : NXM_OF_IP_PROTO, flow->nw_proto);
 
         if (flow->nw_proto == IPPROTO_TCP) {
-            nxm_put_16m(b, NXM_OF_TCP_SRC, flow->tp_src, cr->wc.tp_src_mask);
-            nxm_put_16m(b, NXM_OF_TCP_DST, flow->tp_dst, cr->wc.tp_dst_mask);
+            nxm_put_16m(b, oxm ? OXM_OF_TCP_SRC : NXM_OF_TCP_SRC,
+                        flow->tp_src, cr->wc.tp_src_mask);
+            nxm_put_16m(b, oxm ? OXM_OF_TCP_DST : NXM_OF_TCP_DST,
+                        flow->tp_dst, cr->wc.tp_dst_mask);
         } else if (flow->nw_proto == IPPROTO_UDP) {
-            nxm_put_16m(b, NXM_OF_UDP_SRC, flow->tp_src, cr->wc.tp_src_mask);
-            nxm_put_16m(b, NXM_OF_UDP_DST, flow->tp_dst, cr->wc.tp_dst_mask);
+            nxm_put_16m(b, oxm ? OXM_OF_UDP_SRC : NXM_OF_UDP_SRC,
+                        flow->tp_src, cr->wc.tp_src_mask);
+            nxm_put_16m(b, oxm ? OXM_OF_UDP_DST : NXM_OF_UDP_DST,
+                        flow->tp_dst, cr->wc.tp_dst_mask);
         } else if (flow->nw_proto == icmp_proto) {
             if (cr->wc.tp_src_mask) {
                 nxm_put_8(b, icmp_type, ntohs(flow->tp_src));
@@ -450,10 +539,9 @@ nxm_put_ip(struct ofpbuf *b, const struct cls_rule *cr,
 }
 
 /* Appends to 'b' the nx_match format that expresses 'cr' (except for
- * 'cr->priority', because priority is not part of nx_match), plus enough
- * zero bytes to pad the nx_match out to a multiple of 8.  For Flow Mod
- * and Flow Stats Requests messages, a 'cookie' and 'cookie_mask' may be
- * supplied.  Otherwise, 'cookie_mask' should be zero.
+ * 'cr->priority', because priority is not part of nx_match).  For Flow Mod and
+ * Flow Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied.
+ * Otherwise, 'cookie_mask' should be zero.
  *
  * This function can cause 'b''s data to be reallocated.
  *
@@ -461,9 +549,9 @@ nxm_put_ip(struct ofpbuf *b, const struct cls_rule *cr,
  *
  * If 'cr' is a catch-all rule that matches every packet, then this function
  * appends nothing to 'b' and returns 0. */
-int
-nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
-             ovs_be64 cookie, ovs_be64 cookie_mask)
+static int
+nx_put_raw(struct ofpbuf *b, bool oxm, const struct cls_rule *cr,
+           ovs_be64 cookie, ovs_be64 cookie_mask)
 {
     const flow_wildcards_t wc = cr->wc.wildcards;
     const struct flow *flow = &cr->flow;
@@ -471,73 +559,98 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
     int match_len;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     /* Metadata. */
     if (!(wc & FWW_IN_PORT)) {
         uint16_t in_port = flow->in_port;
-        nxm_put_16(b, NXM_OF_IN_PORT, htons(in_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));
+        }
     }
 
     /* Ethernet. */
-    nxm_put_eth_dst(b, wc, flow->dl_dst);
-    if (!(wc & FWW_DL_SRC)) {
-        nxm_put_eth(b, NXM_OF_ETH_SRC, flow->dl_src);
-    }
+    nxm_put_eth_masked(b, oxm ? OXM_OF_ETH_SRC : NXM_OF_ETH_SRC,
+                       flow->dl_src, cr->wc.dl_src_mask);
+    nxm_put_eth_masked(b, oxm ? OXM_OF_ETH_DST : NXM_OF_ETH_DST,
+                       flow->dl_dst, cr->wc.dl_dst_mask);
     if (!(wc & FWW_DL_TYPE)) {
-        nxm_put_16(b, NXM_OF_ETH_TYPE,
+        nxm_put_16(b, oxm ? OXM_OF_ETH_TYPE : NXM_OF_ETH_TYPE,
                    ofputil_dl_type_to_openflow(flow->dl_type));
     }
 
     /* 802.1Q. */
-    nxm_put_16m(b, NXM_OF_VLAN_TCI, flow->vlan_tci, cr->wc.vlan_tci_mask);
+    if (oxm) {
+        ovs_be16 vid = flow->vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI);
+        ovs_be16 mask = cr->wc.vlan_tci_mask & htons(VLAN_VID_MASK | VLAN_CFI);
+
+        if (mask == htons(VLAN_VID_MASK | VLAN_CFI)) {
+            nxm_put_16(b, OXM_OF_VLAN_VID, vid);
+        } else if (mask) {
+            nxm_put_16m(b, OXM_OF_VLAN_VID, vid, mask);
+        }
+
+        if (vid && vlan_tci_to_pcp(cr->wc.vlan_tci_mask)) {
+            nxm_put_8(b, OXM_OF_VLAN_PCP, vlan_tci_to_pcp(flow->vlan_tci));
+        }
+
+    } else {
+        nxm_put_16m(b, NXM_OF_VLAN_TCI, flow->vlan_tci, cr->wc.vlan_tci_mask);
+    }
 
     /* L3. */
     if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IP)) {
         /* IP. */
-        nxm_put_32m(b, NXM_OF_IP_SRC, flow->nw_src, cr->wc.nw_src_mask);
-        nxm_put_32m(b, NXM_OF_IP_DST, flow->nw_dst, cr->wc.nw_dst_mask);
-        nxm_put_ip(b, cr, IPPROTO_ICMP, NXM_OF_ICMP_TYPE, NXM_OF_ICMP_CODE);
+        nxm_put_32m(b, oxm ? OXM_OF_IPV4_SRC : NXM_OF_IP_SRC,
+                    flow->nw_src, cr->wc.nw_src_mask);
+        nxm_put_32m(b, oxm ? OXM_OF_IPV4_DST : NXM_OF_IP_DST,
+                    flow->nw_dst, cr->wc.nw_dst_mask);
+        nxm_put_ip(b, cr, IPPROTO_ICMP,
+                   oxm ? OXM_OF_ICMPV4_TYPE : NXM_OF_ICMP_TYPE,
+                   oxm ? OXM_OF_ICMPV4_CODE : NXM_OF_ICMP_CODE, oxm);
     } else if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IPV6)) {
         /* IPv6. */
-        nxm_put_ipv6(b, NXM_NX_IPV6_SRC, &flow->ipv6_src,
-                &cr->wc.ipv6_src_mask);
-        nxm_put_ipv6(b, NXM_NX_IPV6_DST, &flow->ipv6_dst,
-                &cr->wc.ipv6_dst_mask);
-        nxm_put_ip(b, cr,
-                   IPPROTO_ICMPV6, NXM_NX_ICMPV6_TYPE, NXM_NX_ICMPV6_CODE);
-
-        if (!(wc & FWW_IPV6_LABEL)) {
-            nxm_put_32(b, NXM_NX_IPV6_LABEL, flow->ipv6_label);
-        }
+        nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_SRC : NXM_NX_IPV6_SRC,
+                     &flow->ipv6_src, &cr->wc.ipv6_src_mask);
+        nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_DST : NXM_NX_IPV6_DST,
+                     &flow->ipv6_dst, &cr->wc.ipv6_dst_mask);
+        nxm_put_ip(b, cr, IPPROTO_ICMPV6,
+                   oxm ? OXM_OF_ICMPV6_TYPE : NXM_NX_ICMPV6_TYPE,
+                   oxm ? OXM_OF_ICMPV6_CODE : NXM_NX_ICMPV6_CODE, oxm);
+
+        nxm_put_32m(b, oxm ? OXM_OF_IPV6_FLABEL : NXM_NX_IPV6_LABEL,
+                    flow->ipv6_label, cr->wc.ipv6_label_mask);
 
         if (flow->nw_proto == IPPROTO_ICMPV6
             && (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
                 flow->tp_src == htons(ND_NEIGHBOR_ADVERT))) {
-            nxm_put_ipv6(b, NXM_NX_ND_TARGET, &flow->nd_target,
-                         &cr->wc.nd_target_mask);
-            if (!(wc & FWW_ARP_SHA)
-                && flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
-                nxm_put_eth(b, NXM_NX_ND_SLL, flow->arp_sha);
+            nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_ND_TARGET : NXM_NX_ND_TARGET,
+                         &flow->nd_target, &cr->wc.nd_target_mask);
+            if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
+                nxm_put_eth_masked(b, oxm ? OXM_OF_IPV6_ND_SLL : NXM_NX_ND_SLL,
+                                   flow->arp_sha, cr->wc.arp_sha_mask);
             }
-            if (!(wc & FWW_ARP_THA)
-                && flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
-                nxm_put_eth(b, NXM_NX_ND_TLL, flow->arp_tha);
+            if (flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+                nxm_put_eth_masked(b, oxm ? OXM_OF_IPV6_ND_TLL : NXM_NX_ND_TLL,
+                                   flow->arp_tha, cr->wc.arp_tha_mask);
             }
         }
     } else if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_ARP)) {
         /* ARP. */
         if (!(wc & FWW_NW_PROTO)) {
-            nxm_put_16(b, NXM_OF_ARP_OP, htons(flow->nw_proto));
-        }
-        nxm_put_32m(b, NXM_OF_ARP_SPA, flow->nw_src, cr->wc.nw_src_mask);
-        nxm_put_32m(b, NXM_OF_ARP_TPA, flow->nw_dst, cr->wc.nw_dst_mask);
-        if (!(wc & FWW_ARP_SHA)) {
-            nxm_put_eth(b, NXM_NX_ARP_SHA, flow->arp_sha);
-        }
-        if (!(wc & FWW_ARP_THA)) {
-            nxm_put_eth(b, NXM_NX_ARP_THA, flow->arp_tha);
+            nxm_put_16(b, oxm ? OXM_OF_ARP_OP : NXM_OF_ARP_OP,
+                       htons(flow->nw_proto));
         }
+        nxm_put_32m(b, oxm ? OXM_OF_ARP_SPA : NXM_OF_ARP_SPA,
+                    flow->nw_src, cr->wc.nw_src_mask);
+        nxm_put_32m(b, oxm ? OXM_OF_ARP_TPA : NXM_OF_ARP_TPA,
+                    flow->nw_dst, cr->wc.nw_dst_mask);
+        nxm_put_eth_masked(b, oxm ? OXM_OF_ARP_SHA : NXM_NX_ARP_SHA,
+                           flow->arp_sha, cr->wc.arp_sha_mask);
+        nxm_put_eth_masked(b, oxm ? OXM_OF_ARP_THA : NXM_NX_ARP_THA,
+                           flow->arp_tha, cr->wc.arp_tha_mask);
     }
 
     /* Tunnel ID. */
@@ -549,11 +662,63 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
                     htonl(flow->regs[i]), htonl(cr->wc.reg_masks[i]));
     }
 
+    /* OpenFlow 1.1+ Metadata. */
+    nxm_put_64m(b, OXM_OF_METADATA, flow->metadata, cr->wc.metadata_mask);
+
     /* Cookie. */
     nxm_put_64m(b, NXM_NX_COOKIE, cookie, cookie_mask);
 
     match_len = b->size - start_len;
+    return match_len;
+}
+
+/* Appends to 'b' the nx_match format that expresses 'cr' (except for
+ * 'cr->priority', because priority is not part of nx_match), plus enough zero
+ * bytes to pad the nx_match out to a multiple of 8.  For Flow Mod and Flow
+ * Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied.
+ * Otherwise, 'cookie_mask' should be zero.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b', excluding padding.  The return
+ * value can be zero if it appended nothing at all to 'b' (which happens if
+ * 'cr' is a catch-all rule that matches every packet). */
+int
+nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
+             ovs_be64 cookie, ovs_be64 cookie_mask)
+{
+    int match_len = nx_put_raw(b, false, cr, cookie, cookie_mask);
+
+    ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
+    return match_len;
+}
+
+
+/* Appends to 'b' an struct ofp11_match_header followed by the oxm format that
+ * expresses 'cr' (except for 'cr->priority', because priority is not part of
+ * nx_match), plus enough zero bytes to pad the data appended out to a multiple
+ * of 8.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b', excluding the padding.  Never
+ * returns zero. */
+int
+oxm_put_match(struct ofpbuf *b, const struct cls_rule *cr)
+{
+    int match_len;
+    struct ofp11_match_header *omh;
+    size_t start_len = b->size;
+    ovs_be64 cookie = htonll(0), cookie_mask = htonll(0);
+
+    ofpbuf_put_uninit(b, sizeof *omh);
+    match_len = nx_put_raw(b, true, cr, cookie, cookie_mask) + sizeof *omh;
     ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
+
+    omh = (struct ofp11_match_header *)((char *)b->data + start_len);
+    omh->type = htons(OFPMT_OXM);
+    omh->length = htons(match_len);
+
     return match_len;
 }
 \f
@@ -612,12 +777,49 @@ nx_match_to_string(const uint8_t *p, unsigned int match_len)
     return ds_steal_cstr(&s);
 }
 
+char *
+oxm_match_to_string(const uint8_t *p, unsigned int match_len)
+{
+    const struct ofp11_match_header *omh = (struct ofp11_match_header *)p;
+    uint16_t match_len_;
+    struct ds s;
+
+    ds_init(&s);
+
+    if (match_len < sizeof *omh) {
+        ds_put_format(&s, "<match too short: %u>", match_len);
+        goto err;
+    }
+
+    if (omh->type != htons(OFPMT_OXM)) {
+        ds_put_format(&s, "<bad match type field: %u>", ntohs(omh->type));
+        goto err;
+    }
+
+    match_len_ = ntohs(omh->length);
+    if (match_len_ < sizeof *omh) {
+        ds_put_format(&s, "<match length field too short: %u>", match_len_);
+        goto err;
+    }
+
+    if (match_len_ != match_len) {
+        ds_put_format(&s, "<match length field incorrect: %u != %u>",
+                      match_len_, match_len);
+        goto err;
+    }
+
+    return nx_match_to_string(p + sizeof *omh, match_len - sizeof *omh);
+
+err:
+    return ds_steal_cstr(&s);
+}
+
 static void
 format_nxm_field_name(struct ds *s, uint32_t header)
 {
     const struct mf_field *mf = mf_from_nxm_header(header);
     if (mf) {
-        ds_put_cstr(s, mf->nxm_name);
+        ds_put_cstr(s, IS_OXM_HEADER(header) ? mf->oxm_name : mf->nxm_name);
         if (NXM_HASMASK(header)) {
             ds_put_cstr(s, "_W");
         }
@@ -644,20 +846,29 @@ parse_nxm_field_name(const char *name, int name_len)
 
     for (i = 0; i < MFF_N_IDS; i++) {
         const struct mf_field *mf = mf_from_id(i);
+        uint32_t header;
 
-        if (mf->nxm_name
-            && !strncmp(mf->nxm_name, name, name_len)
-            && mf->nxm_name[name_len] == '\0') {
-            if (!wild) {
-                return mf->nxm_header;
-            } else if (mf->maskable != MFM_NONE) {
-                return NXM_MAKE_WILD_HEADER(mf->nxm_header);
-            }
+        if (mf->nxm_name &&
+            !strncmp(mf->nxm_name, name, name_len) &&
+            mf->nxm_name[name_len] == '\0') {
+            header = mf->nxm_header;
+        } else if (mf->oxm_name &&
+                   !strncmp(mf->oxm_name, name, name_len) &&
+                   mf->oxm_name[name_len] == '\0') {
+            header = mf->oxm_header;
+        } else {
+            continue;
+        }
+
+        if (!wild) {
+            return header;
+        } else if (mf->maskable != MFM_NONE) {
+            return NXM_MAKE_WILD_HEADER(header);
         }
     }
 
-    if (!strncmp("NXM_NX_COOKIE", name, name_len)
-                && (name_len == strlen("NXM_NX_COOKIE"))) {
+    if (!strncmp("NXM_NX_COOKIE", name, name_len) &&
+        (name_len == strlen("NXM_NX_COOKIE"))) {
         if (!wild) {
             return NXM_NX_COOKIE;
         } else {
@@ -679,12 +890,11 @@ parse_nxm_field_name(const char *name, int name_len)
 \f
 /* nx_match_from_string(). */
 
-int
-nx_match_from_string(const char *s, struct ofpbuf *b)
+static int
+nx_match_from_string_raw(const char *s, struct ofpbuf *b)
 {
     const char *full_s = s;
     const size_t start_len = b->size;
-    int match_len;
 
     if (!strcmp(s, "<any>")) {
         /* Ensure that 'b->data' isn't actually null. */
@@ -736,205 +946,213 @@ nx_match_from_string(const char *s, struct ofpbuf *b)
         s++;
     }
 
-    match_len = b->size - start_len;
+    return b->size - start_len;
+}
+
+int
+nx_match_from_string(const char *s, struct ofpbuf *b)
+{
+    int match_len = nx_match_from_string_raw(s, b);
+    ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
+    return match_len;
+}
+
+int
+oxm_match_from_string(const char *s, struct ofpbuf *b)
+{
+    int match_len;
+    struct ofp11_match_header *omh;
+    size_t start_len = b->size;
+
+    ofpbuf_put_uninit(b, sizeof *omh);
+    match_len = nx_match_from_string_raw(s, b) + sizeof *omh;
     ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
+
+    omh = (struct ofp11_match_header *)((char *)b->data + start_len);
+    omh->type = htons(OFPMT_OXM);
+    omh->length = htons(match_len);
+
     return match_len;
 }
 \f
 void
-nxm_parse_reg_move(struct nx_action_reg_move *move, const char *s)
+nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s)
 {
     const char *full_s = s;
-    struct mf_subfield src, dst;
 
-    s = mf_parse_subfield(&src, s);
+    s = mf_parse_subfield(&move->src, s);
     if (strncmp(s, "->", 2)) {
         ovs_fatal(0, "%s: missing `->' following source", full_s);
     }
     s += 2;
-    s = mf_parse_subfield(&dst, s);
+    s = mf_parse_subfield(&move->dst, s);
     if (*s != '\0') {
         ovs_fatal(0, "%s: trailing garbage following destination", full_s);
     }
 
-    if (src.n_bits != dst.n_bits) {
+    if (move->src.n_bits != move->dst.n_bits) {
         ovs_fatal(0, "%s: source field is %d bits wide but destination is "
-                  "%d bits wide", full_s, src.n_bits, dst.n_bits);
+                  "%d bits wide", full_s,
+                  move->src.n_bits, move->dst.n_bits);
     }
-
-    ofputil_init_NXAST_REG_MOVE(move);
-    move->n_bits = htons(src.n_bits);
-    move->src_ofs = htons(src.ofs);
-    move->dst_ofs = htons(dst.ofs);
-    move->src = htonl(src.field->nxm_header);
-    move->dst = htonl(dst.field->nxm_header);
 }
 
 void
-nxm_parse_reg_load(struct nx_action_reg_load *load, const char *s)
+nxm_parse_reg_load(struct ofpact_reg_load *load, const char *s)
 {
     const char *full_s = s;
-    struct mf_subfield dst;
-    uint64_t value;
 
-    value = strtoull(s, (char **) &s, 0);
+    load->value = strtoull(s, (char **) &s, 0);
     if (strncmp(s, "->", 2)) {
         ovs_fatal(0, "%s: missing `->' following value", full_s);
     }
     s += 2;
-    s = mf_parse_subfield(&dst, s);
+    s = mf_parse_subfield(&load->dst, s);
     if (*s != '\0') {
         ovs_fatal(0, "%s: trailing garbage following destination", full_s);
     }
 
-    if (dst.n_bits < 64 && (value >> dst.n_bits) != 0) {
-        ovs_fatal(0, "%s: value %"PRIu64" does not fit into %u bits",
-                  full_s, value, dst.n_bits);
+    if (load->dst.n_bits < 64 && (load->value >> load->dst.n_bits) != 0) {
+        ovs_fatal(0, "%s: value %"PRIu64" does not fit into %d bits",
+                  full_s, load->value, load->dst.n_bits);
     }
-
-    ofputil_init_NXAST_REG_LOAD(load);
-    load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
-    load->dst = htonl(dst.field->nxm_header);
-    load->value = htonll(value);
 }
 \f
 /* nxm_format_reg_move(), nxm_format_reg_load(). */
 
 void
-nxm_format_reg_move(const struct nx_action_reg_move *move, struct ds *s)
+nxm_format_reg_move(const struct ofpact_reg_move *move, struct ds *s)
 {
-    struct mf_subfield src, dst;
-
-    nxm_decode_discrete(&src, move->src, move->src_ofs, move->n_bits);
-    nxm_decode_discrete(&dst, move->dst, move->dst_ofs, move->n_bits);
-
     ds_put_format(s, "move:");
-    mf_format_subfield(&src, s);
+    mf_format_subfield(&move->src, s);
     ds_put_cstr(s, "->");
-    mf_format_subfield(&dst, s);
+    mf_format_subfield(&move->dst, s);
 }
 
 void
-nxm_format_reg_load(const struct nx_action_reg_load *load, struct ds *s)
+nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s)
 {
-    struct mf_subfield dst;
+    ds_put_format(s, "load:%#"PRIx64"->", load->value);
+    mf_format_subfield(&load->dst, s);
+}
+\f
+enum ofperr
+nxm_reg_move_from_openflow(const struct nx_action_reg_move *narm,
+                           struct ofpbuf *ofpacts)
+{
+    struct ofpact_reg_move *move;
 
-    ds_put_format(s, "load:%#"PRIx64"->", ntohll(load->value));
+    move = ofpact_put_REG_MOVE(ofpacts);
+    move->src.field = mf_from_nxm_header(ntohl(narm->src));
+    move->src.ofs = ntohs(narm->src_ofs);
+    move->src.n_bits = ntohs(narm->n_bits);
+    move->dst.field = mf_from_nxm_header(ntohl(narm->dst));
+    move->dst.ofs = ntohs(narm->dst_ofs);
+    move->dst.n_bits = ntohs(narm->n_bits);
 
-    nxm_decode(&dst, load->dst, load->ofs_nbits);
-    mf_format_subfield(&dst, s);
+    return nxm_reg_move_check(move, NULL);
 }
-\f
-/* nxm_check_reg_move(), nxm_check_reg_load(). */
 
 enum ofperr
-nxm_check_reg_move(const struct nx_action_reg_move *action,
-                   const struct flow *flow)
+nxm_reg_load_from_openflow(const struct nx_action_reg_load *narl,
+                           struct ofpbuf *ofpacts)
 {
-    struct mf_subfield src;
-    struct mf_subfield dst;
-    int error;
+    struct ofpact_reg_load *load;
 
-    nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits);
-    error = mf_check_src(&src, flow);
-    if (error) {
-        return error;
+    load = ofpact_put_REG_LOAD(ofpacts);
+    load->dst.field = mf_from_nxm_header(ntohl(narl->dst));
+    load->dst.ofs = nxm_decode_ofs(narl->ofs_nbits);
+    load->dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits);
+    load->value = ntohll(narl->value);
+
+    /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in
+     * narl->value. */
+    if (load->dst.n_bits < 64 && load->value >> load->dst.n_bits) {
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
 
-    nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits);
-    return mf_check_dst(&dst, flow);
+    return nxm_reg_load_check(load, NULL);
 }
-
+\f
 enum ofperr
-nxm_check_reg_load(const struct nx_action_reg_load *action,
-                   const struct flow *flow)
+nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow)
 {
-    struct mf_subfield dst;
     enum ofperr error;
 
-    nxm_decode(&dst, action->dst, action->ofs_nbits);
-    error = mf_check_dst(&dst, flow);
+    error = mf_check_src(&move->src, flow);
     if (error) {
         return error;
     }
 
-    /* Reject 'action' if a bit numbered 'n_bits' or higher is set to 1 in
-     * action->value. */
-    if (dst.n_bits < 64 && ntohll(action->value) >> dst.n_bits) {
-        return OFPERR_OFPBAC_BAD_ARGUMENT;
-    }
+    return mf_check_dst(&move->dst, NULL);
+}
 
-    return 0;
+enum ofperr
+nxm_reg_load_check(const struct ofpact_reg_load *load, const struct flow *flow)
+{
+    return mf_check_dst(&load->dst, flow);
 }
 \f
-/* nxm_execute_reg_move(), nxm_execute_reg_load(). */
-
 void
-nxm_execute_reg_move(const struct nx_action_reg_move *action,
-                     struct flow *flow)
+nxm_reg_move_to_nxast(const struct ofpact_reg_move *move,
+                      struct ofpbuf *openflow)
 {
-    struct mf_subfield src, dst;
-    union mf_value src_value;
-    union mf_value dst_value;
+    struct nx_action_reg_move *narm;
+
+    narm = ofputil_put_NXAST_REG_MOVE(openflow);
+    narm->n_bits = htons(move->dst.n_bits);
+    narm->src_ofs = htons(move->src.ofs);
+    narm->dst_ofs = htons(move->dst.ofs);
+    narm->src = htonl(move->src.field->nxm_header);
+    narm->dst = htonl(move->dst.field->nxm_header);
+}
 
-    nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits);
-    nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits);
+void
+nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
+                      struct ofpbuf *openflow)
+{
+    struct nx_action_reg_load *narl;
 
-    mf_get_value(dst.field, flow, &dst_value);
-    mf_get_value(src.field, flow, &src_value);
-    bitwise_copy(&src_value, src.field->n_bytes, src.ofs,
-                 &dst_value, dst.field->n_bytes, dst.ofs,
-                 src.n_bits);
-    mf_set_flow_value(dst.field, &dst_value, flow);
+    narl = ofputil_put_NXAST_REG_LOAD(openflow);
+    narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits);
+    narl->dst = htonl(load->dst.field->nxm_header);
+    narl->value = htonll(load->value);
 }
+\f
+/* nxm_execute_reg_move(), nxm_execute_reg_load(). */
 
 void
-nxm_execute_reg_load(const struct nx_action_reg_load *action,
+nxm_execute_reg_move(const struct ofpact_reg_move *move,
                      struct flow *flow)
 {
-    struct mf_subfield dst;
+    union mf_value src_value;
+    union mf_value dst_value;
 
-    nxm_decode(&dst, action->dst, action->ofs_nbits);
-    mf_set_subfield_value(&dst, ntohll(action->value), flow);
+    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,
+                 &dst_value, move->dst.field->n_bytes, move->dst.ofs,
+                 move->src.n_bits);
+    mf_set_flow_value(move->dst.field, &dst_value, flow);
 }
 
-/* Initializes 'sf->field' with the field corresponding to the given NXM
- * 'header' and 'sf->ofs' and 'sf->n_bits' decoded from 'ofs_nbits' with
- * nxm_decode_ofs() and nxm_decode_n_bits(), respectively.
- *
- * Afterward, 'sf' might be invalid in a few different ways:
- *
- *   - 'sf->field' will be NULL if 'header' is unknown.
- *
- *   - 'sf->ofs' and 'sf->n_bits' might exceed the width of sf->field.
- *
- * The caller should call mf_check_src() or mf_check_dst() to check for these
- * problems. */
 void
-nxm_decode(struct mf_subfield *sf, ovs_be32 header, ovs_be16 ofs_nbits)
+nxm_execute_reg_load(const struct ofpact_reg_load *load, struct flow *flow)
 {
-    sf->field = mf_from_nxm_header(ntohl(header));
-    sf->ofs = nxm_decode_ofs(ofs_nbits);
-    sf->n_bits = nxm_decode_n_bits(ofs_nbits);
+    nxm_reg_load(&load->dst, load->value, flow);
 }
 
-/* Initializes 'sf->field' with the field corresponding to the given NXM
- * 'header' and 'sf->ofs' and 'sf->n_bits' from 'ofs' and 'n_bits',
- * respectively.
- *
- * Afterward, 'sf' might be invalid in a few different ways:
- *
- *   - 'sf->field' will be NULL if 'header' is unknown.
- *
- *   - 'sf->ofs' and 'sf->n_bits' might exceed the width of sf->field.
- *
- * The caller should call mf_check_src() or mf_check_dst() to check for these
- * problems. */
 void
-nxm_decode_discrete(struct mf_subfield *sf, ovs_be32 header,
-                    ovs_be16 ofs, ovs_be16 n_bits)
+nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
+             struct flow *flow)
 {
-    sf->field = mf_from_nxm_header(ntohl(header));
-    sf->ofs = ntohs(ofs);
-    sf->n_bits = ntohs(n_bits);
+    union mf_value dst_value;
+    union mf_value src_value;
+
+    mf_get_value(dst->field, flow, &dst_value);
+    src_value.be64 = htonll(src_data);
+    bitwise_copy(&src_value, sizeof src_value.be64, 0,
+                 &dst_value, dst->field->n_bytes, dst->ofs,
+                 dst->n_bits);
+    mf_set_flow_value(dst->field, &dst_value, flow);
 }
index 296a63a..3bfeeb7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <netinet/in.h>
 #include "flow.h"
+#include "ofp-errors.h"
 #include "openvswitch/types.h"
 #include "ofp-errors.h"
 
@@ -28,6 +29,8 @@ struct cls_rule;
 struct ds;
 struct flow;
 struct mf_subfield;
+struct ofpact_reg_move;
+struct ofpact_reg_load;
 struct ofpbuf;
 struct nx_action_reg_load;
 struct nx_action_reg_move;
@@ -41,27 +44,47 @@ enum ofperr nx_pull_match(struct ofpbuf *, unsigned int match_len,
                           uint16_t priority, struct cls_rule *,
                           ovs_be64 *cookie, ovs_be64 *cookie_mask);
 enum ofperr nx_pull_match_loose(struct ofpbuf *, unsigned int match_len,
-                                uint16_t priority, struct cls_rule *,
-                                ovs_be64 *cookie, ovs_be64 *cookie_mask);
+                                uint16_t priority,
+                                struct cls_rule *, ovs_be64 *cookie,
+                                ovs_be64 *cookie_mask);
+enum ofperr oxm_pull_match(struct ofpbuf *, uint16_t priority,
+                           struct cls_rule *);
+enum ofperr oxm_pull_match_loose(struct ofpbuf *, uint16_t priority,
+                                 struct cls_rule *);
 int nx_put_match(struct ofpbuf *, const struct cls_rule *,
                  ovs_be64 cookie, ovs_be64 cookie_mask);
+int oxm_put_match(struct ofpbuf *, const struct cls_rule *);
 
 char *nx_match_to_string(const uint8_t *, unsigned int match_len);
+char *oxm_match_to_string(const uint8_t *, unsigned int match_len);
 int nx_match_from_string(const char *, struct ofpbuf *);
+int oxm_match_from_string(const char *, struct ofpbuf *);
+
+void nxm_parse_reg_move(struct ofpact_reg_move *, const char *);
+void nxm_parse_reg_load(struct ofpact_reg_load *, const char *);
 
-void nxm_parse_reg_move(struct nx_action_reg_move *, const char *);
-void nxm_parse_reg_load(struct nx_action_reg_load *, const char *);
+void nxm_format_reg_move(const struct ofpact_reg_move *, struct ds *);
+void nxm_format_reg_load(const struct ofpact_reg_load *, struct ds *);
 
-void nxm_format_reg_move(const struct nx_action_reg_move *, struct ds *);
-void nxm_format_reg_load(const struct nx_action_reg_load *, struct ds *);
+enum ofperr nxm_reg_move_from_openflow(const struct nx_action_reg_move *,
+                                       struct ofpbuf *ofpacts);
+enum ofperr nxm_reg_load_from_openflow(const struct nx_action_reg_load *,
+                                       struct ofpbuf *ofpacts);
 
-enum ofperr nxm_check_reg_move(const struct nx_action_reg_move *,
+enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *,
                                const struct flow *);
-enum ofperr nxm_check_reg_load(const struct nx_action_reg_load *,
+enum ofperr nxm_reg_load_check(const struct ofpact_reg_load *,
                                const struct flow *);
 
-void nxm_execute_reg_move(const struct nx_action_reg_move *, struct flow *);
-void nxm_execute_reg_load(const struct nx_action_reg_load *, struct flow *);
+void nxm_reg_move_to_nxast(const struct ofpact_reg_move *,
+                           struct ofpbuf *openflow);
+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_load(const struct ofpact_reg_load *, struct flow *);
+void nxm_reg_load(const struct mf_subfield *, uint64_t src_data,
+                  struct flow *);
 
 int nxm_field_bytes(uint32_t header);
 int nxm_field_bits(uint32_t header);
@@ -85,50 +108,7 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits)
 {
     return (ntohs(ofs_nbits) & 0x3f) + 1;
 }
-
-void nxm_decode(struct mf_subfield *, ovs_be32 header, ovs_be16 ofs_nbits);
-void nxm_decode_discrete(struct mf_subfield *, ovs_be32 header,
-                         ovs_be16 ofs, ovs_be16 n_bits);
 \f
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
-/* Upper bound on the length of an nx_match.  The longest nx_match (an
- * IPV6 neighbor discovery message using 5 registers) would be:
- *
- *                   header  value  mask  total
- *                   ------  -----  ----  -----
- *  NXM_OF_IN_PORT      4       2    --      6
- *  NXM_OF_ETH_DST_W    4       6     6     16
- *  NXM_OF_ETH_SRC      4       6    --     10
- *  NXM_OF_ETH_TYPE     4       2    --      6
- *  NXM_OF_VLAN_TCI     4       2     2      8
- *  NXM_OF_IP_TOS       4       1    --      5
- *  NXM_NX_IP_ECN       4       1    --      5
- *  NXM_OF_IP_TTL       4       1    --      5
- *  NXM_NX_IP_FRAG      4       1     1      8
- *  NXM_OF_IP_PROTO     4       2    --      6
- *  NXM_OF_IPV6_SRC_W   4      16    16     36
- *  NXM_OF_IPV6_DST_W   4      16    16     36
- *  NXM_OF_IPV6_LABEL   4       4    --      8
- *  NXM_OF_ICMP_TYPE    4       1    --      5
- *  NXM_OF_ICMP_CODE    4       1    --      5
- *  NXM_NX_ND_TARGET    4      16    16     36
- *  NXM_NX_ND_SLL       4       6    --     10
- *  NXM_NX_REG_W(0)     4       4     4     12
- *  NXM_NX_REG_W(1)     4       4     4     12
- *  NXM_NX_REG_W(2)     4       4     4     12
- *  NXM_NX_REG_W(3)     4       4     4     12
- *  NXM_NX_REG_W(4)     4       4     4     12
- *  NXM_NX_REG_W(5)     4       4     4     12
- *  NXM_NX_REG_W(6)     4       4     4     12
- *  NXM_NX_REG_W(7)     4       4     4     12
- *  NXM_NX_TUN_ID_W     4       8     8     20
- *  -------------------------------------------
- *  total                                  327
- *
- * So this value is conservative.
- */
-#define NXM_MAX_LEN 400
-
 /* This is my guess at the length of a "typical" nx_match, for use in
  * predicting space requirements. */
 #define NXM_TYPICAL_LEN 64
index 8fa3359..901dac3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include <arpa/inet.h>
 #include <config.h>
+#include <arpa/inet.h>
 #include "odp-util.h"
 #include <errno.h>
 #include <inttypes.h>
@@ -30,9 +30,8 @@
 #include "flow.h"
 #include "netlink.h"
 #include "ofpbuf.h"
-#include "openvswitch/tunnel.h"
 #include "packets.h"
-#include "shash.h"
+#include "simap.h"
 #include "timeval.h"
 #include "util.h"
 #include "vlog.h"
@@ -49,7 +48,7 @@ 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 shash *port_names,
+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);
 
@@ -166,6 +165,52 @@ format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
     ds_put_format(ds, "))");
 }
 
+static const char *
+slow_path_reason_to_string(enum slow_path_reason bit)
+{
+    switch (bit) {
+    case SLOW_CFM:
+        return "cfm";
+    case SLOW_LACP:
+        return "lacp";
+    case SLOW_STP:
+        return "stp";
+    case SLOW_IN_BAND:
+        return "in_band";
+    case SLOW_CONTROLLER:
+        return "controller";
+    case SLOW_MATCH:
+        return "match";
+    default:
+        return NULL;
+    }
+}
+
+static void
+format_slow_path_reason(struct ds *ds, uint32_t slow)
+{
+    uint32_t bad = 0;
+
+    while (slow) {
+        uint32_t bit = rightmost_1bit(slow);
+        const char *s;
+
+        s = slow_path_reason_to_string(bit);
+        if (s) {
+            ds_put_format(ds, "%s,", s);
+        } else {
+            bad |= bit;
+        }
+
+        slow &= ~bit;
+    }
+
+    if (bad) {
+        ds_put_format(ds, "0x%"PRIx32",", bad);
+    }
+    ds_chomp(ds, ',');
+}
+
 static void
 format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
 {
@@ -185,17 +230,31 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
 
     if (a[OVS_USERSPACE_ATTR_USERDATA]) {
         uint64_t userdata = nl_attr_get_u64(a[OVS_USERSPACE_ATTR_USERDATA]);
-        struct user_action_cookie cookie;
+        union user_action_cookie cookie;
 
         memcpy(&cookie, &userdata, sizeof cookie);
 
-        if (cookie.type == USER_ACTION_COOKIE_SFLOW) {
-            ds_put_format(ds, ",sFlow,n_output=%"PRIu8","
-                          "vid=%"PRIu16",pcp=%"PRIu8",ifindex=%"PRIu32,
-                          cookie.n_output, vlan_tci_to_vid(cookie.vlan_tci),
-                          vlan_tci_to_pcp(cookie.vlan_tci), cookie.data);
-        } else {
+        switch (cookie.type) {
+        case USER_ACTION_COOKIE_SFLOW:
+            ds_put_format(ds, ",sFlow("
+                          "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
+                          vlan_tci_to_vid(cookie.sflow.vlan_tci),
+                          vlan_tci_to_pcp(cookie.sflow.vlan_tci),
+                          cookie.sflow.output);
+            break;
+
+        case USER_ACTION_COOKIE_SLOW_PATH:
+            ds_put_cstr(ds, ",slow_path(");
+            if (cookie.slow_path.reason) {
+                format_slow_path_reason(ds, cookie.slow_path.reason);
+            }
+            ds_put_char(ds, ')');
+            break;
+
+        case USER_ACTION_COOKIE_UNSPEC:
+        default:
             ds_put_format(ds, ",userdata=0x%"PRIx64, userdata);
+            break;
         }
     }
 
@@ -295,7 +354,7 @@ format_odp_actions(struct ds *ds, const struct nlattr *actions,
 }
 
 static int
-parse_odp_action(const char *s, const struct shash *port_names,
+parse_odp_action(const char *s, const struct simap *port_names,
                  struct ofpbuf *actions)
 {
     /* Many of the sscanf calls in this function use oversized destination
@@ -320,31 +379,29 @@ parse_odp_action(const char *s, const struct shash *port_names,
 
     if (port_names) {
         int len = strcspn(s, delimiters);
-        struct shash_node *node;
+        struct simap_node *node;
 
-        node = shash_find_len(port_names, s, len);
+        node = simap_find_len(port_names, s, len);
         if (node) {
-            nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT,
-                           (uintptr_t) node->data);
+            nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data);
             return len;
         }
     }
 
     {
         unsigned long long int pid;
-        unsigned long long int ifindex;
+        unsigned long long int output;
         char userdata_s[32];
-        int n_output;
         int vid, pcp;
         int n = -1;
 
         if (sscanf(s, "userspace(pid=%lli)%n", &pid, &n) > 0 && n > 0) {
             odp_put_userspace_action(pid, NULL, actions);
             return n;
-        } else if (sscanf(s, "userspace(pid=%lli,sFlow,n_output=%i,vid=%i,"
-                          "pcp=%i,ifindex=%lli)%n", &pid, &n_output,
-                          &vid, &pcp, &ifindex, &n) > 0 && n > 0) {
-            struct user_action_cookie cookie;
+        } else if (sscanf(s, "userspace(pid=%lli,sFlow(vid=%i,"
+                          "pcp=%i,output=%lli))%n",
+                          &pid, &vid, &pcp, &output, &n) > 0 && n > 0) {
+            union user_action_cookie cookie;
             uint16_t tci;
 
             tci = vid | (pcp << VLAN_PCP_SHIFT);
@@ -353,15 +410,50 @@ parse_odp_action(const char *s, const struct shash *port_names,
             }
 
             cookie.type = USER_ACTION_COOKIE_SFLOW;
-            cookie.n_output = n_output;
-            cookie.vlan_tci = htons(tci);
-            cookie.data = ifindex;
+            cookie.sflow.vlan_tci = htons(tci);
+            cookie.sflow.output = output;
+            odp_put_userspace_action(pid, &cookie, actions);
+            return n;
+        } else if (sscanf(s, "userspace(pid=%lli,slow_path(%n", &pid, &n) > 0
+                   && n > 0) {
+            union user_action_cookie cookie;
+
+            cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
+            cookie.slow_path.unused = 0;
+            cookie.slow_path.reason = 0;
+
+            while (s[n] != ')') {
+                uint32_t bit;
+
+                for (bit = 1; bit; bit <<= 1) {
+                    const char *reason = slow_path_reason_to_string(bit);
+                    size_t len = strlen(reason);
+
+                    if (reason
+                        && !strncmp(s + n, reason, len)
+                        && (s[n + len] == ',' || s[n + len] == ')'))
+                    {
+                        cookie.slow_path.reason |= bit;
+                        n += len + (s[n + len] == ',');
+                        break;
+                    }
+                }
+
+                if (!bit) {
+                    return -EINVAL;
+                }
+            }
+            if (s[n + 1] != ')') {
+                return -EINVAL;
+            }
+            n += 2;
+
             odp_put_userspace_action(pid, &cookie, actions);
             return n;
         } else if (sscanf(s, "userspace(pid=%lli,userdata="
                           "%31[x0123456789abcdefABCDEF])%n", &pid, userdata_s,
                           &n) > 0 && n > 0) {
-            struct user_action_cookie cookie;
+            union user_action_cookie cookie;
             uint64_t userdata;
 
             userdata = strtoull(userdata_s, NULL, 0);
@@ -440,7 +532,7 @@ parse_odp_action(const char *s, const struct shash *port_names,
             for (;;) {
                 int retval;
 
-                s += strspn(s, delimiters);
+                n += strspn(s + n, delimiters);
                 if (s[n] == ')') {
                     break;
                 }
@@ -450,7 +542,6 @@ parse_odp_action(const char *s, const struct shash *port_names,
                     return retval;
                 }
                 n += retval;
-
             }
             nl_msg_end_nested(actions, actions_ofs);
             nl_msg_end_nested(actions, sample_ofs);
@@ -468,7 +559,7 @@ parse_odp_action(const char *s, const struct shash *port_names,
  * Netlink attributes.  On failure, no data is appended to 'actions'.  Either
  * way, 'actions''s data might be reallocated. */
 int
-odp_actions_from_string(const char *s, const struct shash *port_names,
+odp_actions_from_string(const char *s, const struct simap *port_names,
                         struct ofpbuf *actions)
 {
     size_t old_size;
@@ -785,7 +876,7 @@ ovs_frag_type_from_string(const char *s, enum ovs_frag_type *type)
 }
 
 static int
-parse_odp_key_attr(const char *s, const struct shash *port_names,
+parse_odp_key_attr(const char *s, const struct simap *port_names,
                    struct ofpbuf *key)
 {
     /* Many of the sscanf calls in this function use oversized destination
@@ -832,14 +923,14 @@ parse_odp_key_attr(const char *s, const struct shash *port_names,
 
     if (port_names && !strncmp(s, "in_port(", 8)) {
         const char *name;
-        const struct shash_node *node;
+        const struct simap_node *node;
         int name_len;
 
         name = s + 8;
         name_len = strcspn(s, ")");
-        node = shash_find_len(port_names, name, name_len);
+        node = simap_find_len(port_names, name, name_len);
         if (node) {
-            nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, (uintptr_t) node->data);
+            nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, node->data);
             return 8 + name_len + 1;
         }
     }
@@ -1116,15 +1207,15 @@ parse_odp_key_attr(const char *s, const struct shash *port_names,
  * data is appended to 'key'.  Either way, 'key''s data might be
  * reallocated.
  *
- * If 'port_names' is nonnull, it points to an shash that maps from a port name
- * to a port number cast to void *.  (Port names may be used instead of port
- * numbers in in_port.)
+ * If 'port_names' is nonnull, it points to an simap that maps from a port name
+ * to a port number.  (Port names may be used instead of port numbers in
+ * in_port.)
  *
  * On success, the attributes appended to 'key' are individually syntactically
  * 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 shash *port_names,
+odp_flow_key_from_string(const char *s, const struct simap *port_names,
                          struct ofpbuf *key)
 {
     const size_t old_size = key->size;
@@ -1155,7 +1246,10 @@ ovs_to_odp_frag(uint8_t nw_frag)
           : OVS_FRAG_TYPE_LATER);
 }
 
-/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'. */
+/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
+ *
+ * '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)
 {
@@ -1170,7 +1264,7 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
         nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tun_id);
     }
 
-    if (flow->in_port != OFPP_NONE) {
+    if (flow->in_port != OFPP_NONE && flow->in_port != OFPP_CONTROLLER) {
         nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT,
                        ofp_port_to_odp_port(flow->in_port));
     }
@@ -1723,7 +1817,7 @@ odp_key_fitness_to_string(enum odp_key_fitness fitness)
  * the start of the cookie.  (If 'cookie' is null, then the return value is not
  * meaningful.) */
 size_t
-odp_put_userspace_action(uint32_t pid, const struct user_action_cookie *cookie,
+odp_put_userspace_action(uint32_t pid, const union user_action_cookie *cookie,
                          struct ofpbuf *odp_actions)
 {
     size_t offset;
@@ -1882,7 +1976,7 @@ static void
 commit_set_port_action(const struct flow *flow, struct flow *base,
                        struct ofpbuf *odp_actions)
 {
-    if (!base->tp_src || !base->tp_dst) {
+    if (!base->tp_src && !base->tp_dst) {
         return;
     }
 
index 0028499..16f2b15 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,9 +30,9 @@ struct ds;
 struct flow;
 struct nlattr;
 struct ofpbuf;
-struct shash;
+struct simap;
 
-#define OVSP_NONE ((uint16_t) -1)
+#define OVSP_NONE UINT16_MAX
 
 static inline uint16_t
 ofp_port_to_odp_port(uint16_t ofp_port)
@@ -62,11 +62,19 @@ odp_port_to_ofp_port(uint16_t odp_port)
 
 void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
                         size_t actions_len);
-int odp_actions_from_string(const char *, const struct shash *port_names,
+int odp_actions_from_string(const char *, const struct simap *port_names,
                             struct ofpbuf *odp_actions);
 
-/* Upper bound on the length of a nlattr-formatted flow key.  The longest
- * nlattr-formatted flow key would be:
+/* The maximum number of bytes that odp_flow_key_from_flow() appends to a
+ * buffer.  This is the upper bound on the length of a nlattr-formatted flow
+ * key that ovs-vswitchd fully understands.
+ *
+ * OVS doesn't insist that ovs-vswitchd and the datapath have exactly the same
+ * idea of a flow, so therefore this value isn't necessarily an upper bound on
+ * the length of a flow key that the datapath can pass to ovs-vswitchd.
+ *
+ * The longest nlattr-formatted flow key appended by odp_flow_key_from_flow()
+ * would be:
  *
  *                         struct  pad  nl hdr  total
  *                         ------  ---  ------  -----
@@ -74,15 +82,20 @@ int odp_actions_from_string(const char *, const struct shash *port_names,
  *  OVS_KEY_ATTR_TUN_ID        8    --     4     12
  *  OVS_KEY_ATTR_IN_PORT       4    --     4      8
  *  OVS_KEY_ATTR_ETHERNET     12    --     4     16
+ *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (outer VLAN ethertype)
  *  OVS_KEY_ATTR_8021Q         4    --     4      8
- *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8
+ *  OVS_KEY_ATTR_ENCAP         0    --     4      4  (VLAN encapsulation)
+ *  OVS_KEY_ATTR_ETHERTYPE     2     2     4      8  (inner VLAN ethertype)
  *  OVS_KEY_ATTR_IPV6         40    --     4     44
  *  OVS_KEY_ATTR_ICMPV6        2     2     4      8
  *  OVS_KEY_ATTR_ND           28    --     4     32
  *  -------------------------------------------------
- *  total                                       144
+ *  total                                       156
+ *
+ * We include some slack space in case the calculation isn't quite right or we
+ * add another field and forget to adjust this value.
  */
-#define ODPUTIL_FLOW_KEY_BYTES 144
+#define ODPUTIL_FLOW_KEY_BYTES 200
 
 /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
  * key.  An array of "struct nlattr" might not, in theory, be sufficiently
@@ -92,7 +105,7 @@ struct odputil_keybuf {
 };
 
 void odp_flow_key_format(const struct nlattr *, size_t, struct ds *);
-int odp_flow_key_from_string(const char *s, const struct shash *port_names,
+int odp_flow_key_from_string(const char *s, const struct simap *port_names,
                              struct ofpbuf *);
 
 void odp_flow_key_from_flow(struct ofpbuf *, const struct flow *);
@@ -116,27 +129,60 @@ enum odp_key_fitness odp_flow_key_to_flow(const struct nlattr *, size_t,
                                           struct flow *);
 const char *odp_key_fitness_to_string(enum odp_key_fitness);
 
+void commit_odp_actions(const struct flow *, struct flow *base,
+                        struct ofpbuf *odp_actions);
+\f
+/* ofproto-dpif interface.
+ *
+ * The following types and functions are logically part of ofproto-dpif.
+ * ofproto-dpif puts values of these types into the flows that it installs in
+ * the kernel datapath, though, so ovs-dpctl needs to interpret them so that
+ * it can print flows in a more human-readable manner. */
+
 enum user_action_cookie_type {
     USER_ACTION_COOKIE_UNSPEC,
     USER_ACTION_COOKIE_SFLOW,        /* Packet for sFlow sampling. */
+    USER_ACTION_COOKIE_SLOW_PATH     /* Userspace must process this flow. */
 };
 
 /* user_action_cookie is passed as argument to OVS_ACTION_ATTR_USERSPACE.
- * Since is it passed to kernel as u64, its size has to be 8 bytes. */
-struct user_action_cookie {
-    uint8_t   type;                 /* enum user_action_cookie_type. */
-    uint8_t   n_output;             /* No of output ports. used by sflow. */
-    ovs_be16  vlan_tci;             /* Used by sFlow */
-    uint32_t  data;                 /* Data is len for OFPP_CONTROLLER action.
-                                       For sFlow it is port_ifindex. */
+ * Since it is passed to kernel as u64, its size has to be 8 bytes. */
+union user_action_cookie {
+    uint16_t type;              /* enum user_action_cookie_type. */
+
+    struct {
+        uint16_t type;          /* USER_ACTION_COOKIE_SFLOW. */
+        ovs_be16 vlan_tci;      /* Destination VLAN TCI. */
+        uint32_t output;        /* SFL_FLOW_SAMPLE_TYPE 'output' value. */
+    } sflow;
+
+    struct {
+        uint16_t type;          /* USER_ACTION_COOKIE_SLOW_PATH. */
+        uint16_t unused;
+        uint32_t reason;        /* enum slow_path_reason. */
+    } slow_path;
 };
-
-BUILD_ASSERT_DECL(sizeof(struct user_action_cookie) == 8);
+BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 8);
 
 size_t odp_put_userspace_action(uint32_t pid,
-                                const struct user_action_cookie *,
+                                const union user_action_cookie *,
                                 struct ofpbuf *odp_actions);
 
-void commit_odp_actions(const struct flow *, struct flow *base,
-                        struct ofpbuf *odp_actions);
+/* 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. */
+
+    /* Mutually exclusive with SLOW_CFM, SLOW_LACP, SLOW_STP.
+     * Could possibly appear with SLOW_IN_BAND. */
+    SLOW_CONTROLLER = 1 << 4,   /* 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 << 5,        /* Datapath can't match specifically enough. */
+};
+
 #endif /* odp-util.h */
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
new file mode 100644 (file)
index 0000000..898455e
--- /dev/null
@@ -0,0 +1,1882 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "ofp-actions.h"
+#include "autopath.h"
+#include "bundle.h"
+#include "byte-order.h"
+#include "compiler.h"
+#include "dynamic-string.h"
+#include "learn.h"
+#include "meta-flow.h"
+#include "multipath.h"
+#include "nx-match.h"
+#include "ofp-util.h"
+#include "ofpbuf.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ofp_actions);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+\f
+/* Converting OpenFlow 1.0 to ofpacts. */
+
+static enum ofperr
+output_from_openflow10(const struct ofp10_action_output *oao,
+                       struct ofpbuf *out)
+{
+    struct ofpact_output *output;
+
+    output = ofpact_put_OUTPUT(out);
+    output->port = ntohs(oao->port);
+    output->max_len = ntohs(oao->max_len);
+
+    return ofputil_check_output_port(output->port, OFPP_MAX);
+}
+
+static enum ofperr
+enqueue_from_openflow10(const struct ofp_action_enqueue *oae,
+                        struct ofpbuf *out)
+{
+    struct ofpact_enqueue *enqueue;
+
+    enqueue = ofpact_put_ENQUEUE(out);
+    enqueue->port = ntohs(oae->port);
+    enqueue->queue = ntohl(oae->queue_id);
+    if (enqueue->port >= OFPP_MAX && enqueue->port != OFPP_IN_PORT
+        && enqueue->port != OFPP_LOCAL) {
+        return OFPERR_OFPBAC_BAD_OUT_PORT;
+    }
+    return 0;
+}
+
+static void
+resubmit_from_openflow(const struct nx_action_resubmit *nar,
+                       struct ofpbuf *out)
+{
+    struct ofpact_resubmit *resubmit;
+
+    resubmit = ofpact_put_RESUBMIT(out);
+    resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT;
+    resubmit->in_port = ntohs(nar->in_port);
+    resubmit->table_id = 0xff;
+}
+
+static enum ofperr
+resubmit_table_from_openflow(const struct nx_action_resubmit *nar,
+                             struct ofpbuf *out)
+{
+    struct ofpact_resubmit *resubmit;
+
+    if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
+    }
+
+    resubmit = ofpact_put_RESUBMIT(out);
+    resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT_TABLE;
+    resubmit->in_port = ntohs(nar->in_port);
+    resubmit->table_id = nar->table;
+    return 0;
+}
+
+static enum ofperr
+output_reg_from_openflow(const struct nx_action_output_reg *naor,
+                         struct ofpbuf *out)
+{
+    struct ofpact_output_reg *output_reg;
+
+    if (!is_all_zeros(naor->zero, sizeof naor->zero)) {
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
+    }
+
+    output_reg = ofpact_put_OUTPUT_REG(out);
+    output_reg->src.field = mf_from_nxm_header(ntohl(naor->src));
+    output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits);
+    output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits);
+    output_reg->max_len = ntohs(naor->max_len);
+
+    return mf_check_src(&output_reg->src, NULL);
+}
+
+static void
+fin_timeout_from_openflow(const struct nx_action_fin_timeout *naft,
+                          struct ofpbuf *out)
+{
+    struct ofpact_fin_timeout *oft;
+
+    oft = ofpact_put_FIN_TIMEOUT(out);
+    oft->fin_idle_timeout = ntohs(naft->fin_idle_timeout);
+    oft->fin_hard_timeout = ntohs(naft->fin_hard_timeout);
+}
+
+static void
+controller_from_openflow(const struct nx_action_controller *nac,
+                         struct ofpbuf *out)
+{
+    struct ofpact_controller *oc;
+
+    oc = ofpact_put_CONTROLLER(out);
+    oc->max_len = ntohs(nac->max_len);
+    oc->controller_id = ntohs(nac->controller_id);
+    oc->reason = nac->reason;
+}
+
+static void
+note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out)
+{
+    struct ofpact_note *note;
+    unsigned int length;
+
+    length = ntohs(nan->len) - offsetof(struct nx_action_note, note);
+    note = ofpact_put(out, OFPACT_NOTE,
+                      offsetof(struct ofpact_note, data) + length);
+    note->length = length;
+    memcpy(note->data, nan->note, length);
+}
+
+static enum ofperr
+dec_ttl_from_openflow(struct ofpbuf *out)
+{
+    uint16_t id = 0;
+    struct ofpact_cnt_ids *ids;
+    enum ofperr error = 0;
+
+    ids = ofpact_put_DEC_TTL(out);
+    ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
+    ids->n_controllers = 1;
+    ofpbuf_put(out, &id, sizeof id);
+    ids = out->l2;
+    ofpact_update_len(out, &ids->ofpact);
+    return error;
+}
+
+static enum ofperr
+dec_ttl_cnt_ids_from_openflow(const struct nx_action_cnt_ids *nac_ids,
+                      struct ofpbuf *out)
+{
+    struct ofpact_cnt_ids *ids;
+    size_t ids_size;
+    int i;
+
+    ids = ofpact_put_DEC_TTL(out);
+    ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS;
+    ids->n_controllers = ntohs(nac_ids->n_controllers);
+    ids_size = ntohs(nac_ids->len) - sizeof *nac_ids;
+
+    if (!is_all_zeros(nac_ids->zeros, sizeof nac_ids->zeros)) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
+
+    if (ids_size < ids->n_controllers * sizeof(ovs_be16)) {
+        VLOG_WARN_RL(&rl, "Nicira action dec_ttl_cnt_ids only has %zu bytes "
+                     "allocated for controller ids.  %zu bytes are required for "
+                     "%"PRIu16" controllers.", ids_size,
+                     ids->n_controllers * sizeof(ovs_be16), ids->n_controllers);
+        return OFPERR_OFPBAC_BAD_LEN;
+    }
+
+    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;
+    ofpact_update_len(out, &ids->ofpact);
+
+    return 0;
+}
+
+static enum ofperr
+decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code)
+{
+    const struct nx_action_header *nah = (const struct nx_action_header *) a;
+    uint16_t len = ntohs(a->header.len);
+
+    if (len < sizeof(struct nx_action_header)) {
+        return OFPERR_OFPBAC_BAD_LEN;
+    } else if (a->vendor.vendor != CONSTANT_HTONL(NX_VENDOR_ID)) {
+        return OFPERR_OFPBAC_BAD_VENDOR;
+    }
+
+    switch (nah->subtype) {
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)    \
+        case CONSTANT_HTONS(ENUM):                      \
+            if (EXTENSIBLE                              \
+                ? len >= sizeof(struct STRUCT)          \
+                : len == sizeof(struct STRUCT)) {       \
+                *code = OFPUTIL_##ENUM;                 \
+                return 0;                               \
+            } else {                                    \
+                return OFPERR_OFPBAC_BAD_LEN;           \
+            }                                           \
+            NOT_REACHED();
+#include "ofp-util.def"
+
+    case CONSTANT_HTONS(NXAST_SNAT__OBSOLETE):
+    case CONSTANT_HTONS(NXAST_DROP_SPOOFED_ARP__OBSOLETE):
+    default:
+        return OFPERR_OFPBAC_BAD_TYPE;
+    }
+}
+
+/* Parses 'a' to determine its type.  On success stores the correct type into
+ * '*code' and returns 0.  On failure returns an OFPERR_* error code and
+ * '*code' is indeterminate.
+ *
+ * The caller must have already verified that 'a''s length is potentially
+ * correct (that is, a->header.len is nonzero and a multiple of sizeof(union
+ * ofp_action) and no longer than the amount of space allocated to 'a').
+ *
+ * This function verifies that 'a''s length is correct for the type of action
+ * that it represents. */
+static enum ofperr
+decode_openflow10_action(const union ofp_action *a,
+                         enum ofputil_action_code *code)
+{
+    switch (a->type) {
+    case CONSTANT_HTONS(OFPAT10_VENDOR):
+        return decode_nxast_action(a, code);
+
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME)                          \
+        case CONSTANT_HTONS(ENUM):                                  \
+            if (a->header.len == htons(sizeof(struct STRUCT))) {    \
+                *code = OFPUTIL_##ENUM;                             \
+                return 0;                                           \
+            } else {                                                \
+                return OFPERR_OFPBAC_BAD_LEN;                       \
+            }                                                       \
+            break;
+#include "ofp-util.def"
+
+    default:
+        return OFPERR_OFPBAC_BAD_TYPE;
+    }
+}
+
+static enum ofperr
+ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
+                  struct ofpbuf *out)
+{
+    const struct nx_action_resubmit *nar;
+    const struct nx_action_set_tunnel *nast;
+    const struct nx_action_set_queue *nasq;
+    const struct nx_action_note *nan;
+    const struct nx_action_set_tunnel64 *nast64;
+    struct ofpact_tunnel *tunnel;
+    enum ofperr error = 0;
+
+    switch (code) {
+    case OFPUTIL_ACTION_INVALID:
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+        NOT_REACHED();
+
+    case OFPUTIL_NXAST_RESUBMIT:
+        resubmit_from_openflow((const struct nx_action_resubmit *) a, out);
+        break;
+
+    case OFPUTIL_NXAST_SET_TUNNEL:
+        nast = (const struct nx_action_set_tunnel *) a;
+        tunnel = ofpact_put_SET_TUNNEL(out);
+        tunnel->ofpact.compat = code;
+        tunnel->tun_id = ntohl(nast->tun_id);
+        break;
+
+    case OFPUTIL_NXAST_SET_QUEUE:
+        nasq = (const struct nx_action_set_queue *) a;
+        ofpact_put_SET_QUEUE(out)->queue_id = ntohl(nasq->queue_id);
+        break;
+
+    case OFPUTIL_NXAST_POP_QUEUE:
+        ofpact_put_POP_QUEUE(out);
+        break;
+
+    case OFPUTIL_NXAST_REG_MOVE:
+        error = nxm_reg_move_from_openflow(
+            (const struct nx_action_reg_move *) a, out);
+        break;
+
+    case OFPUTIL_NXAST_REG_LOAD:
+        error = nxm_reg_load_from_openflow(
+            (const struct nx_action_reg_load *) a, out);
+        break;
+
+    case OFPUTIL_NXAST_NOTE:
+        nan = (const struct nx_action_note *) a;
+        note_from_openflow(nan, out);
+        break;
+
+    case OFPUTIL_NXAST_SET_TUNNEL64:
+        nast64 = (const struct nx_action_set_tunnel64 *) a;
+        tunnel = ofpact_put_SET_TUNNEL(out);
+        tunnel->ofpact.compat = code;
+        tunnel->tun_id = ntohll(nast64->tun_id);
+        break;
+
+    case OFPUTIL_NXAST_MULTIPATH:
+        error = multipath_from_openflow((const struct nx_action_multipath *) a,
+                                        ofpact_put_MULTIPATH(out));
+        break;
+
+    case OFPUTIL_NXAST_AUTOPATH__DEPRECATED:
+        error = autopath_from_openflow((const struct nx_action_autopath *) a,
+                                       ofpact_put_AUTOPATH(out));
+        break;
+
+    case OFPUTIL_NXAST_BUNDLE:
+    case OFPUTIL_NXAST_BUNDLE_LOAD:
+        error = bundle_from_openflow((const struct nx_action_bundle *) a, out);
+        break;
+
+    case OFPUTIL_NXAST_OUTPUT_REG:
+        error = output_reg_from_openflow(
+            (const struct nx_action_output_reg *) a, out);
+        break;
+
+    case OFPUTIL_NXAST_RESUBMIT_TABLE:
+        nar = (const struct nx_action_resubmit *) a;
+        error = resubmit_table_from_openflow(nar, out);
+        break;
+
+    case OFPUTIL_NXAST_LEARN:
+        error = learn_from_openflow((const struct nx_action_learn *) a, out);
+        break;
+
+    case OFPUTIL_NXAST_EXIT:
+        ofpact_put_EXIT(out);
+        break;
+
+    case OFPUTIL_NXAST_DEC_TTL:
+        error = dec_ttl_from_openflow(out);
+        break;
+
+    case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
+        error = dec_ttl_cnt_ids_from_openflow(
+                    (const struct nx_action_cnt_ids *) a, out);
+        break;
+
+    case OFPUTIL_NXAST_FIN_TIMEOUT:
+        fin_timeout_from_openflow(
+            (const struct nx_action_fin_timeout *) a, out);
+        break;
+
+    case OFPUTIL_NXAST_CONTROLLER:
+        controller_from_openflow((const struct nx_action_controller *) a, out);
+        break;
+    }
+
+    return error;
+}
+
+static enum ofperr
+ofpact_from_openflow10(const union ofp_action *a, struct ofpbuf *out)
+{
+    enum ofputil_action_code code;
+    enum ofperr error;
+
+    error = decode_openflow10_action(a, &code);
+    if (error) {
+        return error;
+    }
+
+    switch (code) {
+    case OFPUTIL_ACTION_INVALID:
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+        NOT_REACHED();
+
+    case OFPUTIL_OFPAT10_OUTPUT:
+        return output_from_openflow10(&a->output10, out);
+
+    case OFPUTIL_OFPAT10_SET_VLAN_VID:
+        if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+        ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+        break;
+
+    case OFPUTIL_OFPAT10_SET_VLAN_PCP:
+        if (a->vlan_pcp.vlan_pcp & ~7) {
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+        ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
+        break;
+
+    case OFPUTIL_OFPAT10_STRIP_VLAN:
+        ofpact_put_STRIP_VLAN(out);
+        break;
+
+    case OFPUTIL_OFPAT10_SET_DL_SRC:
+        memcpy(ofpact_put_SET_ETH_SRC(out)->mac,
+               ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+        break;
+
+    case OFPUTIL_OFPAT10_SET_DL_DST:
+        memcpy(ofpact_put_SET_ETH_DST(out)->mac,
+               ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+        break;
+
+    case OFPUTIL_OFPAT10_SET_NW_SRC:
+        ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr;
+        break;
+
+    case OFPUTIL_OFPAT10_SET_NW_DST:
+        ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr;
+        break;
+
+    case OFPUTIL_OFPAT10_SET_NW_TOS:
+        if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) {
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+        ofpact_put_SET_IPV4_DSCP(out)->dscp = a->nw_tos.nw_tos;
+        break;
+
+    case OFPUTIL_OFPAT10_SET_TP_SRC:
+        ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port);
+        break;
+
+    case OFPUTIL_OFPAT10_SET_TP_DST:
+        ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port);
+
+        break;
+
+    case OFPUTIL_OFPAT10_ENQUEUE:
+        error = enqueue_from_openflow10((const struct ofp_action_enqueue *) a,
+                                        out);
+        break;
+
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+       return ofpact_from_nxast(a, code, out);
+    }
+
+    return error;
+}
+
+static inline union ofp_action *
+action_next(const union ofp_action *a)
+{
+    return ((union ofp_action *) (void *)
+            ((uint8_t *) a + ntohs(a->header.len)));
+}
+
+static inline bool
+action_is_valid(const union ofp_action *a, size_t n_actions)
+{
+    uint16_t len = ntohs(a->header.len);
+    return (!(len % OFP_ACTION_ALIGN)
+            && len >= sizeof *a
+            && len / sizeof *a <= n_actions);
+}
+
+/* This macro is careful to check for actions with bad lengths. */
+#define ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS)                 \
+    for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS);                      \
+         (LEFT) > 0 && action_is_valid(ITER, LEFT);                     \
+         ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
+          (ITER) = action_next(ITER)))
+
+static void
+log_bad_action(const union ofp_action *actions, size_t n_actions, size_t ofs,
+               enum ofperr error)
+{
+    if (!VLOG_DROP_WARN(&rl)) {
+        struct ds s;
+
+        ds_init(&s);
+        ds_put_hex_dump(&s, actions, n_actions * sizeof *actions, 0, false);
+        VLOG_WARN("bad action at offset %#zx (%s):\n%s",
+                  ofs * sizeof *actions, ofperr_get_name(error), ds_cstr(&s));
+        ds_destroy(&s);
+    }
+}
+
+static enum ofperr
+ofpacts_from_openflow(const union ofp_action *in, size_t n_in,
+                      struct ofpbuf *out,
+                      enum ofperr (*ofpact_from_openflow)(
+                          const union ofp_action *a, struct ofpbuf *out))
+{
+    const union ofp_action *a;
+    size_t left;
+
+    ACTION_FOR_EACH (a, left, in, n_in) {
+        enum ofperr error = ofpact_from_openflow(a, out);
+        if (error) {
+            log_bad_action(in, n_in, a - in, error);
+            return error;
+        }
+    }
+    if (left) {
+        enum ofperr error = OFPERR_OFPBAC_BAD_LEN;
+        log_bad_action(in, n_in, n_in - left, error);
+        return error;
+    }
+
+    ofpact_pad(out);
+    return 0;
+}
+
+static enum ofperr
+ofpacts_from_openflow10(const union ofp_action *in, size_t n_in,
+                        struct ofpbuf *out)
+{
+    return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow10);
+}
+
+static enum ofperr
+ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len,
+                     struct ofpbuf *ofpacts,
+                     enum ofperr (*translate)(const union ofp_action *actions,
+                                              size_t n_actions,
+                                              struct ofpbuf *ofpacts))
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    const union ofp_action *actions;
+    enum ofperr error;
+
+    ofpbuf_clear(ofpacts);
+
+    if (actions_len % OFP_ACTION_ALIGN != 0) {
+        VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a "
+                     "multiple of %d", actions_len, OFP_ACTION_ALIGN);
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    actions = ofpbuf_try_pull(openflow, actions_len);
+    if (actions == NULL) {
+        VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds "
+                     "remaining message length (%zu)",
+                     actions_len, openflow->size);
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    error = translate(actions, actions_len / OFP_ACTION_ALIGN, ofpacts);
+    if (error) {
+        ofpbuf_clear(ofpacts);
+    }
+    return error;
+}
+
+/* Attempts to convert 'actions_len' bytes of OpenFlow 1.0 actions from the
+ * front of 'openflow' into ofpacts.  On success, replaces any existing content
+ * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
+ * Returns 0 if successful, otherwise an OpenFlow error.
+ *
+ * The parsed actions are valid generically, but they may not be valid in a
+ * specific context.  For example, port numbers up to OFPP_MAX are valid
+ * generically, but specific datapaths may only support port numbers in a
+ * smaller range.  Use ofpacts_check() to additional check whether actions are
+ * valid in a specific context. */
+enum ofperr
+ofpacts_pull_openflow10(struct ofpbuf *openflow, unsigned int actions_len,
+                        struct ofpbuf *ofpacts)
+{
+    return ofpacts_pull_actions(openflow, actions_len, ofpacts,
+                                ofpacts_from_openflow10);
+}
+\f
+/* OpenFlow 1.1 actions. */
+
+/* Parses 'a' to determine its type.  On success stores the correct type into
+ * '*code' and returns 0.  On failure returns an OFPERR_* error code and
+ * '*code' is indeterminate.
+ *
+ * The caller must have already verified that 'a''s length is potentially
+ * correct (that is, a->header.len is nonzero and a multiple of sizeof(union
+ * ofp_action) and no longer than the amount of space allocated to 'a').
+ *
+ * This function verifies that 'a''s length is correct for the type of action
+ * that it represents. */
+static enum ofperr
+decode_openflow11_action(const union ofp_action *a,
+                         enum ofputil_action_code *code)
+{
+    switch (a->type) {
+    case CONSTANT_HTONS(OFPAT11_EXPERIMENTER):
+        return decode_nxast_action(a, code);
+
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME)                          \
+        case CONSTANT_HTONS(ENUM):                                  \
+            if (a->header.len == htons(sizeof(struct STRUCT))) {    \
+                *code = OFPUTIL_##ENUM;                             \
+                return 0;                                           \
+            } else {                                                \
+                return OFPERR_OFPBAC_BAD_LEN;                       \
+            }                                                       \
+            break;
+#include "ofp-util.def"
+
+    default:
+        return OFPERR_OFPBAC_BAD_TYPE;
+    }
+}
+
+static enum ofperr
+output_from_openflow11(const struct ofp11_action_output *oao,
+                       struct ofpbuf *out)
+{
+    struct ofpact_output *output;
+    enum ofperr error;
+
+    output = ofpact_put_OUTPUT(out);
+    output->max_len = ntohs(oao->max_len);
+
+    error = ofputil_port_from_ofp11(oao->port, &output->port);
+    if (error) {
+        return error;
+    }
+
+    return ofputil_check_output_port(output->port, OFPP_MAX);
+}
+
+static enum ofperr
+ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
+{
+    enum ofputil_action_code code;
+    enum ofperr error;
+
+    error = decode_openflow11_action(a, &code);
+    if (error) {
+        return error;
+    }
+
+    switch (code) {
+    case OFPUTIL_ACTION_INVALID:
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+        NOT_REACHED();
+
+    case OFPUTIL_OFPAT11_OUTPUT:
+        return output_from_openflow11((const struct ofp11_action_output *) a,
+                                      out);
+
+    case OFPUTIL_OFPAT11_SET_VLAN_VID:
+        if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+        ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+        break;
+
+    case OFPUTIL_OFPAT11_SET_VLAN_PCP:
+        if (a->vlan_pcp.vlan_pcp & ~7) {
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+        ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
+        break;
+
+    case OFPUTIL_OFPAT11_SET_DL_SRC:
+        memcpy(ofpact_put_SET_ETH_SRC(out)->mac,
+               ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+        break;
+
+    case OFPUTIL_OFPAT11_SET_DL_DST:
+        memcpy(ofpact_put_SET_ETH_DST(out)->mac,
+               ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+        break;
+
+    case OFPUTIL_OFPAT11_SET_NW_SRC:
+        ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr;
+        break;
+
+    case OFPUTIL_OFPAT11_SET_NW_DST:
+        ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr;
+        break;
+
+    case OFPUTIL_OFPAT11_SET_NW_TOS:
+        if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) {
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+        ofpact_put_SET_IPV4_DSCP(out)->dscp = a->nw_tos.nw_tos;
+        break;
+
+    case OFPUTIL_OFPAT11_SET_TP_SRC:
+        ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port);
+        break;
+
+    case OFPUTIL_OFPAT11_SET_TP_DST:
+        ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port);
+        break;
+
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+        return ofpact_from_nxast(a, code, out);
+    }
+
+    return error;
+}
+
+static enum ofperr
+ofpacts_from_openflow11(const union ofp_action *in, size_t n_in,
+                        struct ofpbuf *out)
+{
+    return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow11);
+}
+\f
+/* OpenFlow 1.1 instructions. */
+
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME)             \
+    static inline const struct STRUCT *                         \
+    instruction_get_##ENUM(const struct ofp11_instruction *inst)\
+    {                                                           \
+        assert(inst->type == htons(ENUM));                      \
+        return (struct STRUCT *)inst;                           \
+    }                                                           \
+                                                                \
+    static inline void                                          \
+    instruction_init_##ENUM(struct STRUCT *s)                   \
+    {                                                           \
+        memset(s, 0, sizeof *s);                                \
+        s->type = htons(ENUM);                                  \
+        s->len = htons(sizeof *s);                              \
+    }                                                           \
+                                                                \
+    static inline struct STRUCT *                               \
+    instruction_put_##ENUM(struct ofpbuf *buf)                  \
+    {                                                           \
+        struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s);   \
+        instruction_init_##ENUM(s);                             \
+        return s;                                               \
+    }
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+
+struct instruction_type_info {
+    enum ovs_instruction_type type;
+    const char *name;
+};
+
+static const struct instruction_type_info inst_info[] = {
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME)    {OVSINST_##ENUM, NAME},
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+};
+
+const char *
+ofpact_instruction_name_from_type(enum ovs_instruction_type type)
+{
+    return inst_info[type].name;
+}
+
+int
+ofpact_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++) {
+        if (!strcasecmp(name, p->name)) {
+            return p->type;
+        }
+    }
+    return -1;
+}
+
+static inline struct ofp11_instruction *
+instruction_next(const struct ofp11_instruction *inst)
+{
+    return ((struct ofp11_instruction *) (void *)
+            ((uint8_t *) inst + ntohs(inst->len)));
+}
+
+static inline bool
+instruction_is_valid(const struct ofp11_instruction *inst,
+                     size_t n_instructions)
+{
+    uint16_t len = ntohs(inst->len);
+    return (!(len % OFP11_INSTRUCTION_ALIGN)
+            && len >= sizeof *inst
+            && len / sizeof *inst <= n_instructions);
+}
+
+/* This macro is careful to check for instructions with bad lengths. */
+#define INSTRUCTION_FOR_EACH(ITER, LEFT, INSTRUCTIONS, N_INSTRUCTIONS)  \
+    for ((ITER) = (INSTRUCTIONS), (LEFT) = (N_INSTRUCTIONS);            \
+         (LEFT) > 0 && instruction_is_valid(ITER, LEFT);                \
+         ((LEFT) -= (ntohs((ITER)->len)                                 \
+                     / sizeof(struct ofp11_instruction)),               \
+          (ITER) = instruction_next(ITER)))
+
+static enum ofperr
+decode_openflow11_instruction(const struct ofp11_instruction *inst,
+                              enum ovs_instruction_type *type)
+{
+    uint16_t len = ntohs(inst->len);
+
+    switch (inst->type) {
+    case CONSTANT_HTONS(OFPIT11_EXPERIMENTER):
+        return OFPERR_OFPBIC_BAD_EXPERIMENTER;
+
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME)     \
+        case CONSTANT_HTONS(ENUM):                      \
+            if (EXTENSIBLE                              \
+                ? len >= sizeof(struct STRUCT)          \
+                : len == sizeof(struct STRUCT)) {       \
+                *type = OVSINST_##ENUM;                 \
+                return 0;                               \
+            } else {                                    \
+                return OFPERR_OFPBIC_BAD_LEN;           \
+            }
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+
+    default:
+        return OFPERR_OFPBIC_UNKNOWN_INST;
+    }
+}
+
+static enum ofperr
+decode_openflow11_instructions(const struct ofp11_instruction insts[],
+                               size_t n_insts,
+                               const struct ofp11_instruction *out[])
+{
+    const struct ofp11_instruction *inst;
+    size_t left;
+
+    memset(out, 0, N_OVS_INSTRUCTIONS * sizeof *out);
+    INSTRUCTION_FOR_EACH (inst, left, insts, n_insts) {
+        enum ovs_instruction_type type;
+        enum ofperr error;
+
+        error = decode_openflow11_instruction(inst, &type);
+        if (error) {
+            return error;
+        }
+
+        if (out[type]) {
+            return OFPERR_NXBIC_DUP_TYPE;
+        }
+        out[type] = inst;
+    }
+
+    if (left) {
+        VLOG_WARN_RL(&rl, "bad instruction format at offset %zu",
+                     (n_insts - left) * sizeof *inst);
+        return OFPERR_OFPBIC_BAD_LEN;
+    }
+    return 0;
+}
+
+static void
+get_actions_from_instruction(const struct ofp11_instruction *inst,
+                         const union ofp_action **actions,
+                         size_t *n_actions)
+{
+    *actions = (const union ofp_action *) (inst + 1);
+    *n_actions = (ntohs(inst->len) - sizeof *inst) / OFP11_INSTRUCTION_ALIGN;
+}
+
+/* Attempts to convert 'actions_len' bytes of OpenFlow 1.1 actions from the
+ * front of 'openflow' into ofpacts.  On success, replaces any existing content
+ * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
+ * Returns 0 if successful, otherwise an OpenFlow error.
+ *
+ * In most places in OpenFlow 1.1 and 1.2, actions appear encapsulated in
+ * instructions, so you should call ofpacts_pull_openflow11_instructions()
+ * instead of this function.
+ *
+ * The parsed actions are valid generically, but they may not be valid in a
+ * specific context.  For example, port numbers up to OFPP_MAX are valid
+ * generically, but specific datapaths may only support port numbers in a
+ * smaller range.  Use ofpacts_check() to additional check whether actions are
+ * valid in a specific context. */
+enum ofperr
+ofpacts_pull_openflow11_actions(struct ofpbuf *openflow,
+                                unsigned int actions_len,
+                                struct ofpbuf *ofpacts)
+{
+    return ofpacts_pull_actions(openflow, actions_len, ofpacts,
+                                ofpacts_from_openflow11);
+}
+
+enum ofperr
+ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
+                                     unsigned int instructions_len,
+                                     struct ofpbuf *ofpacts)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    const struct ofp11_instruction *instructions;
+    const struct ofp11_instruction *insts[N_OVS_INSTRUCTIONS];
+    enum ofperr error;
+
+    ofpbuf_clear(ofpacts);
+
+    if (instructions_len % OFP11_INSTRUCTION_ALIGN != 0) {
+        VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u is not a "
+                     "multiple of %d",
+                     instructions_len, OFP11_INSTRUCTION_ALIGN);
+        error = OFPERR_OFPBIC_BAD_LEN;
+        goto exit;
+    }
+
+    instructions = ofpbuf_try_pull(openflow, instructions_len);
+    if (instructions == NULL) {
+        VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u exceeds "
+                     "remaining message length (%zu)",
+                     instructions_len, openflow->size);
+        error = OFPERR_OFPBIC_BAD_LEN;
+        goto exit;
+    }
+
+    error = decode_openflow11_instructions(
+        instructions, instructions_len / OFP11_INSTRUCTION_ALIGN,
+        insts);
+    if (error) {
+        goto exit;
+    }
+
+    if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) {
+        const union ofp_action *actions;
+        size_t n_actions;
+
+        get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS],
+                                     &actions, &n_actions);
+        error = ofpacts_from_openflow11(actions, n_actions, ofpacts);
+        if (error) {
+            goto exit;
+        }
+    }
+
+    if (insts[OVSINST_OFPIT11_GOTO_TABLE] ||
+        insts[OVSINST_OFPIT11_WRITE_METADATA] ||
+        insts[OVSINST_OFPIT11_WRITE_ACTIONS] ||
+        insts[OVSINST_OFPIT11_CLEAR_ACTIONS]) {
+        error = OFPERR_OFPBIC_UNSUP_INST;
+        goto exit;
+    }
+
+exit:
+    if (error) {
+        ofpbuf_clear(ofpacts);
+    }
+    return error;
+}
+\f
+static enum ofperr
+ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports)
+{
+    const struct ofpact_enqueue *enqueue;
+
+    switch (a->type) {
+    case OFPACT_OUTPUT:
+        return ofputil_check_output_port(ofpact_get_OUTPUT(a)->port,
+                                         max_ports);
+
+    case OFPACT_CONTROLLER:
+        return 0;
+
+    case OFPACT_ENQUEUE:
+        enqueue = ofpact_get_ENQUEUE(a);
+        if (enqueue->port >= max_ports && enqueue->port != OFPP_IN_PORT
+            && enqueue->port != OFPP_LOCAL) {
+            return OFPERR_OFPBAC_BAD_OUT_PORT;
+        }
+        return 0;
+
+    case OFPACT_OUTPUT_REG:
+        return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow);
+
+    case OFPACT_BUNDLE:
+        return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow);
+
+    case OFPACT_SET_VLAN_VID:
+    case OFPACT_SET_VLAN_PCP:
+    case OFPACT_STRIP_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:
+        return 0;
+
+    case OFPACT_REG_MOVE:
+        return nxm_reg_move_check(ofpact_get_REG_MOVE(a), flow);
+
+    case OFPACT_REG_LOAD:
+        return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow);
+
+    case OFPACT_DEC_TTL:
+    case OFPACT_SET_TUNNEL:
+    case OFPACT_SET_QUEUE:
+    case OFPACT_POP_QUEUE:
+    case OFPACT_FIN_TIMEOUT:
+    case OFPACT_RESUBMIT:
+        return 0;
+
+    case OFPACT_LEARN:
+        return learn_check(ofpact_get_LEARN(a), flow);
+
+    case OFPACT_MULTIPATH:
+        return multipath_check(ofpact_get_MULTIPATH(a), flow);
+
+    case OFPACT_AUTOPATH:
+        return autopath_check(ofpact_get_AUTOPATH(a), flow);
+
+    case OFPACT_NOTE:
+    case OFPACT_EXIT:
+        return 0;
+
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* 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. */
+enum ofperr
+ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len,
+              const struct flow *flow, int max_ports)
+{
+    const struct ofpact *a;
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        enum ofperr error = ofpact_check__(a, flow, max_ports);
+        if (error) {
+            return error;
+        }
+    }
+
+    return 0;
+}
+\f
+/* Converting ofpacts to Nicira OpenFlow extensions. */
+
+static void
+ofpact_output_reg_to_nxast(const struct ofpact_output_reg *output_reg,
+                                struct ofpbuf *out)
+{
+    struct nx_action_output_reg *naor = ofputil_put_NXAST_OUTPUT_REG(out);
+
+    naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs,
+                                           output_reg->src.n_bits);
+    naor->src = htonl(output_reg->src.field->nxm_header);
+    naor->max_len = htons(output_reg->max_len);
+}
+
+static void
+ofpact_resubmit_to_nxast(const struct ofpact_resubmit *resubmit,
+                         struct ofpbuf *out)
+{
+    struct nx_action_resubmit *nar;
+
+    if (resubmit->table_id == 0xff
+        && resubmit->ofpact.compat != OFPUTIL_NXAST_RESUBMIT_TABLE) {
+        nar = ofputil_put_NXAST_RESUBMIT(out);
+    } else {
+        nar = ofputil_put_NXAST_RESUBMIT_TABLE(out);
+        nar->table = resubmit->table_id;
+    }
+    nar->in_port = htons(resubmit->in_port);
+}
+
+static void
+ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel,
+                           struct ofpbuf *out)
+{
+    uint64_t tun_id = tunnel->tun_id;
+
+    if (tun_id <= UINT32_MAX
+        && tunnel->ofpact.compat != OFPUTIL_NXAST_SET_TUNNEL64) {
+        ofputil_put_NXAST_SET_TUNNEL(out)->tun_id = htonl(tun_id);
+    } else {
+        ofputil_put_NXAST_SET_TUNNEL64(out)->tun_id = htonll(tun_id);
+    }
+}
+
+static void
+ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out)
+{
+    size_t start_ofs = out->size;
+    struct nx_action_note *nan;
+    unsigned int remainder;
+    unsigned int len;
+
+    nan = ofputil_put_NXAST_NOTE(out);
+    out->size -= sizeof nan->note;
+
+    ofpbuf_put(out, note->data, note->length);
+
+    len = out->size - start_ofs;
+    remainder = len % OFP_ACTION_ALIGN;
+    if (remainder) {
+        ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder);
+    }
+    nan = (struct nx_action_note *)((char *)out->data + start_ofs);
+    nan->len = htons(out->size - start_ofs);
+}
+
+static void
+ofpact_controller_to_nxast(const struct ofpact_controller *oc,
+                           struct ofpbuf *out)
+{
+    struct nx_action_controller *nac;
+
+    nac = ofputil_put_NXAST_CONTROLLER(out);
+    nac->max_len = htons(oc->max_len);
+    nac->controller_id = htons(oc->controller_id);
+    nac->reason = oc->reason;
+}
+
+static void
+ofpact_dec_ttl_to_nxast(const struct ofpact_cnt_ids *oc_ids,
+                        struct ofpbuf *out)
+{
+    if (oc_ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL) {
+        ofputil_put_NXAST_DEC_TTL(out);
+    } else {
+        struct nx_action_cnt_ids *nac_ids =
+            ofputil_put_NXAST_DEC_TTL_CNT_IDS(out);
+        int ids_len = ROUND_UP(2 * oc_ids->n_controllers, OFP_ACTION_ALIGN);
+        ovs_be16 *ids;
+        size_t i;
+
+        nac_ids->len = htons(ntohs(nac_ids->len) + ids_len);
+        nac_ids->n_controllers = htons(oc_ids->n_controllers);
+
+        ids = ofpbuf_put_zeros(out, ids_len);
+        for (i = 0; i < oc_ids->n_controllers; i++) {
+            ids[i] = htons(oc_ids->cnt_ids[i]);
+        }
+    }
+}
+
+static void
+ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout,
+                            struct ofpbuf *out)
+{
+    struct nx_action_fin_timeout *naft = ofputil_put_NXAST_FIN_TIMEOUT(out);
+    naft->fin_idle_timeout = htons(fin_timeout->fin_idle_timeout);
+    naft->fin_hard_timeout = htons(fin_timeout->fin_hard_timeout);
+}
+
+static void
+ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
+{
+    switch (a->type) {
+    case OFPACT_CONTROLLER:
+        ofpact_controller_to_nxast(ofpact_get_CONTROLLER(a), out);
+        break;
+
+    case OFPACT_OUTPUT_REG:
+        ofpact_output_reg_to_nxast(ofpact_get_OUTPUT_REG(a), out);
+        break;
+
+    case OFPACT_BUNDLE:
+        bundle_to_nxast(ofpact_get_BUNDLE(a), out);
+        break;
+
+    case OFPACT_REG_MOVE:
+        nxm_reg_move_to_nxast(ofpact_get_REG_MOVE(a), out);
+        break;
+
+    case OFPACT_REG_LOAD:
+        nxm_reg_load_to_nxast(ofpact_get_REG_LOAD(a), out);
+        break;
+
+    case OFPACT_DEC_TTL:
+        ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out);
+        break;
+
+    case OFPACT_SET_TUNNEL:
+        ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out);
+        break;
+
+    case OFPACT_SET_QUEUE:
+        ofputil_put_NXAST_SET_QUEUE(out)->queue_id
+            = htonl(ofpact_get_SET_QUEUE(a)->queue_id);
+        break;
+
+    case OFPACT_POP_QUEUE:
+        ofputil_put_NXAST_POP_QUEUE(out);
+        break;
+
+    case OFPACT_FIN_TIMEOUT:
+        ofpact_fin_timeout_to_nxast(ofpact_get_FIN_TIMEOUT(a), out);
+        break;
+
+    case OFPACT_RESUBMIT:
+        ofpact_resubmit_to_nxast(ofpact_get_RESUBMIT(a), out);
+        break;
+
+    case OFPACT_LEARN:
+        learn_to_nxast(ofpact_get_LEARN(a), out);
+        break;
+
+    case OFPACT_MULTIPATH:
+        multipath_to_nxast(ofpact_get_MULTIPATH(a), out);
+        break;
+
+    case OFPACT_AUTOPATH:
+        autopath_to_nxast(ofpact_get_AUTOPATH(a), out);
+        break;
+
+    case OFPACT_NOTE:
+        ofpact_note_to_nxast(ofpact_get_NOTE(a), out);
+        break;
+
+    case OFPACT_EXIT:
+        ofputil_put_NXAST_EXIT(out);
+        break;
+
+    case OFPACT_OUTPUT:
+    case OFPACT_ENQUEUE:
+    case OFPACT_SET_VLAN_VID:
+    case OFPACT_SET_VLAN_PCP:
+    case OFPACT_STRIP_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:
+        NOT_REACHED();
+    }
+}
+\f
+/* Converting ofpacts to OpenFlow 1.0. */
+
+static void
+ofpact_output_to_openflow10(const struct ofpact_output *output,
+                            struct ofpbuf *out)
+{
+    struct ofp10_action_output *oao;
+
+    oao = ofputil_put_OFPAT10_OUTPUT(out);
+    oao->port = htons(output->port);
+    oao->max_len = htons(output->max_len);
+}
+
+static void
+ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue,
+                             struct ofpbuf *out)
+{
+    struct ofp_action_enqueue *oae;
+
+    oae = ofputil_put_OFPAT10_ENQUEUE(out);
+    oae->port = htons(enqueue->port);
+    oae->queue_id = htonl(enqueue->queue);
+}
+
+static void
+ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
+{
+    switch (a->type) {
+    case OFPACT_OUTPUT:
+        ofpact_output_to_openflow10(ofpact_get_OUTPUT(a), out);
+        break;
+
+    case OFPACT_ENQUEUE:
+        ofpact_enqueue_to_openflow10(ofpact_get_ENQUEUE(a), out);
+        break;
+
+    case OFPACT_SET_VLAN_VID:
+        ofputil_put_OFPAT10_SET_VLAN_VID(out)->vlan_vid
+            = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid);
+        break;
+
+    case OFPACT_SET_VLAN_PCP:
+        ofputil_put_OFPAT10_SET_VLAN_PCP(out)->vlan_pcp
+            = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp;
+        break;
+
+    case OFPACT_STRIP_VLAN:
+        ofputil_put_OFPAT10_STRIP_VLAN(out);
+        break;
+
+    case OFPACT_SET_ETH_SRC:
+        memcpy(ofputil_put_OFPAT10_SET_DL_SRC(out)->dl_addr,
+               ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
+        break;
+
+    case OFPACT_SET_ETH_DST:
+        memcpy(ofputil_put_OFPAT10_SET_DL_DST(out)->dl_addr,
+               ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN);
+        break;
+
+    case OFPACT_SET_IPV4_SRC:
+        ofputil_put_OFPAT10_SET_NW_SRC(out)->nw_addr
+            = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+        break;
+
+    case OFPACT_SET_IPV4_DST:
+        ofputil_put_OFPAT10_SET_NW_DST(out)->nw_addr
+            = ofpact_get_SET_IPV4_DST(a)->ipv4;
+        break;
+
+    case OFPACT_SET_IPV4_DSCP:
+        ofputil_put_OFPAT10_SET_NW_TOS(out)->nw_tos
+            = ofpact_get_SET_IPV4_DSCP(a)->dscp;
+        break;
+
+    case OFPACT_SET_L4_SRC_PORT:
+        ofputil_put_OFPAT10_SET_TP_SRC(out)->tp_port
+            = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
+        break;
+
+    case OFPACT_SET_L4_DST_PORT:
+        ofputil_put_OFPAT10_SET_TP_DST(out)->tp_port
+            = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
+        break;
+
+    case OFPACT_CONTROLLER:
+    case OFPACT_OUTPUT_REG:
+    case OFPACT_BUNDLE:
+    case OFPACT_REG_MOVE:
+    case OFPACT_REG_LOAD:
+    case OFPACT_DEC_TTL:
+    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_AUTOPATH:
+    case OFPACT_NOTE:
+    case OFPACT_EXIT:
+        ofpact_to_nxast(a, out);
+        break;
+    }
+}
+
+/* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow 1.0
+ * actions in 'openflow', appending the actions to any existing data in
+ * 'openflow'. */
+void
+ofpacts_put_openflow10(const struct ofpact ofpacts[], size_t ofpacts_len,
+                       struct ofpbuf *openflow)
+{
+    const struct ofpact *a;
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        ofpact_to_openflow10(a, openflow);
+    }
+}
+\f
+/* Converting ofpacts to OpenFlow 1.1. */
+
+static void
+ofpact_output_to_openflow11(const struct ofpact_output *output,
+                            struct ofpbuf *out)
+{
+    struct ofp11_action_output *oao;
+
+    oao = ofputil_put_OFPAT11_OUTPUT(out);
+    oao->port = ofputil_port_to_ofp11(output->port);
+    oao->max_len = htons(output->max_len);
+}
+
+static void
+ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
+{
+    switch (a->type) {
+    case OFPACT_OUTPUT:
+        return ofpact_output_to_openflow11(ofpact_get_OUTPUT(a), out);
+
+    case OFPACT_ENQUEUE:
+        /* XXX */
+        break;
+
+    case OFPACT_SET_VLAN_VID:
+        ofputil_put_OFPAT11_SET_VLAN_VID(out)->vlan_vid
+            = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid);
+        break;
+
+    case OFPACT_SET_VLAN_PCP:
+        ofputil_put_OFPAT11_SET_VLAN_PCP(out)->vlan_pcp
+            = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp;
+        break;
+
+    case OFPACT_STRIP_VLAN:
+        /* XXX */
+        break;
+
+    case OFPACT_SET_ETH_SRC:
+        memcpy(ofputil_put_OFPAT11_SET_DL_SRC(out)->dl_addr,
+               ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
+        break;
+
+    case OFPACT_SET_ETH_DST:
+        memcpy(ofputil_put_OFPAT11_SET_DL_DST(out)->dl_addr,
+               ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN);
+        break;
+
+    case OFPACT_SET_IPV4_SRC:
+        ofputil_put_OFPAT11_SET_NW_SRC(out)->nw_addr
+            = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+        break;
+
+    case OFPACT_SET_IPV4_DST:
+        ofputil_put_OFPAT11_SET_NW_DST(out)->nw_addr
+            = ofpact_get_SET_IPV4_DST(a)->ipv4;
+        break;
+
+    case OFPACT_SET_IPV4_DSCP:
+        ofputil_put_OFPAT11_SET_NW_TOS(out)->nw_tos
+            = ofpact_get_SET_IPV4_DSCP(a)->dscp;
+        break;
+
+    case OFPACT_SET_L4_SRC_PORT:
+        ofputil_put_OFPAT11_SET_TP_SRC(out)->tp_port
+            = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
+        break;
+
+    case OFPACT_SET_L4_DST_PORT:
+        ofputil_put_OFPAT11_SET_TP_DST(out)->tp_port
+            = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
+        break;
+
+    case OFPACT_CONTROLLER:
+    case OFPACT_OUTPUT_REG:
+    case OFPACT_BUNDLE:
+    case OFPACT_REG_MOVE:
+    case OFPACT_REG_LOAD:
+    case OFPACT_DEC_TTL:
+    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_AUTOPATH:
+    case OFPACT_NOTE:
+    case OFPACT_EXIT:
+        ofpact_to_nxast(a, out);
+        break;
+    }
+}
+
+/* Converts the ofpacts in 'ofpacts' (terminated by OFPACT_END) into OpenFlow
+ * 1.1 actions in 'openflow', appending the actions to any existing data in
+ * 'openflow'. */
+size_t
+ofpacts_put_openflow11_actions(const struct ofpact ofpacts[],
+                               size_t ofpacts_len, struct ofpbuf *openflow)
+{
+    const struct ofpact *a;
+    size_t start_size = openflow->size;
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        ofpact_to_openflow11(a, openflow);
+    }
+
+    return openflow->size - start_size;
+}
+
+void
+ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
+                                    size_t ofpacts_len,
+                                    struct ofpbuf *openflow)
+{
+    struct ofp11_instruction_actions *oia;
+    size_t ofs;
+
+    /* Put an OFPIT11_APPLY_ACTIONS instruction and fill it in. */
+    ofs = openflow->size;
+    instruction_put_OFPIT11_APPLY_ACTIONS(openflow);
+    ofpacts_put_openflow11_actions(ofpacts, ofpacts_len, openflow);
+
+    /* Update the instruction's length (or, if it's empty, delete it). */
+    oia = ofpbuf_at_assert(openflow, ofs, sizeof *oia);
+    if (openflow->size > ofs + sizeof *oia) {
+        oia->len = htons(openflow->size - ofs);
+    } else {
+        openflow->size = ofs;
+    }
+}
+\f
+/* Returns true if 'action' outputs to 'port', false otherwise. */
+static bool
+ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
+{
+    switch (ofpact->type) {
+    case OFPACT_OUTPUT:
+        return ofpact_get_OUTPUT(ofpact)->port == port;
+    case OFPACT_ENQUEUE:
+        return ofpact_get_ENQUEUE(ofpact)->port == port;
+    case OFPACT_CONTROLLER:
+        return port == OFPP_CONTROLLER;
+
+    case OFPACT_OUTPUT_REG:
+    case OFPACT_BUNDLE:
+    case OFPACT_SET_VLAN_VID:
+    case OFPACT_SET_VLAN_PCP:
+    case OFPACT_STRIP_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_DEC_TTL:
+    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_AUTOPATH:
+    case OFPACT_NOTE:
+    case OFPACT_EXIT:
+    default:
+        return false;
+    }
+}
+
+/* Returns true if any action in the 'ofpacts_len' bytes of 'ofpacts' outputs
+ * to 'port', false otherwise. */
+bool
+ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len,
+                       uint16_t port)
+{
+    const struct ofpact *a;
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        if (ofpact_outputs_to_port(a, port)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool
+ofpacts_equal(const struct ofpact *a, size_t a_len,
+              const struct ofpact *b, size_t b_len)
+{
+    return a_len == b_len && !memcmp(a, b, a_len);
+}
+\f
+/* Formatting ofpacts. */
+
+static void
+print_note(const struct ofpact_note *note, struct ds *string)
+{
+    size_t i;
+
+    ds_put_cstr(string, "note:");
+    for (i = 0; i < note->length; i++) {
+        if (i) {
+            ds_put_char(string, '.');
+        }
+        ds_put_format(string, "%02"PRIx8, note->data[i]);
+    }
+}
+
+static void
+print_dec_ttl(const struct ofpact_cnt_ids *ids,
+              struct ds *s)
+{
+    size_t i;
+
+    ds_put_cstr(s, "dec_ttl");
+    if (ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL_CNT_IDS) {
+        ds_put_cstr(s, "(");
+        for (i = 0; i < ids->n_controllers; i++) {
+            if (i) {
+                ds_put_cstr(s, ",");
+            }
+            ds_put_format(s, "%"PRIu16, ids->cnt_ids[i]);
+        }
+        ds_put_cstr(s, ")");
+    }
+}
+
+static void
+print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout,
+                  struct ds *s)
+{
+    ds_put_cstr(s, "fin_timeout(");
+    if (fin_timeout->fin_idle_timeout) {
+        ds_put_format(s, "idle_timeout=%"PRIu16",",
+                      fin_timeout->fin_idle_timeout);
+    }
+    if (fin_timeout->fin_hard_timeout) {
+        ds_put_format(s, "hard_timeout=%"PRIu16",",
+                      fin_timeout->fin_hard_timeout);
+    }
+    ds_chomp(s, ',');
+    ds_put_char(s, ')');
+}
+
+static void
+ofpact_format(const struct ofpact *a, struct ds *s)
+{
+    const struct ofpact_enqueue *enqueue;
+    const struct ofpact_resubmit *resubmit;
+    const struct ofpact_autopath *autopath;
+    const struct ofpact_controller *controller;
+    const struct ofpact_tunnel *tunnel;
+    uint16_t port;
+
+    switch (a->type) {
+    case OFPACT_OUTPUT:
+        port = ofpact_get_OUTPUT(a)->port;
+        if (port < OFPP_MAX) {
+            ds_put_format(s, "output:%"PRIu16, port);
+        } else {
+            ofputil_format_port(port, s);
+            if (port == OFPP_CONTROLLER) {
+                ds_put_format(s, ":%"PRIu16, ofpact_get_OUTPUT(a)->max_len);
+            }
+        }
+        break;
+
+    case OFPACT_CONTROLLER:
+        controller = ofpact_get_CONTROLLER(a);
+        if (controller->reason == OFPR_ACTION &&
+            controller->controller_id == 0) {
+            ds_put_format(s, "CONTROLLER:%"PRIu16,
+                          ofpact_get_CONTROLLER(a)->max_len);
+        } else {
+            enum ofp_packet_in_reason reason = controller->reason;
+
+            ds_put_cstr(s, "controller(");
+            if (reason != OFPR_ACTION) {
+                ds_put_format(s, "reason=%s,",
+                              ofputil_packet_in_reason_to_string(reason));
+            }
+            if (controller->max_len != UINT16_MAX) {
+                ds_put_format(s, "max_len=%"PRIu16",", controller->max_len);
+            }
+            if (controller->controller_id != 0) {
+                ds_put_format(s, "id=%"PRIu16",", controller->controller_id);
+            }
+            ds_chomp(s, ',');
+            ds_put_char(s, ')');
+        }
+        break;
+
+    case OFPACT_ENQUEUE:
+        enqueue = ofpact_get_ENQUEUE(a);
+        ds_put_format(s, "enqueue:");
+        ofputil_format_port(enqueue->port, s);
+        ds_put_format(s, "q%"PRIu32, enqueue->queue);
+        break;
+
+    case OFPACT_OUTPUT_REG:
+        ds_put_cstr(s, "output:");
+        mf_format_subfield(&ofpact_get_OUTPUT_REG(a)->src, s);
+        break;
+
+    case OFPACT_BUNDLE:
+        bundle_format(ofpact_get_BUNDLE(a), s);
+        break;
+
+    case OFPACT_SET_VLAN_VID:
+        ds_put_format(s, "mod_vlan_vid:%"PRIu16,
+                      ofpact_get_SET_VLAN_VID(a)->vlan_vid);
+        break;
+
+    case OFPACT_SET_VLAN_PCP:
+        ds_put_format(s, "mod_vlan_pcp:%"PRIu8,
+                      ofpact_get_SET_VLAN_PCP(a)->vlan_pcp);
+        break;
+
+    case OFPACT_STRIP_VLAN:
+        ds_put_cstr(s, "strip_vlan");
+        break;
+
+    case OFPACT_SET_ETH_SRC:
+        ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
+                      ETH_ADDR_ARGS(ofpact_get_SET_ETH_SRC(a)->mac));
+        break;
+
+    case OFPACT_SET_ETH_DST:
+        ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT,
+                      ETH_ADDR_ARGS(ofpact_get_SET_ETH_DST(a)->mac));
+        break;
+
+    case OFPACT_SET_IPV4_SRC:
+        ds_put_format(s, "mod_nw_src:"IP_FMT,
+                      IP_ARGS(&ofpact_get_SET_IPV4_SRC(a)->ipv4));
+        break;
+
+    case OFPACT_SET_IPV4_DST:
+        ds_put_format(s, "mod_nw_dst:"IP_FMT,
+                      IP_ARGS(&ofpact_get_SET_IPV4_DST(a)->ipv4));
+        break;
+
+    case OFPACT_SET_IPV4_DSCP:
+        ds_put_format(s, "mod_nw_tos:%d", ofpact_get_SET_IPV4_DSCP(a)->dscp);
+        break;
+
+    case OFPACT_SET_L4_SRC_PORT:
+        ds_put_format(s, "mod_tp_src:%d", ofpact_get_SET_L4_SRC_PORT(a)->port);
+        break;
+
+    case OFPACT_SET_L4_DST_PORT:
+        ds_put_format(s, "mod_tp_dst:%d", ofpact_get_SET_L4_DST_PORT(a)->port);
+        break;
+
+    case OFPACT_REG_MOVE:
+        nxm_format_reg_move(ofpact_get_REG_MOVE(a), s);
+        break;
+
+    case OFPACT_REG_LOAD:
+        nxm_format_reg_load(ofpact_get_REG_LOAD(a), s);
+        break;
+
+    case OFPACT_DEC_TTL:
+        print_dec_ttl(ofpact_get_DEC_TTL(a), s);
+        break;
+
+    case OFPACT_SET_TUNNEL:
+        tunnel = ofpact_get_SET_TUNNEL(a);
+        ds_put_format(s, "set_tunnel%s:%#"PRIx64,
+                      (tunnel->tun_id > UINT32_MAX
+                       || a->compat == OFPUTIL_NXAST_SET_TUNNEL64 ? "64" : ""),
+                      tunnel->tun_id);
+        break;
+
+    case OFPACT_SET_QUEUE:
+        ds_put_format(s, "set_queue:%"PRIu32,
+                      ofpact_get_SET_QUEUE(a)->queue_id);
+        break;
+
+    case OFPACT_POP_QUEUE:
+        ds_put_cstr(s, "pop_queue");
+        break;
+
+    case OFPACT_FIN_TIMEOUT:
+        print_fin_timeout(ofpact_get_FIN_TIMEOUT(a), s);
+        break;
+
+    case OFPACT_RESUBMIT:
+        resubmit = ofpact_get_RESUBMIT(a);
+        if (resubmit->in_port != OFPP_IN_PORT && resubmit->table_id == 255) {
+            ds_put_format(s, "resubmit:%"PRIu16, resubmit->in_port);
+        } else {
+            ds_put_format(s, "resubmit(");
+            if (resubmit->in_port != OFPP_IN_PORT) {
+                ofputil_format_port(resubmit->in_port, s);
+            }
+            ds_put_char(s, ',');
+            if (resubmit->table_id != 255) {
+                ds_put_format(s, "%"PRIu8, resubmit->table_id);
+            }
+            ds_put_char(s, ')');
+        }
+        break;
+
+    case OFPACT_LEARN:
+        learn_format(ofpact_get_LEARN(a), s);
+        break;
+
+    case OFPACT_MULTIPATH:
+        multipath_format(ofpact_get_MULTIPATH(a), s);
+        break;
+
+    case OFPACT_AUTOPATH:
+        autopath = ofpact_get_AUTOPATH(a);
+        ds_put_format(s, "autopath(%u,", autopath->port);
+        mf_format_subfield(&autopath->dst, s);
+        ds_put_char(s, ')');
+        break;
+
+    case OFPACT_NOTE:
+        print_note(ofpact_get_NOTE(a), s);
+        break;
+
+    case OFPACT_EXIT:
+        ds_put_cstr(s, "exit");
+        break;
+    }
+}
+
+/* Appends a string representing the 'ofpacts_len' bytes of ofpacts in
+ * 'ofpacts' to 'string'. */
+void
+ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
+               struct ds *string)
+{
+    ds_put_cstr(string, "actions=");
+    if (!ofpacts_len) {
+        ds_put_cstr(string, "drop");
+    } else {
+        const struct ofpact *a;
+
+        OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+            if (a != ofpacts) {
+                ds_put_cstr(string, ",");
+            }
+            ofpact_format(a, string);
+        }
+    }
+}
+\f
+/* Internal use by helpers. */
+
+void *
+ofpact_put(struct ofpbuf *ofpacts, enum ofpact_type type, size_t len)
+{
+    struct ofpact *ofpact;
+
+    ofpact_pad(ofpacts);
+    ofpact = ofpacts->l2 = ofpbuf_put_uninit(ofpacts, len);
+    ofpact_init(ofpact, type, len);
+    return ofpact;
+}
+
+void
+ofpact_init(struct ofpact *ofpact, enum ofpact_type type, size_t len)
+{
+    memset(ofpact, 0, len);
+    ofpact->type = type;
+    ofpact->compat = OFPUTIL_ACTION_INVALID;
+    ofpact->len = len;
+}
+\f
+/* Updates 'ofpact->len' to the number of bytes in the tail of 'ofpacts'
+ * starting at 'ofpact'.
+ *
+ * This is the correct way to update a variable-length ofpact's length after
+ * adding the variable-length part of the payload.  (See the large comment
+ * near the end of ofp-actions.h for more information.) */
+void
+ofpact_update_len(struct ofpbuf *ofpacts, struct ofpact *ofpact)
+{
+    assert(ofpact == ofpacts->l2);
+    ofpact->len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact;
+}
+
+/* Pads out 'ofpacts' to a multiple of OFPACT_ALIGNTO bytes in length.  Each
+ * ofpact_put_<ENUM>() calls this function automatically beforehand, but the
+ * client must call this itself after adding the final ofpact to an array of
+ * them.
+ *
+ * (The consequences of failing to call this function are probably not dire.
+ * OFPACT_FOR_EACH will calculate a pointer beyond the end of the ofpacts, but
+ * not dereference it.  That's undefined behavior, technically, but it will not
+ * cause a real problem on common systems.  Still, it seems better to call
+ * it.) */
+void
+ofpact_pad(struct ofpbuf *ofpacts)
+{
+    unsigned int rem = ofpacts->size % OFPACT_ALIGNTO;
+    if (rem) {
+        ofpbuf_put_zeros(ofpacts, OFPACT_ALIGNTO - rem);
+    }
+}
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
new file mode 100644 (file)
index 0000000..4fc9094
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFP_ACTIONS_H
+#define OFP_ACTIONS_H 1
+
+#include <stdint.h>
+#include "meta-flow.h"
+#include "ofp-errors.h"
+#include "ofp-util.h"
+#include "openflow/openflow.h"
+#include "openflow/nicira-ext.h"
+#include "openvswitch/types.h"
+
+/* List of OVS abstracted actions.
+ *
+ * This macro is used directly only internally by this header, but the list is
+ * still of interest to developers.
+ *
+ * Each DEFINE_OFPACT invocation has the following parameters:
+ *
+ * 1. <ENUM>, used below in the enum definition of OFPACT_<ENUM>, and
+ *    elsewhere.
+ *
+ * 2. <STRUCT> corresponding to a structure "struct <STRUCT>", that must be
+ *    defined below.  This structure must be an abstract definition of the
+ *    action.  Its first member must have type "struct ofpact" and name
+ *    "ofpact".  It may be fixed length or end with a flexible array member
+ *    (e.g. "int member[];").
+ *
+ * 3. <MEMBER>, which has one of two possible values:
+ *
+ *        - If "struct <STRUCT>" is fixed-length, it must be "ofpact".
+ *
+ *        - If "struct <STRUCT>" is variable-length, it must be the name of the
+ *          flexible array member.
+ */
+#define OFPACTS                                                     \
+    /* Output. */                                                   \
+    DEFINE_OFPACT(OUTPUT,          ofpact_output,        ofpact)    \
+    DEFINE_OFPACT(CONTROLLER,      ofpact_controller,    ofpact)    \
+    DEFINE_OFPACT(ENQUEUE,         ofpact_enqueue,       ofpact)    \
+    DEFINE_OFPACT(OUTPUT_REG,      ofpact_output_reg,    ofpact)    \
+    DEFINE_OFPACT(BUNDLE,          ofpact_bundle,        slaves)    \
+                                                                    \
+    /* Header changes. */                                           \
+    DEFINE_OFPACT(SET_VLAN_VID,    ofpact_vlan_vid,      ofpact)    \
+    DEFINE_OFPACT(SET_VLAN_PCP,    ofpact_vlan_pcp,      ofpact)    \
+    DEFINE_OFPACT(STRIP_VLAN,      ofpact_null,          ofpact)    \
+    DEFINE_OFPACT(SET_ETH_SRC,     ofpact_mac,           ofpact)    \
+    DEFINE_OFPACT(SET_ETH_DST,     ofpact_mac,           ofpact)    \
+    DEFINE_OFPACT(SET_IPV4_SRC,    ofpact_ipv4,          ofpact)    \
+    DEFINE_OFPACT(SET_IPV4_DST,    ofpact_ipv4,          ofpact)    \
+    DEFINE_OFPACT(SET_IPV4_DSCP,   ofpact_dscp,          ofpact)    \
+    DEFINE_OFPACT(SET_L4_SRC_PORT, ofpact_l4_port,       ofpact)    \
+    DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port,       ofpact)    \
+    DEFINE_OFPACT(REG_MOVE,        ofpact_reg_move,      ofpact)    \
+    DEFINE_OFPACT(REG_LOAD,        ofpact_reg_load,      ofpact)    \
+    DEFINE_OFPACT(DEC_TTL,         ofpact_cnt_ids,       cnt_ids)   \
+                                                                    \
+    /* Metadata. */                                                 \
+    DEFINE_OFPACT(SET_TUNNEL,      ofpact_tunnel,        ofpact)    \
+    DEFINE_OFPACT(SET_QUEUE,       ofpact_queue,         ofpact)    \
+    DEFINE_OFPACT(POP_QUEUE,       ofpact_null,          ofpact)    \
+    DEFINE_OFPACT(FIN_TIMEOUT,     ofpact_fin_timeout,   ofpact)    \
+                                                                    \
+    /* Flow table interaction. */                                   \
+    DEFINE_OFPACT(RESUBMIT,        ofpact_resubmit,      ofpact)    \
+    DEFINE_OFPACT(LEARN,           ofpact_learn,         specs)     \
+                                                                    \
+    /* Arithmetic. */                                               \
+    DEFINE_OFPACT(MULTIPATH,       ofpact_multipath,     ofpact)    \
+    DEFINE_OFPACT(AUTOPATH,        ofpact_autopath,      ofpact)    \
+                                                                    \
+    /* Other. */                                                    \
+    DEFINE_OFPACT(NOTE,            ofpact_note,          data)      \
+    DEFINE_OFPACT(EXIT,            ofpact_null,          ofpact)
+
+/* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */
+enum OVS_PACKED_ENUM ofpact_type {
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) OFPACT_##ENUM,
+    OFPACTS
+#undef DEFINE_OFPACT
+};
+
+/* N_OFPACTS, the number of values of "enum ofpact_type". */
+enum {
+    N_OFPACTS =
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) + 1
+    OFPACTS
+#undef DEFINE_OFPACT
+};
+
+/* Header for an action.
+ *
+ * Each action is a structure "struct ofpact_*" that begins with "struct
+ * ofpact", usually followed by other data that describes the action.  Actions
+ * are padded out to a multiple of OFPACT_ALIGNTO bytes in length. */
+struct ofpact {
+    enum ofpact_type type;      /* OFPACT_*. */
+    enum ofputil_action_code compat; /* Original type when added, if any. */
+    uint16_t len;               /* Length of the action, in bytes, including
+                                 * struct ofpact, excluding padding. */
+};
+
+#ifdef __GNUC__
+/* Make sure that OVS_PACKED_ENUM really worked. */
+BUILD_ASSERT_DECL(sizeof(struct ofpact) == 4);
+#endif
+
+/* Alignment. */
+#define OFPACT_ALIGNTO 8
+#define OFPACT_ALIGN(SIZE) ROUND_UP(SIZE, OFPACT_ALIGNTO)
+
+static inline struct ofpact *
+ofpact_next(const struct ofpact *ofpact)
+{
+    return (void *) ((uint8_t *) ofpact + OFPACT_ALIGN(ofpact->len));
+}
+
+static inline struct ofpact *
+ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len)
+{
+    return (void *) ((uint8_t *) ofpacts + ofpacts_len);
+}
+
+/* Assigns POS to each ofpact, in turn, in the OFPACTS_LEN bytes of ofpacts
+ * starting at OFPACTS. */
+#define OFPACT_FOR_EACH(POS, OFPACTS, OFPACTS_LEN)                      \
+    for ((POS) = (OFPACTS); (POS) < ofpact_end(OFPACTS, OFPACTS_LEN);  \
+         (POS) = ofpact_next(POS))
+\f
+/* Action structure for each OFPACT_*. */
+
+/* OFPACT_STRIP_VLAN, OFPACT_POP_QUEUE, OFPACT_EXIT.
+ *
+ * Used for OFPAT10_STRIP_VLAN, NXAST_DEC_TTL, NXAST_POP_QUEUE, NXAST_EXIT.
+ *
+ * Action structure for actions that do not have any extra data beyond the
+ * action type. */
+struct ofpact_null {
+    struct ofpact ofpact;
+};
+
+/* OFPACT_OUTPUT.
+ *
+ * Used for OFPAT10_OUTPUT. */
+struct ofpact_output {
+    struct ofpact ofpact;
+    uint16_t port;              /* Output port. */
+    uint16_t max_len;           /* Max send len, for port OFPP_CONTROLLER. */
+};
+
+/* OFPACT_CONTROLLER.
+ *
+ * Used for NXAST_CONTROLLER. */
+struct ofpact_controller {
+    struct ofpact ofpact;
+    uint16_t max_len;           /* Maximum length to send to controller. */
+    uint16_t controller_id;     /* Controller ID to send packet-in. */
+    enum ofp_packet_in_reason reason; /* Reason to put in packet-in. */
+};
+
+/* OFPACT_ENQUEUE.
+ *
+ * Used for OFPAT10_ENQUEUE. */
+struct ofpact_enqueue {
+    struct ofpact ofpact;
+    uint16_t port;
+    uint32_t queue;
+};
+
+/* OFPACT_OUTPUT_REG.
+ *
+ * Used for NXAST_OUTPUT_REG. */
+struct ofpact_output_reg {
+    struct ofpact ofpact;
+    struct mf_subfield src;
+    uint16_t max_len;
+};
+
+/* OFPACT_BUNDLE.
+ *
+ * Used for NXAST_BUNDLE. */
+struct ofpact_bundle {
+    struct ofpact ofpact;
+
+    /* Slave choice algorithm to apply to hash value. */
+    enum nx_bd_algorithm algorithm;
+
+    /* What fields to hash and how. */
+    enum nx_hash_fields fields;
+    uint16_t basis;             /* Universal hash parameter. */
+
+    struct mf_subfield dst;
+
+    /* Slaves for output. */
+    unsigned int n_slaves;
+    uint16_t slaves[];
+};
+
+/* OFPACT_SET_VLAN_VID.
+ *
+ * Used for OFPAT10_SET_VLAN_VID. */
+struct ofpact_vlan_vid {
+    struct ofpact ofpact;
+    uint16_t vlan_vid;          /* VLAN VID in low 12 bits, 0 in other bits. */
+};
+
+/* OFPACT_SET_VLAN_PCP.
+ *
+ * Used for OFPAT10_SET_VLAN_PCP. */
+struct ofpact_vlan_pcp {
+    struct ofpact ofpact;
+    uint8_t vlan_pcp;           /* VLAN PCP in low 3 bits, 0 in other bits. */
+};
+
+/* OFPACT_SET_ETH_SRC, OFPACT_SET_ETH_DST.
+ *
+ * Used for OFPAT10_SET_DL_SRC, OFPAT10_SET_DL_DST. */
+struct ofpact_mac {
+    struct ofpact ofpact;
+    uint8_t mac[ETH_ADDR_LEN];
+};
+
+/* OFPACT_SET_IPV4_SRC, OFPACT_SET_IPV4_DST.
+ *
+ * Used for OFPAT10_SET_NW_SRC, OFPAT10_SET_NW_DST. */
+struct ofpact_ipv4 {
+    struct ofpact ofpact;
+    ovs_be32 ipv4;
+};
+
+/* OFPACT_SET_IPV4_DSCP.
+ *
+ * Used for OFPAT10_SET_NW_TOS. */
+struct ofpact_dscp {
+    struct ofpact ofpact;
+    uint8_t dscp;               /* DSCP in high 6 bits, rest ignored. */
+};
+
+/* OFPACT_SET_L4_SRC_PORT, OFPACT_SET_L4_DST_PORT.
+ *
+ * Used for OFPAT10_SET_TP_SRC, OFPAT10_SET_TP_DST. */
+struct ofpact_l4_port {
+    struct ofpact ofpact;
+    uint16_t port;              /* TCP or UDP port number. */
+};
+
+/* OFPACT_REG_MOVE.
+ *
+ * Used for NXAST_REG_MOVE. */
+struct ofpact_reg_move {
+    struct ofpact ofpact;
+    struct mf_subfield src;
+    struct mf_subfield dst;
+};
+
+/* OFPACT_REG_LOAD.
+ *
+ * Used for NXAST_REG_LOAD. */
+struct ofpact_reg_load {
+    struct ofpact ofpact;
+    struct mf_subfield dst;
+    uint64_t value;
+};
+
+/* OFPACT_SET_TUNNEL.
+ *
+ * Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */
+struct ofpact_tunnel {
+    struct ofpact ofpact;
+    uint64_t tun_id;
+};
+
+/* OFPACT_SET_QUEUE.
+ *
+ * Used for NXAST_SET_QUEUE. */
+struct ofpact_queue {
+    struct ofpact ofpact;
+    uint32_t queue_id;
+};
+
+/* OFPACT_FIN_TIMEOUT.
+ *
+ * Used for NXAST_FIN_TIMEOUT. */
+struct ofpact_fin_timeout {
+    struct ofpact ofpact;
+    uint16_t fin_idle_timeout;
+    uint16_t fin_hard_timeout;
+};
+
+/* OFPACT_RESUBMIT.
+ *
+ * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */
+struct ofpact_resubmit {
+    struct ofpact ofpact;
+    uint16_t in_port;
+    uint8_t table_id;
+};
+
+/* Part of struct ofpact_learn, below. */
+struct ofpact_learn_spec {
+    int n_bits;
+
+    int src_type;
+    struct mf_subfield src;
+    union mf_subvalue src_imm;
+
+    int dst_type;
+    struct mf_subfield dst;
+};
+
+/* OFPACT_LEARN.
+ *
+ * Used for NXAST_LEARN. */
+struct ofpact_learn {
+    struct ofpact ofpact;
+
+    uint16_t idle_timeout;      /* Idle time before discarding (seconds). */
+    uint16_t hard_timeout;      /* Max time before discarding (seconds). */
+    uint16_t priority;          /* Priority level of flow entry. */
+    uint64_t cookie;            /* Cookie for new flow. */
+    uint16_t flags;             /* Either 0 or OFPFF_SEND_FLOW_REM. */
+    uint8_t table_id;           /* Table to insert flow entry. */
+    uint16_t fin_idle_timeout;  /* Idle timeout after FIN, if nonzero. */
+    uint16_t fin_hard_timeout;  /* Hard timeout after FIN, if nonzero. */
+
+    unsigned int n_specs;
+    struct ofpact_learn_spec specs[];
+};
+
+/* OFPACT_MULTIPATH.
+ *
+ * Used for NXAST_MULTIPATH. */
+struct ofpact_multipath {
+    struct ofpact ofpact;
+
+    /* What fields to hash and how. */
+    enum nx_hash_fields fields;
+    uint16_t basis;             /* Universal hash parameter. */
+
+    /* Multipath link choice algorithm to apply to hash value. */
+    enum nx_mp_algorithm algorithm;
+    uint16_t max_link;          /* Number of output links, minus 1. */
+    uint32_t arg;               /* Algorithm-specific argument. */
+
+    /* Where to store the result. */
+    struct mf_subfield dst;
+};
+
+/* OFPACT_AUTOPATH.
+ *
+ * Used for NXAST_AUTOPATH. */
+struct ofpact_autopath {
+    struct ofpact ofpact;
+    struct mf_subfield dst;
+    uint32_t port;
+};
+
+/* OFPACT_NOTE.
+ *
+ * Used for NXAST_NOTE. */
+struct ofpact_note {
+    struct ofpact ofpact;
+    size_t length;
+    uint8_t data[];
+};
+
+/* OFPACT_DEC_TTL.
+ *
+ * Used for NXAST_DEC_TTL and NXAST_DEC_TTL_CNT_IDS. */
+struct ofpact_cnt_ids {
+    struct ofpact ofpact;
+
+    /* Controller ids. */
+    unsigned int n_controllers;
+    uint16_t cnt_ids[];
+
+};
+
+/* Converting OpenFlow to ofpacts. */
+enum ofperr ofpacts_pull_openflow10(struct ofpbuf *openflow,
+                                    unsigned int actions_len,
+                                    struct ofpbuf *ofpacts);
+enum ofperr ofpacts_pull_openflow11_actions(struct ofpbuf *openflow,
+                                            unsigned int actions_len,
+                                            struct ofpbuf *ofpacts);
+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);
+
+/* Converting ofpacts to OpenFlow. */
+void ofpacts_put_openflow10(const struct ofpact[], size_t ofpacts_len,
+                            struct ofpbuf *openflow);
+size_t ofpacts_put_openflow11_actions(const struct ofpact[], size_t ofpacts_len,
+                                      struct ofpbuf *openflow);
+void ofpacts_put_openflow11_instructions(const struct ofpact[],
+                                         size_t ofpacts_len,
+                                         struct ofpbuf *openflow);
+
+/* Working with ofpacts. */
+bool ofpacts_output_to_port(const struct ofpact[], size_t ofpacts_len,
+                            uint16_t port);
+bool ofpacts_equal(const struct ofpact a[], size_t a_len,
+                   const struct ofpact b[], size_t b_len);
+
+/* Formatting ofpacts.
+ *
+ * (For parsing ofpacts, see ofp-parse.h.) */
+void ofpacts_format(const struct ofpact[], size_t ofpacts_len, struct ds *);
+
+/* Internal use by the helpers below. */
+void ofpact_init(struct ofpact *, enum ofpact_type, size_t len);
+void *ofpact_put(struct ofpbuf *, enum ofpact_type, size_t len);
+
+/* For each OFPACT_<ENUM> with a corresponding struct <STRUCT>, this defines
+ * the following commonly useful functions:
+ *
+ *   struct <STRUCT> *ofpact_put_<ENUM>(struct ofpbuf *ofpacts);
+ *
+ *     Appends a new 'ofpact', of length OFPACT_<ENUM>_RAW_SIZE, to 'ofpacts',
+ *     initializes it with ofpact_init_<ENUM>(), and returns it.  Also sets
+ *     'ofpacts->l2' to the returned action.
+ *
+ *     After using this function to add a variable-length action, add the
+ *     elements of the flexible array (e.g. with ofpbuf_put()), then use
+ *     ofpact_update_len() to update the length embedded into the action.
+ *     (Keep in mind the need to refresh the structure from 'ofpacts->l2' after
+ *     adding data to 'ofpacts'.)
+ *
+ *   struct <STRUCT> *ofpact_get_<ENUM>(const struct ofpact *ofpact);
+ *
+ *     Returns 'ofpact' cast to "struct <STRUCT> *".  'ofpact->type' must be
+ *     OFPACT_<ENUM>.
+ *
+ * as well as the following more rarely useful definitions:
+ *
+ *   void ofpact_init_<ENUM>(struct <STRUCT> *ofpact);
+ *
+ *     Initializes the parts of 'ofpact' that identify it as having type
+ *     OFPACT_<ENUM> and length OFPACT_<ENUM>_RAW_SIZE and zeros the rest.
+ *
+ *   <ENUM>_RAW_SIZE
+ *
+ *     The size of the action structure.  For a fixed-length action, this is
+ *     sizeof(struct <STRUCT>).  For a variable-length action, this is the
+ *     offset to the variable-length part.
+ *
+ *   <ENUM>_SIZE
+ *
+ *     An integer constant, the value of OFPACT_<ENUM>_RAW_SIZE rounded up to a
+ *     multiple of OFPACT_ALIGNTO.
+ */
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER)                             \
+    BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0);            \
+                                                                        \
+    enum { OFPACT_##ENUM##_RAW_SIZE                                     \
+           = (offsetof(struct STRUCT, MEMBER)                           \
+              ? offsetof(struct STRUCT, MEMBER)                         \
+              : sizeof(struct STRUCT)) };                               \
+                                                                        \
+    enum { OFPACT_##ENUM##_SIZE                                         \
+           = ROUND_UP(OFPACT_##ENUM##_RAW_SIZE, OFPACT_ALIGNTO) };      \
+                                                                        \
+    static inline struct STRUCT *                                       \
+    ofpact_get_##ENUM(const struct ofpact *ofpact)                      \
+    {                                                                   \
+        assert(ofpact->type == OFPACT_##ENUM);                          \
+        return (struct STRUCT *) ofpact;                                \
+    }                                                                   \
+                                                                        \
+    static inline struct STRUCT *                                       \
+    ofpact_put_##ENUM(struct ofpbuf *ofpacts)                           \
+    {                                                                   \
+        return ofpact_put(ofpacts, OFPACT_##ENUM,                       \
+                          OFPACT_##ENUM##_RAW_SIZE);                    \
+    }                                                                   \
+                                                                        \
+    static inline void                                                  \
+    ofpact_init_##ENUM(struct STRUCT *ofpact)                           \
+    {                                                                   \
+        ofpact_init(&ofpact->ofpact, OFPACT_##ENUM,                     \
+                    OFPACT_##ENUM##_RAW_SIZE);                          \
+    }
+OFPACTS
+#undef DEFINE_OFPACT
+
+/* Functions to use after adding ofpacts to a buffer. */
+void ofpact_update_len(struct ofpbuf *, struct ofpact *);
+void ofpact_pad(struct ofpbuf *);
+
+/* OpenFlow 1.1 instructions.
+ * The order is sorted in execution order. Not in the value of OFPIT11_xxx.
+ * It is enforced on parser from text string.
+ */
+#define OVS_INSTRUCTIONS                                    \
+    DEFINE_INST(OFPIT11_APPLY_ACTIONS,                      \
+                ofp11_instruction_actions,        true,     \
+                "apply_actions")                            \
+                                                            \
+    DEFINE_INST(OFPIT11_CLEAR_ACTIONS,                      \
+                ofp11_instruction,                false,    \
+                "clear_actions")                            \
+                                                            \
+    DEFINE_INST(OFPIT11_WRITE_ACTIONS,                      \
+                ofp11_instruction_actions,        true,     \
+                "write_actions")                            \
+                                                            \
+    DEFINE_INST(OFPIT11_WRITE_METADATA,                     \
+                ofp11_instruction_write_metadata, false,    \
+                "write_metadata")                           \
+                                                            \
+    DEFINE_INST(OFPIT11_GOTO_TABLE,                         \
+                ofp11_instruction_goto_table,     false,    \
+                "goto_table")
+
+enum ovs_instruction_type {
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) OVSINST_##ENUM,
+    OVS_INSTRUCTIONS
+#undef DEFINE_INST
+};
+
+enum {
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) + 1
+    N_OVS_INSTRUCTIONS = OVS_INSTRUCTIONS
+#undef DEFINE_INST
+};
+
+const char *ofpact_instruction_name_from_type(enum ovs_instruction_type type);
+int ofpact_instruction_type_from_name(const char *name);
+
+#endif /* ofp-actions.h */
index e1349b0..2c71312 100644 (file)
@@ -3,6 +3,7 @@
 #include <errno.h>
 #include "byte-order.h"
 #include "dynamic-string.h"
+#include "ofp-msgs.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
@@ -20,20 +21,27 @@ struct pair {
  * 'version' (one of the possible values of struct ofp_header's 'version'
  * member).  Returns NULL if the version isn't defined or isn't understood by
  * OVS. */
-const struct ofperr_domain *
-ofperr_domain_from_version(uint8_t version)
+static const struct ofperr_domain *
+ofperr_domain_from_version(enum ofp_version version)
 {
-    return (version == ofperr_of10.version ? &ofperr_of10
-            : version == ofperr_of11.version ? &ofperr_of11
-            : version == ofperr_of12.version ? &ofperr_of12
-            : NULL);
+    switch (version) {
+    case OFP10_VERSION:
+        return &ofperr_of10;
+    case OFP11_VERSION:
+        return &ofperr_of11;
+    case OFP12_VERSION:
+        return &ofperr_of12;
+    default:
+        return NULL;
+    }
 }
 
-/* Returns the name (e.g. "OpenFlow 1.0") of OpenFlow error domain 'domain'. */
+/* Returns the name (e.g. "OpenFlow 1.0") of OpenFlow version 'version'. */
 const char *
-ofperr_domain_get_name(const struct ofperr_domain *domain)
+ofperr_domain_get_name(enum ofp_version version)
 {
-    return domain->name;
+    const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+    return domain ? domain->name : NULL;
 }
 
 /* Returns true if 'error' is a valid OFPERR_* value, false otherwise. */
@@ -70,26 +78,31 @@ ofperr_is_nx_extension(enum ofperr error)
  * A given error may not be encodable in some domains because each OpenFlow
  * version tends to introduce new errors and retire some old ones. */
 bool
-ofperr_is_encodable(enum ofperr error, const struct ofperr_domain *domain)
+ofperr_is_encodable(enum ofperr error, enum ofp_version version)
 {
+    const struct ofperr_domain *domain = ofperr_domain_from_version(version);
     return (ofperr_is_valid(error)
-            && domain->errors[error - OFPERR_OFS].code >= 0);
+            && domain && domain->errors[error - OFPERR_OFS].code >= 0);
 }
 
 /* Returns the OFPERR_* value that corresponds to 'type' and 'code' within
- * 'domain', or 0 if no such OFPERR_* value exists. */
+ * 'version', or 0 if either no such OFPERR_* value exists or 'version' is
+ * unknown. */
 enum ofperr
-ofperr_decode(const struct ofperr_domain *domain, uint16_t type, uint16_t code)
+ofperr_decode(enum ofp_version version, uint16_t type, uint16_t code)
 {
-    return domain->decode(type, code);
+    const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+    return domain ? domain->decode(type, code) : 0;
 }
 
 /* Returns the OFPERR_* value that corresponds to the category 'type' within
- * 'domain', or 0 if no such OFPERR_* value exists. */
+ * 'version', or 0 if either no such OFPERR_* value exists or 'version' is
+ * unknown. */
 enum ofperr
-ofperr_decode_type(const struct ofperr_domain *domain, uint16_t type)
+ofperr_decode_type(enum ofp_version version, uint16_t type)
 {
-    return domain->decode_type(type);
+    const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+    return domain ? domain->decode_type(type) : 0;
 }
 
 /* Returns the name of 'error', e.g. "OFPBRC_BAD_TYPE" if 'error' is
@@ -144,18 +157,20 @@ ofperr_get_pair__(enum ofperr error, const struct ofperr_domain *domain)
 }
 
 static struct ofpbuf *
-ofperr_encode_msg__(enum ofperr error, const struct ofperr_domain *domain,
+ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
                     ovs_be32 xid, const void *data, size_t data_len)
 {
     struct ofp_error_msg *oem;
     const struct pair *pair;
     struct ofpbuf *buf;
+    const struct ofperr_domain *domain;
 
+    domain = ofperr_domain_from_version(ofp_version);
     if (!domain) {
         return NULL;
     }
 
-    if (!ofperr_is_encodable(error, domain)) {
+    if (!ofperr_is_encodable(error, ofp_version)) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
         if (!ofperr_is_valid(error)) {
@@ -176,25 +191,28 @@ ofperr_encode_msg__(enum ofperr error, const struct ofperr_domain *domain,
 
     pair = ofperr_get_pair__(error, domain);
     if (!ofperr_is_nx_extension(error)) {
-        oem = make_openflow_xid(data_len + sizeof *oem, OFPT_ERROR, xid, &buf);
+        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 {
         struct nx_vendor_error *nve;
 
-        oem = make_openflow_xid(data_len + sizeof *oem + sizeof *nve,
-                                OFPT_ERROR, xid, &buf);
+        buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
+                               sizeof *oem + sizeof *nve + data_len);
+
+        oem = ofpbuf_put_uninit(buf, sizeof *oem);
         oem->type = htons(NXET_VENDOR);
         oem->code = htons(NXVC_VENDOR_ERROR);
 
-        nve = (struct nx_vendor_error *) oem->data;
+        nve = ofpbuf_put_uninit(buf, sizeof *nve);
         nve->vendor = htonl(NX_VENDOR_ID);
         nve->type = htons(pair->type);
         nve->code = htons(pair->code);
     }
-    oem->header.version = domain->version;
 
-    buf->size -= data_len;
     ofpbuf_put(buf, data, data_len);
 
     return buf;
@@ -216,90 +234,91 @@ ofperr_encode_msg__(enum ofperr error, const struct ofperr_domain *domain,
 struct ofpbuf *
 ofperr_encode_reply(enum ofperr error, const struct ofp_header *oh)
 {
-    const struct ofperr_domain *domain;
     uint16_t len = ntohs(oh->length);
 
-    domain = ofperr_domain_from_version(oh->version);
-    return ofperr_encode_msg__(error, domain, oh->xid, oh, MIN(len, 64));
+    return ofperr_encode_msg__(error, oh->version, oh->xid, oh, MIN(len, 64));
 }
 
 /* Creates and returns an OpenFlow message of type OFPT_ERROR that conveys the
  * given 'error', in the error domain 'domain'.  The error message will include
  * the additional null-terminated text string 's'.
  *
- * If 'domain' is NULL, uses the OpenFlow 1.0 error domain.  OFPET_HELLO_FAILED
- * error messages are supposed to be backward-compatible, so in theory this
- * should work.
+ * If 'version' is an unknown version then OFP10_VERSION is used.
+ * OFPET_HELLO_FAILED error messages are supposed to be backward-compatible,
+ * so in theory this should work.
  *
  * Returns NULL if 'error' is not an OpenFlow error code or if 'error' cannot
  * be encoded in 'domain'. */
 struct ofpbuf *
-ofperr_encode_hello(enum ofperr error, const struct ofperr_domain *domain,
+ofperr_encode_hello(enum ofperr error, enum ofp_version ofp_version,
                     const char *s)
 {
-    if (!domain) {
-        domain = &ofperr_of10;
+    switch (ofp_version) {
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        break;
+
+    default:
+        ofp_version = OFP10_VERSION;
     }
-    return ofperr_encode_msg__(error, domain, htonl(0), s, strlen(s));
+
+    return ofperr_encode_msg__(error, ofp_version, htonl(0), s, strlen(s));
 }
 
 /* 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
- * 'domain'.
+ * 'version' or 'version' is unknown.
  *
  * 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */
 int
-ofperr_get_type(enum ofperr error, const struct ofperr_domain *domain)
+ofperr_get_type(enum ofperr error, enum ofp_version version)
 {
-    return ofperr_get_pair__(error, domain)->type;
+    const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+    return domain ? ofperr_get_pair__(error, domain)->type : -1;
 }
 
 /* Returns the value that would go into an OFPT_ERROR message's 'code' for
  * encoding 'error' in 'domain'.  Returns -1 if 'error' is not encodable in
- * 'domain' or if 'error' represents a category rather than a specific error.
+ * 'version', 'version' is unknown or if 'error' represents a category
+ * rather than a specific error.
+ *
  *
  * 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */
 int
-ofperr_get_code(enum ofperr error, const struct ofperr_domain *domain)
+ofperr_get_code(enum ofperr error, enum ofp_version version)
 {
-    return ofperr_get_pair__(error, domain)->code;
+    const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+    return domain ? ofperr_get_pair__(error, domain)->code : -1;
 }
 
 /* Tries to decodes 'oh', which should be an OpenFlow OFPT_ERROR message.
  * Returns an OFPERR_* constant on success, 0 on failure.
  *
- * If 'payload_ofs' is nonnull, on success '*payload_ofs' is set to the offset
- * to the payload starting from 'oh' and on failure it is set to 0. */
+ * If 'payload' is nonnull, on success '*payload' is initialized to the
+ * error's payload, and on failure it is cleared. */
 enum ofperr
-ofperr_decode_msg(const struct ofp_header *oh, size_t *payload_ofs)
+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 ofperr_domain *domain;
     const struct ofp_error_msg *oem;
+    enum ofpraw raw;
     uint16_t type, code;
     enum ofperr error;
     struct ofpbuf b;
 
-    if (payload_ofs) {
-        *payload_ofs = 0;
+    if (payload) {
+        memset(payload, 0, sizeof *payload);
     }
 
     /* Pull off the error message. */
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
-    oem = ofpbuf_try_pull(&b, sizeof *oem);
-    if (!oem) {
-        return 0;
-    }
-
-    /* Check message type and version. */
-    if (oh->type != OFPT_ERROR) {
-        return 0;
-    }
-    domain = ofperr_domain_from_version(oh->version);
-    if (!domain) {
+    error = ofpraw_pull(&raw, &b);
+    if (error) {
         return 0;
     }
+    oem = ofpbuf_pull(&b, sizeof *oem);
 
     /* Get the error type and code. */
     type = ntohs(oem->type);
@@ -321,12 +340,12 @@ ofperr_decode_msg(const struct ofp_header *oh, size_t *payload_ofs)
 
     /* Translate the error type and code into an ofperr.
      * If we don't know the error type and code, at least try for the type. */
-    error = ofperr_decode(domain, type, code);
+    error = ofperr_decode(oh->version, type, code);
     if (!error) {
-        error = ofperr_decode_type(domain, type);
+        error = ofperr_decode_type(oh->version, type);
     }
-    if (error && payload_ofs) {
-        *payload_ofs = (uint8_t *) b.data - (uint8_t *) oh;
+    if (error && payload) {
+        ofpbuf_use_const(payload, b.data, b.size);
     }
     return error;
 }
index 40e5a61..2f28a42 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <stddef.h>
 #include <stdint.h>
 
+#include "openflow/openflow.h"
+
 struct ds;
-struct ofp_header;
+struct ofpbuf;
 
 /* Error codes.
  *
@@ -129,6 +131,20 @@ enum ofperr {
      * 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').  */
+    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. */
+    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. */
+    OFPERR_NXBRC_FM_BAD_ID,
+
 /* ## ---------------- ## */
 /* ## OFPET_BAD_ACTION ## */
 /* ## ---------------- ## */
@@ -193,7 +209,7 @@ enum ofperr {
 /* ## --------------------- ## */
 
     /* OF1.1+(3).  Error in instruction list. */
-    OFPERR_OFPET_BAD_INSTRUCTION,
+    OFPERR_OFPIT_BAD_INSTRUCTION,
 
     /* OF1.1+(3,0).  Unknown instruction. */
     OFPERR_OFPBIC_UNKNOWN_INST,
@@ -222,6 +238,9 @@ enum ofperr {
     /* OF1.2+(3,8).  Permissions error. */
     OFPERR_OFPBIC_EPERM,
 
+    /* NX1.1+(3,256).  Duplicate instruction type in set of instructions. */
+    OFPERR_NXBIC_DUP_TYPE,
+
 /* ## --------------- ## */
 /* ## OFPET_BAD_MATCH ## */
 /* ## --------------- ## */
@@ -321,6 +340,9 @@ enum ofperr {
      * extension is enabled. */
     OFPERR_NXFMFC_BAD_TABLE_ID,
 
+    /* NX1.0+(3,258).  'out_group' specified but groups not yet supported. */
+    OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED,
+
 /* ## ---------------------- ## */
 /* ## OFPET_GROUP_MOD_FAILED ## */
 /* ## ---------------------- ## */
@@ -466,7 +488,6 @@ enum ofperr {
     /* NX1.0(1,513), NX1.1(1,513), OF1.2+(11,2).  Invalid role. */
     OFPERR_OFPRRFC_BAD_ROLE,
 
-
 /* ## ------------------ ## */
 /* ## OFPET_EXPERIMENTER ## */
 /* ## ------------------ ## */
@@ -475,29 +496,24 @@ enum ofperr {
     OFPERR_OFPET_EXPERIMENTER,
 };
 
-extern const struct ofperr_domain ofperr_of10;
-extern const struct ofperr_domain ofperr_of11;
-extern const struct ofperr_domain ofperr_of12;
-
-const struct ofperr_domain *ofperr_domain_from_version(uint8_t version);
-const char *ofperr_domain_get_name(const struct ofperr_domain *);
+const char *ofperr_domain_get_name(enum ofp_version);
 
 bool ofperr_is_valid(enum ofperr);
 bool ofperr_is_category(enum ofperr);
 bool ofperr_is_nx_extension(enum ofperr);
-bool ofperr_is_encodable(enum ofperr, const struct ofperr_domain *);
+bool ofperr_is_encodable(enum ofperr, enum ofp_version);
 
-enum ofperr ofperr_decode(const struct ofperr_domain *,
-                          uint16_t type, uint16_t code);
-enum ofperr ofperr_decode_type(const struct ofperr_domain *, uint16_t type);
+enum ofperr ofperr_decode(enum ofp_version, uint16_t type, uint16_t code);
+enum ofperr ofperr_decode_type(enum ofp_version, uint16_t type);
 enum ofperr ofperr_from_name(const char *);
 
-enum ofperr ofperr_decode_msg(const struct ofp_header *, size_t *payload_ofs);
+enum ofperr ofperr_decode_msg(const struct ofp_header *,
+                              struct ofpbuf *payload);
 struct ofpbuf *ofperr_encode_reply(enum ofperr, const struct ofp_header *);
-struct ofpbuf *ofperr_encode_hello(enum ofperr, const struct ofperr_domain *,
+struct ofpbuf *ofperr_encode_hello(enum ofperr, enum ofp_version ofp_version,
                                    const char *);
-int ofperr_get_type(enum ofperr, const struct ofperr_domain *);
-int ofperr_get_code(enum ofperr, const struct ofperr_domain *);
+int ofperr_get_type(enum ofperr, enum ofp_version);
+int ofperr_get_code(enum ofperr, enum ofp_version);
 
 const char *ofperr_get_name(enum ofperr);
 const char *ofperr_get_description(enum ofperr);
diff --git a/lib/ofp-msgs.c b/lib/ofp-msgs.c
new file mode 100644 (file)
index 0000000..00e1a84
--- /dev/null
@@ -0,0 +1,989 @@
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "ofp-msgs.h"
+#include <assert.h>
+#include "byte-order.h"
+#include "dynamic-string.h"
+#include "hash.h"
+#include "hmap.h"
+#include "ofpbuf.h"
+#include "openflow/nicira-ext.h"
+#include "openflow/openflow.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ofp_msgs);
+
+#define OFPT_VENDOR 4
+#define OFPT10_STATS_REQUEST 16
+#define OFPT10_STATS_REPLY 17
+#define OFPT11_STATS_REQUEST 18
+#define OFPT11_STATS_REPLY 19
+#define OFPST_VENDOR 0xffff
+
+/* A thin abstraction of OpenFlow headers:
+ *
+ *   - 'version' and 'type' come straight from struct ofp_header, so these are
+ *     always present and meaningful.
+ *
+ *   - 'stat' comes from the 'type' member in statistics messages only.  It is
+ *     meaningful, therefore, only if 'version' and 'type' taken together
+ *     specify a statistics request or reply.  Otherwise it is 0.
+ *
+ *   - 'vendor' is meaningful only for vendor messages, that is, if 'version'
+ *     and 'type' specify a vendor message or if 'version' and 'type' specify
+ *     a statistics message and 'stat' specifies a vendor statistic type.
+ *     Otherwise it is 0.
+ *
+ *   - 'subtype' is meaningful only for vendor messages and otherwise 0.  It
+ *     specifies a vendor-defined subtype.  There is no standard format for
+ *     these but 32 bits seems like it should be enough. */
+struct ofphdrs {
+    uint8_t version;            /* From ofp_header. */
+    uint8_t type;               /* From ofp_header. */
+    uint16_t stat;              /* From ofp10_stats_msg or ofp11_stats_msg. */
+    uint32_t vendor;            /* From ofp_vendor_header,
+                                 * ofp10_vendor_stats_msg, or
+                                 * ofp11_vendor_stats_msg. */
+    uint32_t subtype;           /* From nicira_header, nicira10_stats_msg, or
+                                 * nicira11_stats_msg. */
+};
+BUILD_ASSERT_DECL(sizeof(struct ofphdrs) == 12);
+
+/* A mapping from OpenFlow headers to OFPRAW_*.  */
+struct raw_instance {
+    struct hmap_node hmap_node; /* In 'raw_instance_map'. */
+    struct ofphdrs hdrs;        /* Key. */
+    enum ofpraw raw;            /* Value. */
+    unsigned int hdrs_len;      /* ofphdrs_len(hdrs). */
+};
+
+/* Information about a particular 'enum ofpraw'. */
+struct raw_info {
+    /* All possible instantiations of this OFPRAW_* into OpenFlow headers. */
+    struct raw_instance *instances; /* min_version - max_version + 1 elems. */
+    uint8_t min_version;
+    uint8_t max_version;
+
+    unsigned int min_body;
+    unsigned int extra_multiple;
+    enum ofptype type;
+    const char *name;
+};
+
+/* All understood OpenFlow message types, indexed by their 'struct ofphdrs'. */
+static struct hmap raw_instance_map;
+#include "ofp-msgs.inc"
+
+static ovs_be32 alloc_xid(void);
+
+/* ofphdrs functions. */
+static uint32_t ofphdrs_hash(const struct ofphdrs *);
+static bool ofphdrs_equal(const struct ofphdrs *a, const struct ofphdrs *b);
+static enum ofperr ofphdrs_decode(struct ofphdrs *,
+                                  const struct ofp_header *oh, size_t length);
+static void ofphdrs_decode_assert(struct ofphdrs *,
+                                  const struct ofp_header *oh, size_t length);
+size_t ofphdrs_len(const struct ofphdrs *);
+
+static const struct raw_info *raw_info_get(enum ofpraw);
+static struct raw_instance *raw_instance_get(const struct raw_info *,
+                                             uint8_t version);
+
+static enum ofperr ofpraw_from_ofphdrs(enum ofpraw *, const struct ofphdrs *);
+\f
+/* Returns a transaction ID to use for an outgoing OpenFlow message. */
+static ovs_be32
+alloc_xid(void)
+{
+    static uint32_t next_xid = 1;
+    return htonl(next_xid++);
+}
+\f
+static uint32_t
+ofphdrs_hash(const struct ofphdrs *hdrs)
+{
+    BUILD_ASSERT_DECL(sizeof *hdrs == 12);
+    return hash_words((const uint32_t *) hdrs, 3, 0);
+}
+
+static bool
+ofphdrs_equal(const struct ofphdrs *a, const struct ofphdrs *b)
+{
+    return !memcmp(a, b, sizeof *a);
+}
+
+static void
+log_bad_vendor(uint32_t vendor)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+    VLOG_WARN_RL(&rl, "OpenFlow message has unknown vendor %#"PRIx32, vendor);
+}
+
+static enum ofperr
+ofphdrs_decode(struct ofphdrs *hdrs,
+               const struct ofp_header *oh, size_t length)
+{
+    memset(hdrs, 0, sizeof *hdrs);
+    if (length < sizeof *oh) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    /* Get base message version and type (OFPT_*). */
+    hdrs->version = oh->version;
+    hdrs->type = oh->type;
+
+    if (hdrs->type == OFPT_VENDOR) {
+        /* Get vendor. */
+        const struct ofp_vendor_header *ovh;
+
+        if (length < sizeof *ovh) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+
+        ovh = (const struct ofp_vendor_header *) oh;
+        hdrs->vendor = ntohl(ovh->vendor);
+        if (hdrs->vendor == NX_VENDOR_ID) {
+            /* Get Nicira message subtype (NXT_*). */
+            const struct nicira_header *nh;
+
+            if (length < sizeof *nh) {
+                return OFPERR_OFPBRC_BAD_LEN;
+            }
+            nh = (const struct nicira_header *) oh;
+            hdrs->subtype = ntohl(nh->subtype);
+        } else {
+            log_bad_vendor(hdrs->vendor);
+            return OFPERR_OFPBRC_BAD_VENDOR;
+        }
+    } else if (hdrs->version == OFP10_VERSION
+               && (hdrs->type == OFPT10_STATS_REQUEST ||
+                   hdrs->type == OFPT10_STATS_REPLY)) {
+        const struct ofp10_stats_msg *osm;
+
+        /* Get statistic type (OFPST_*). */
+        if (length < sizeof *osm) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        osm = (const struct ofp10_stats_msg *) oh;
+        hdrs->stat = ntohs(osm->type);
+
+        if (hdrs->stat == OFPST_VENDOR) {
+            /* Get vendor. */
+            const struct ofp10_vendor_stats_msg *ovsm;
+
+            if (length < sizeof *ovsm) {
+                return OFPERR_OFPBRC_BAD_LEN;
+            }
+
+            ovsm = (const struct ofp10_vendor_stats_msg *) oh;
+            hdrs->vendor = ntohl(ovsm->vendor);
+            if (hdrs->vendor == NX_VENDOR_ID) {
+                /* Get Nicira statistic type (NXST_*). */
+                const struct nicira10_stats_msg *nsm;
+
+                if (length < sizeof *nsm) {
+                    return OFPERR_OFPBRC_BAD_LEN;
+                }
+                nsm = (const struct nicira10_stats_msg *) oh;
+                hdrs->subtype = ntohl(nsm->subtype);
+            } else {
+                log_bad_vendor(hdrs->vendor);
+                return OFPERR_OFPBRC_BAD_VENDOR;
+            }
+        }
+    } else if (hdrs->version != OFP10_VERSION
+               && (hdrs->type == OFPT11_STATS_REQUEST ||
+                   hdrs->type == OFPT11_STATS_REPLY)) {
+        const struct ofp11_stats_msg *osm;
+
+        /* Get statistic type (OFPST_*). */
+        if (length < sizeof *osm) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        osm = (const struct ofp11_stats_msg *) oh;
+        hdrs->stat = ntohs(osm->type);
+
+        if (hdrs->stat == OFPST_VENDOR) {
+            /* Get vendor. */
+            const struct ofp11_vendor_stats_msg *ovsm;
+
+            if (length < sizeof *ovsm) {
+                return OFPERR_OFPBRC_BAD_LEN;
+            }
+
+            ovsm = (const struct ofp11_vendor_stats_msg *) oh;
+            hdrs->vendor = ntohl(ovsm->vendor);
+            if (hdrs->vendor == NX_VENDOR_ID) {
+                /* Get Nicira statistic type (NXST_*). */
+                const struct nicira11_stats_msg *nsm;
+
+                if (length < sizeof *nsm) {
+                    return OFPERR_OFPBRC_BAD_LEN;
+                }
+                nsm = (const struct nicira11_stats_msg *) oh;
+                hdrs->subtype = ntohl(nsm->subtype);
+            } else {
+                log_bad_vendor(hdrs->vendor);
+                return OFPERR_OFPBRC_BAD_VENDOR;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void
+ofphdrs_decode_assert(struct ofphdrs *hdrs,
+                      const struct ofp_header *oh, size_t length)
+{
+    enum ofperr error = ofphdrs_decode(hdrs, oh, length);
+    assert(!error);
+}
+
+static bool
+ofphdrs_is_stat(const struct ofphdrs *hdrs)
+{
+    switch ((enum ofp_version) hdrs->version) {
+    case OFP10_VERSION:
+        return (hdrs->type == OFPT10_STATS_REQUEST ||
+                hdrs->type == OFPT10_STATS_REPLY);
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        return (hdrs->type == OFPT11_STATS_REQUEST ||
+                hdrs->type == OFPT11_STATS_REPLY);
+    }
+
+    return false;
+}
+
+size_t
+ofphdrs_len(const struct ofphdrs *hdrs)
+{
+    if (hdrs->type == OFPT_VENDOR) {
+        return sizeof(struct nicira_header);
+    }
+
+    switch ((enum ofp_version) hdrs->version) {
+    case OFP10_VERSION:
+        if (hdrs->type == OFPT10_STATS_REQUEST ||
+            hdrs->type == OFPT10_STATS_REPLY) {
+            return (hdrs->stat == OFPST_VENDOR
+                    ? sizeof(struct nicira10_stats_msg)
+                    : sizeof(struct ofp10_stats_msg));
+        }
+        break;
+
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        if (hdrs->type == OFPT11_STATS_REQUEST ||
+            hdrs->type == OFPT11_STATS_REPLY) {
+            return (hdrs->stat == OFPST_VENDOR
+                    ? sizeof(struct nicira11_stats_msg)
+                    : sizeof(struct ofp11_stats_msg));
+        }
+        break;
+    }
+
+    return sizeof(struct ofp_header);
+}
+\f
+/* Determines the OFPRAW_* type of the OpenFlow message at 'oh', which has
+ * length 'oh->length'.  (The caller must ensure that 'oh->length' bytes of
+ * data are readable at 'oh'.)  On success, returns 0 and stores the type into
+ * '*raw'.  On failure, returns an OFPERR_* error code and zeros '*raw'.
+ *
+ * This function checks that 'oh' is a valid length for its particular type of
+ * message, and returns an error if not. */
+enum ofperr
+ofpraw_decode(enum ofpraw *raw, const struct ofp_header *oh)
+{
+    struct ofpbuf msg;
+
+    ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+    return ofpraw_pull(raw, &msg);
+}
+
+/* Determines the OFPRAW_* type of the OpenFlow message in 'msg', which starts
+ * at 'msg->data' and has length 'msg->size' bytes.  On success, returns 0 and
+ * stores the type into '*rawp'.  On failure, returns an OFPERR_* error code
+ * and zeros '*rawp'.
+ *
+ * This function checks that the message has a valid length for its particular
+ * type of message, and returns an error if not.
+ *
+ * In addition to setting '*rawp', this function pulls off the OpenFlow header
+ * (including the stats headers, vendor header, and any subtype header) with
+ * ofpbuf_pull().  It also sets 'msg->l2' to the start of the OpenFlow header
+ * and 'msg->l3' just beyond the headers (that is, to the final value of
+ * msg->data). */
+enum ofperr
+ofpraw_pull(enum ofpraw *rawp, struct ofpbuf *msg)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+    const struct raw_instance *instance;
+    const struct raw_info *info;
+    struct ofphdrs hdrs;
+
+    unsigned int min_len;
+    unsigned int len;
+
+    enum ofperr error;
+    enum ofpraw raw;
+
+    /* Set default outputs. */
+    msg->l2 = msg->l3 = msg->data;
+    *rawp = 0;
+
+    len = msg->size;
+    error = ofphdrs_decode(&hdrs, msg->data, len);
+    if (error) {
+        return error;
+    }
+
+    error = ofpraw_from_ofphdrs(&raw, &hdrs);
+    if (error) {
+        return error;
+    }
+
+    info = raw_info_get(raw);
+    instance = raw_instance_get(info, hdrs.version);
+    msg->l2 = ofpbuf_pull(msg, instance->hdrs_len);
+    msg->l3 = msg->data;
+
+    min_len = instance->hdrs_len + info->min_body;
+    switch (info->extra_multiple) {
+    case 0:
+        if (len != min_len) {
+            VLOG_WARN_RL(&rl, "received %s with incorrect length %u (expected "
+                         "length %u)", info->name, len, min_len);
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        break;
+
+    case 1:
+        if (len < min_len) {
+            VLOG_WARN_RL(&rl, "received %s with incorrect length %u (expected "
+                         "length at least %u bytes)",
+                         info->name, len, min_len);
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        break;
+
+    default:
+        if (len < min_len || (len - min_len) % info->extra_multiple) {
+            VLOG_WARN_RL(&rl, "received %s with incorrect length %u (must be "
+                         "exactly %u bytes or longer by an integer multiple "
+                         "of %u bytes)",
+                         info->name, len, min_len, info->extra_multiple);
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        break;
+    }
+
+    *rawp = raw;
+    return 0;
+}
+
+/* Does the same job as ofpraw_pull(), except that it assert-fails if
+ * ofpbuf_pull() would have reported an error.  Thus, it's able to use the
+ * return value for the OFPRAW_* message type instead of an error code.
+ *
+ * (It only makes sense to use this function if you previously called
+ * ofpbuf_decode() on the message and thus know that it's OK.) */
+enum ofpraw
+ofpraw_pull_assert(struct ofpbuf *msg)
+{
+    enum ofperr error;
+    enum ofpraw raw;
+
+    error = ofpraw_pull(&raw, msg);
+    assert(!error);
+    return raw;
+}
+
+/* Determines the OFPRAW_* type of the OpenFlow message that starts at 'oh' and
+ * has length 'length' bytes.  On success, returns 0 and stores the type into
+ * '*rawp'.  On failure, returns an OFPERR_* error code and zeros '*rawp'.
+ *
+ * Unlike other functions for decoding message types, this one is not picky
+ * about message length.  For example, it will successfully decode a message
+ * whose body is shorter than the minimum length for a message of its type.
+ * Thus, this is the correct function to use for decoding the type of a message
+ * that might have been truncated, such as the payload of an OpenFlow error
+ * message (which is allowed to be truncated to 64 bytes). */
+enum ofperr
+ofpraw_decode_partial(enum ofpraw *raw,
+                      const struct ofp_header *oh, size_t length)
+{
+    struct ofphdrs hdrs;
+    enum ofperr error;
+
+    error = ofphdrs_decode(&hdrs, oh, length);
+    if (!error) {
+        error = ofpraw_from_ofphdrs(raw, &hdrs);
+    }
+
+    if (error) {
+        *raw = 0;
+    }
+    return error;
+}
+\f
+/* Encoding messages using OFPRAW_* values. */
+
+static void ofpraw_put__(enum ofpraw, uint8_t version, ovs_be32 xid,
+                         size_t extra_tailroom, struct ofpbuf *);
+
+/* Allocates and returns a new ofpbuf that contains an OpenFlow header for
+ * 'raw' with OpenFlow version 'version' and a fresh OpenFlow transaction ID.
+ * The ofpbuf has enough tailroom for the minimum body length of 'raw', plus
+ * 'extra_tailroom' additional bytes.
+ *
+ * Each 'raw' value is valid only for certain OpenFlow versions.  The caller
+ * must specify a valid (raw, version) pair.
+ *
+ * In the returned ofpbuf, 'l2' points to the beginning of the OpenFlow header
+ * and 'l3' points just after it, to where the message's body will start.  The
+ * caller must actually allocate the body into the space reserved for it,
+ * e.g. with ofpbuf_put_uninit().
+ *
+ * The caller owns the returned ofpbuf and must free it when it is no longer
+ * needed, e.g. with ofpbuf_delete(). */
+struct ofpbuf *
+ofpraw_alloc(enum ofpraw raw, uint8_t version, size_t extra_tailroom)
+{
+    return ofpraw_alloc_xid(raw, version, alloc_xid(), extra_tailroom);
+}
+
+/* Same as ofpraw_alloc() but the caller provides the transaction ID. */
+struct ofpbuf *
+ofpraw_alloc_xid(enum ofpraw raw, uint8_t version, ovs_be32 xid,
+                 size_t extra_tailroom)
+{
+    struct ofpbuf *buf = ofpbuf_new(0);
+    ofpraw_put__(raw, version, xid, extra_tailroom, buf);
+    return buf;
+}
+
+/* Same as ofpraw_alloc(), but obtains the OpenFlow version and transaction ID
+ * from 'request->version' and 'request->xid', respectively.
+ *
+ * Even though the version comes from 'request->version', the caller must still
+ * know what it is doing, by specifying a valid pairing of 'raw' and
+ * 'request->version', just like ofpraw_alloc(). */
+struct ofpbuf *
+ofpraw_alloc_reply(enum ofpraw raw, const struct ofp_header *request,
+                   size_t extra_tailroom)
+{
+    return ofpraw_alloc_xid(raw, request->version, request->xid,
+                            extra_tailroom);
+}
+
+/* Allocates and returns a new ofpbuf that contains an OpenFlow header that is
+ * a stats reply to the stats request in 'request', using the same OpenFlow
+ * version and transaction ID as 'request'.  The ofpbuf has enough tailroom for
+ * the stats reply's minimum body length, plus 'extra_tailroom' additional
+ * bytes.
+ *
+ * 'request' must be a stats request, that is, an OFPRAW_OFPST* or OFPRAW_NXST*
+ * value.  Every stats request has a corresponding reply, so the (raw, version)
+ * pairing pitfalls of the other ofpraw_alloc_*() functions don't apply here.
+ *
+ * In the returned ofpbuf, 'l2' points to the beginning of the OpenFlow header
+ * and 'l3' points just after it, to where the message's body will start.  The
+ * caller must actually allocate the body into the space reserved for it,
+ * e.g. with ofpbuf_put_uninit().
+ *
+ * The caller owns the returned ofpbuf and must free it when it is no longer
+ * needed, e.g. with ofpbuf_delete(). */
+struct ofpbuf *
+ofpraw_alloc_stats_reply(const struct ofp_header *request,
+                         size_t extra_tailroom)
+{
+    enum ofpraw request_raw;
+    enum ofpraw reply_raw;
+    enum ofperr error;
+
+    error = ofpraw_decode_partial(&request_raw, request,
+                                  ntohs(request->length));
+    assert(!error);
+
+    reply_raw = ofpraw_stats_request_to_reply(request_raw, request->version);
+    assert(reply_raw);
+
+    return ofpraw_alloc_reply(reply_raw, request, extra_tailroom);
+}
+
+/* Appends to 'buf' an OpenFlow header for 'raw' with OpenFlow version
+ * 'version' and a fresh OpenFlow transaction ID.  Preallocates enough tailroom
+ * in 'buf' for the minimum body length of 'raw', plus 'extra_tailroom'
+ * additional bytes.
+ *
+ * Each 'raw' value is valid only for certain OpenFlow versions.  The caller
+ * must specify a valid (raw, version) pair.
+ *
+ * Upon return, 'buf->l2' points to the beginning of the OpenFlow header and
+ * 'buf->l3' points just after it, to where the message's body will start.  The
+ * caller must actually allocating the body into the space reserved for it,
+ * e.g. with ofpbuf_put_uninit(). */
+void
+ofpraw_put(enum ofpraw raw, uint8_t version, struct ofpbuf *buf)
+{
+    ofpraw_put__(raw, version, alloc_xid(), 0, buf);
+}
+
+/* Same as ofpraw_put() but the caller provides the transaction ID. */
+void
+ofpraw_put_xid(enum ofpraw raw, uint8_t version, ovs_be32 xid,
+               struct ofpbuf *buf)
+{
+    ofpraw_put__(raw, version, xid, 0, buf);
+}
+
+/* Same as ofpraw_put(), but obtains the OpenFlow version and transaction ID
+ * from 'request->version' and 'request->xid', respectively.
+ *
+ * Even though the version comes from 'request->version', the caller must still
+ * know what it is doing, by specifying a valid pairing of 'raw' and
+ * 'request->version', just like ofpraw_put(). */
+void
+ofpraw_put_reply(enum ofpraw raw, const struct ofp_header *request,
+                 struct ofpbuf *buf)
+{
+    ofpraw_put__(raw, request->version, request->xid, 0, buf);
+}
+
+/* Appends to 'buf' an OpenFlow header that is a stats reply to the stats
+ * request in 'request', using the same OpenFlow version and transaction ID as
+ * 'request'.  Preallocate enough tailroom in 'buf for the stats reply's
+ * minimum body length, plus 'extra_tailroom' additional bytes.
+ *
+ * 'request' must be a stats request, that is, an OFPRAW_OFPST* or OFPRAW_NXST*
+ * value.  Every stats request has a corresponding reply, so the (raw, version)
+ * pairing pitfalls of the other ofpraw_alloc_*() functions don't apply here.
+ *
+ * In the returned ofpbuf, 'l2' points to the beginning of the OpenFlow header
+ * and 'l3' points just after it, to where the message's body will start.  The
+ * caller must actually allocate the body into the space reserved for it,
+ * e.g. with ofpbuf_put_uninit().
+ *
+ * The caller owns the returned ofpbuf and must free it when it is no longer
+ * needed, e.g. with ofpbuf_delete(). */
+void
+ofpraw_put_stats_reply(const struct ofp_header *request, struct ofpbuf *buf)
+{
+    enum ofperr error;
+    enum ofpraw raw;
+
+    error = ofpraw_decode_partial(&raw, request, ntohs(request->length));
+    assert(!error);
+
+    raw = ofpraw_stats_request_to_reply(raw, request->version);
+    assert(raw);
+
+    ofpraw_put__(raw, request->version, request->xid, 0, buf);
+}
+
+static void
+ofpraw_put__(enum ofpraw raw, uint8_t version, ovs_be32 xid,
+             size_t extra_tailroom, struct ofpbuf *buf)
+{
+    const struct raw_info *info = raw_info_get(raw);
+    const struct raw_instance *instance = raw_instance_get(info, version);
+    const struct ofphdrs *hdrs = &instance->hdrs;
+    struct ofp_header *oh;
+
+    ofpbuf_prealloc_tailroom(buf, (instance->hdrs_len + info->min_body
+                                   + extra_tailroom));
+    buf->l2 = ofpbuf_put_uninit(buf, instance->hdrs_len);
+    buf->l3 = ofpbuf_tail(buf);
+
+    oh = buf->l2;
+    oh->version = version;
+    oh->type = hdrs->type;
+    oh->length = htons(buf->size);
+    oh->xid = xid;
+
+    if (hdrs->type == OFPT_VENDOR) {
+        struct nicira_header *nh = buf->l2;
+
+        assert(hdrs->vendor == NX_VENDOR_ID);
+        nh->vendor = htonl(hdrs->vendor);
+        nh->subtype = htonl(hdrs->subtype);
+    } else if (version == OFP10_VERSION
+               && (hdrs->type == OFPT10_STATS_REQUEST ||
+                   hdrs->type == OFPT10_STATS_REPLY)) {
+        struct ofp10_stats_msg *osm = buf->l2;
+
+        osm->type = htons(hdrs->stat);
+        osm->flags = htons(0);
+
+        if (hdrs->stat == OFPST_VENDOR) {
+            struct ofp10_vendor_stats_msg *ovsm = buf->l2;
+
+            ovsm->vendor = htonl(hdrs->vendor);
+            if (hdrs->vendor == NX_VENDOR_ID) {
+                struct nicira10_stats_msg *nsm = buf->l2;
+
+                nsm->subtype = htonl(hdrs->subtype);
+                memset(nsm->pad, 0, sizeof nsm->pad);
+            } else {
+                NOT_REACHED();
+            }
+        }
+    } else if (version != OFP10_VERSION
+               && (hdrs->type == OFPT11_STATS_REQUEST ||
+                   hdrs->type == OFPT11_STATS_REPLY)) {
+        struct ofp11_stats_msg *osm = buf->l2;
+
+        osm->type = htons(hdrs->stat);
+        osm->flags = htons(0);
+        memset(osm->pad, 0, sizeof osm->pad);
+
+        if (hdrs->stat == OFPST_VENDOR) {
+            struct ofp11_vendor_stats_msg *ovsm = buf->l2;
+
+            ovsm->vendor = htonl(hdrs->vendor);
+            if (hdrs->vendor == NX_VENDOR_ID) {
+                struct nicira11_stats_msg *nsm = buf->l2;
+
+                nsm->subtype = htonl(hdrs->subtype);
+            } else {
+                NOT_REACHED();
+            }
+        }
+    }
+}
+\f
+/* Returns 'raw''s name.
+ *
+ * The name is the name used for 'raw' in the OpenFlow specification.  For
+ * example, ofpraw_get_name(OFPRAW_OFPT10_FEATURES_REPLY) is
+ * "OFPT_FEATURES_REPLY".
+ *
+ * The caller must not modify or free the returned string. */
+const char *
+ofpraw_get_name(enum ofpraw raw)
+{
+    return raw_info_get(raw)->name;
+}
+
+/* Returns the stats reply that corresponds to 'raw' in the given OpenFlow
+ * 'version'. */
+enum ofpraw
+ofpraw_stats_request_to_reply(enum ofpraw raw, uint8_t version)
+{
+    const struct raw_info *info = raw_info_get(raw);
+    const struct raw_instance *instance = raw_instance_get(info, version);
+    enum ofpraw reply_raw;
+    struct ofphdrs hdrs;
+    enum ofperr error;
+
+    hdrs = instance->hdrs;
+    switch ((enum ofp_version)hdrs.version) {
+    case OFP10_VERSION:
+        assert(hdrs.type == OFPT10_STATS_REQUEST);
+        hdrs.type = OFPT10_STATS_REPLY;
+        break;
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        assert(hdrs.type == OFPT11_STATS_REQUEST);
+        hdrs.type = OFPT11_STATS_REPLY;
+        break;
+    default:
+        NOT_REACHED();
+    }
+
+    error = ofpraw_from_ofphdrs(&reply_raw, &hdrs);
+    assert(!error);
+
+    return reply_raw;
+}
+\f
+/* Determines the OFPTYPE_* type of the OpenFlow message at 'oh', which has
+ * length 'oh->length'.  (The caller must ensure that 'oh->length' bytes of
+ * data are readable at 'oh'.)  On success, returns 0 and stores the type into
+ * '*typep'.  On failure, returns an OFPERR_* error code and zeros '*typep'.
+ *
+ * This function checks that 'oh' is a valid length for its particular type of
+ * message, and returns an error if not. */
+enum ofperr
+ofptype_decode(enum ofptype *typep, const struct ofp_header *oh)
+{
+    enum ofperr error;
+    enum ofpraw raw;
+
+    error = ofpraw_decode(&raw, oh);
+    *typep = error ? 0 : ofptype_from_ofpraw(raw);
+    return error;
+}
+
+/* Determines the OFPTYPE_* type of the OpenFlow message in 'msg', which starts
+ * at 'msg->data' and has length 'msg->size' bytes.  On success, returns 0 and
+ * stores the type into '*typep'.  On failure, returns an OFPERR_* error code
+ * and zeros '*typep'.
+ *
+ * This function checks that the message has a valid length for its particular
+ * type of message, and returns an error if not.
+ *
+ * In addition to setting '*typep', this function pulls off the OpenFlow header
+ * (including the stats headers, vendor header, and any subtype header) with
+ * ofpbuf_pull().  It also sets 'msg->l2' to the start of the OpenFlow header
+ * and 'msg->l3' just beyond the headers (that is, to the final value of
+ * msg->data). */
+enum ofperr
+ofptype_pull(enum ofptype *typep, struct ofpbuf *buf)
+{
+    enum ofperr error;
+    enum ofpraw raw;
+
+    error = ofpraw_pull(&raw, buf);
+    *typep = error ? 0 : ofptype_from_ofpraw(raw);
+    return error;
+}
+
+/* Returns the OFPTYPE_* type that corresponds to 'raw'.
+ *
+ * (This is a one-way trip, because the mapping from ofpraw to ofptype is
+ * many-to-one.)  */
+enum ofptype
+ofptype_from_ofpraw(enum ofpraw raw)
+{
+    return raw_info_get(raw)->type;
+}
+\f
+/* Updates the 'length' field of the OpenFlow message in 'buf' to
+ * 'buf->size'. */
+void
+ofpmsg_update_length(struct ofpbuf *buf)
+{
+    struct ofp_header *oh = ofpbuf_at_assert(buf, 0, sizeof *oh);
+    oh->length = htons(buf->size);
+}
+
+/* Returns just past the Openflow header (including the stats headers, vendor
+ * header, and any subtype header) in 'oh'. */
+const void *
+ofpmsg_body(const struct ofp_header *oh)
+{
+    struct ofphdrs hdrs;
+
+    ofphdrs_decode_assert(&hdrs, oh, ntohs(oh->length));
+    return (const uint8_t *) oh + ofphdrs_len(&hdrs);
+}
+\f
+static ovs_be16 *ofpmp_flags__(const struct ofp_header *);
+
+/* Initializes 'replies' as a new list of stats messages that reply to
+ * 'request', which must be a stats request message.  Initially the list will
+ * consist of only a single reply part without any body.  The caller should
+ * use calls to the other ofpmp_*() functions to add to the body and split the
+ * message into multiple parts, if necessary. */
+void
+ofpmp_init(struct list *replies, const struct ofp_header *request)
+{
+    struct ofpbuf *msg;
+
+    list_init(replies);
+
+    msg = ofpraw_alloc_stats_reply(request, 1000);
+    list_push_back(replies, &msg->list_node);
+}
+
+/* Prepares to append up to 'len' bytes to the series of statistics replies in
+ * 'replies', which should have been initialized with ofpmp_init(), if
+ * necessary adding a new reply to the list.
+ *
+ * Returns an ofpbuf with at least 'len' bytes of tailroom.  The 'len' bytes
+ * have not actually been allocated, so the caller must do so with
+ * e.g. ofpbuf_put_uninit(). */
+struct ofpbuf *
+ofpmp_reserve(struct list *replies, size_t len)
+{
+    struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+
+    if (msg->size + len <= UINT16_MAX) {
+        ofpbuf_prealloc_tailroom(msg, len);
+        return msg;
+    } else {
+        unsigned int hdrs_len;
+        struct ofpbuf *next;
+        struct ofphdrs hdrs;
+
+        ofphdrs_decode_assert(&hdrs, msg->data, msg->size);
+        hdrs_len = ofphdrs_len(&hdrs);
+
+        next = ofpbuf_new(MAX(1024, hdrs_len + len));
+        ofpbuf_put(next, msg->data, hdrs_len);
+        list_push_back(replies, &next->list_node);
+
+        *ofpmp_flags__(msg->data) |= htons(OFPSF_REPLY_MORE);
+
+        return next;
+    }
+}
+
+/* Appends 'len' bytes to the series of statistics replies in 'replies', and
+ * returns the first byte. */
+void *
+ofpmp_append(struct list *replies, size_t len)
+{
+    return ofpbuf_put_uninit(ofpmp_reserve(replies, len), len);
+}
+
+/* Sometimes, when composing stats replies, it's difficult to predict how long
+ * an individual reply chunk will be before actually encoding it into the reply
+ * buffer.  This function allows easy handling of this case: just encode the
+ * reply, then use this function to break the message into two pieces if it
+ * exceeds the OpenFlow message limit.
+ *
+ * In detail, if the final stats message in 'replies' is too long for OpenFlow,
+ * this function breaks it into two separate stats replies, the first one with
+ * the first 'start_ofs' bytes, the second one containing the bytes from that
+ * offset onward. */
+void
+ofpmp_postappend(struct list *replies, size_t start_ofs)
+{
+    struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+
+    assert(start_ofs <= UINT16_MAX);
+    if (msg->size > UINT16_MAX) {
+        size_t len = msg->size - start_ofs;
+        memcpy(ofpmp_append(replies, len),
+               (const uint8_t *) msg->data + start_ofs, len);
+        msg->size = start_ofs;
+    }
+}
+
+static ovs_be16 *
+ofpmp_flags__(const struct ofp_header *oh)
+{
+    switch ((enum ofp_version)oh->version) {
+    case OFP10_VERSION:
+        return &((struct ofp10_stats_msg *) oh)->flags;
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        return &((struct ofp11_stats_msg *) oh)->flags;
+    default:
+        NOT_REACHED();
+    }
+}
+
+/* Returns the OFPSF_* flags found in the OpenFlow stats header of 'oh', which
+ * must be an OpenFlow stats request or reply.
+ *
+ * (OFPSF_REPLY_MORE is the only defined flag.) */
+uint16_t
+ofpmp_flags(const struct ofp_header *oh)
+{
+    return ntohs(*ofpmp_flags__(oh));
+}
+
+/* Returns true if the OFPSF_REPLY_MORE flag is set in the OpenFlow stats
+ * header of 'oh', which must be an OpenFlow stats request or reply, false if
+ * it is not set. */
+bool
+ofpmp_more(const struct ofp_header *oh)
+{
+    return (ofpmp_flags(oh) & OFPSF_REPLY_MORE) != 0;
+}
+\f
+static void ofpmsgs_init(void);
+
+static const struct raw_info *
+raw_info_get(enum ofpraw raw)
+{
+    ofpmsgs_init();
+
+    assert(raw < ARRAY_SIZE(raw_infos));
+    return &raw_infos[raw];
+}
+
+static struct raw_instance *
+raw_instance_get(const struct raw_info *info, uint8_t version)
+{
+    assert(version >= info->min_version && version <= info->max_version);
+    return &info->instances[version - info->min_version];
+}
+
+static enum ofperr
+ofpraw_from_ofphdrs(enum ofpraw *raw, const struct ofphdrs *hdrs)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+    struct raw_instance *raw_hdrs;
+    uint32_t hash;
+
+    ofpmsgs_init();
+
+    hash = ofphdrs_hash(hdrs);
+    HMAP_FOR_EACH_WITH_HASH (raw_hdrs, hmap_node, hash, &raw_instance_map) {
+        if (ofphdrs_equal(hdrs, &raw_hdrs->hdrs)) {
+            *raw = raw_hdrs->raw;
+            return 0;
+        }
+    }
+
+    if (!VLOG_DROP_WARN(&rl)) {
+        struct ds s;
+
+        ds_init(&s);
+        ds_put_format(&s, "version %"PRIu8", type %"PRIu8,
+                      hdrs->version, hdrs->type);
+        if (ofphdrs_is_stat(hdrs)) {
+            ds_put_format(&s, ", stat %"PRIu16, hdrs->stat);
+        }
+        if (hdrs->vendor) {
+            ds_put_format(&s, ", vendor 0x%"PRIx32", subtype %"PRIu32,
+                          hdrs->vendor, hdrs->subtype);
+        }
+        VLOG_WARN("unknown OpenFlow message (%s)", ds_cstr(&s));
+        ds_destroy(&s);
+    }
+
+    return (hdrs->vendor ? OFPERR_OFPBRC_BAD_SUBTYPE
+            : ofphdrs_is_stat(hdrs) ? OFPERR_OFPBRC_BAD_STAT
+            : OFPERR_OFPBRC_BAD_TYPE);
+}
+
+static void
+ofpmsgs_init(void)
+{
+    const struct raw_info *info;
+
+    if (raw_instance_map.buckets) {
+        return;
+    }
+
+    hmap_init(&raw_instance_map);
+    for (info = raw_infos; info < &raw_infos[ARRAY_SIZE(raw_infos)]; info++)
+    {
+        int n_instances = info->max_version - info->min_version + 1;
+        struct raw_instance *inst;
+
+        for (inst = info->instances;
+             inst < &info->instances[n_instances];
+             inst++) {
+            inst->hdrs_len = ofphdrs_len(&inst->hdrs);
+            hmap_insert(&raw_instance_map, &inst->hmap_node,
+                        ofphdrs_hash(&inst->hdrs));
+        }
+    }
+}
diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h
new file mode 100644 (file)
index 0000000..752d12c
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFP_MSGS_H
+#define OFP_MSGS_H 1
+
+/* OpenFlow message headers abstraction.
+ *
+ * OpenFlow headers are unnecessarily complicated:
+ *
+ *   - Some messages with the same meaning were renumbered between 1.0 and 1.1.
+ *
+ *   - "Statistics" (aka multipart) messages have a different format from other
+ *     messages.
+ *
+ *   - The 1.0 header for statistics messages is an odd number of 32-bit words
+ *     long, leaving 64-bit quantities in the body misaligned.  The 1.1 header
+ *     for statistics added a padding word to fix this misalignment, although
+ *     many statistic message bodies did not change.
+ *
+ *   - Vendor-defined messages have an additional header but no standard way to
+ *     distinguish individual types of message within a given vendor.
+ *
+ * This file attempts to abstract out the differences between the various forms
+ * of headers.
+ */
+
+#include "openvswitch/types.h"
+#include "ofp-errors.h"
+#include "util.h"
+
+struct list;
+\f
+/* Raw identifiers for OpenFlow messages.
+ *
+ * Some OpenFlow messages with similar meanings have multiple variants across
+ * OpenFlow versions or vendor extensions.  Each variant has a different
+ * OFPRAW_* enumeration constant.  More specifically, if two messages have
+ * different types, different numbers, or different arguments, then they must
+ * have different OFPRAW_* values.
+ *
+ * The comments here must follow a stylized form because the "extract-ofp-msgs"
+ * program parses them at build time to generate data tables.  The syntax of
+ * each comment is:
+ *
+ *    type versions (number): arguments.
+ *
+ * where the syntax of each part is:
+ *
+ *    - type: One of OFPT (standard OpenFlow message), OFPST (standard OpenFlow
+ *      statistics message), NXT (Nicira extension message), or NXST (Nicira
+ *      extension statistics message).
+ *
+ *      As new vendors implement extensions it will make sense to expand the
+ *      dictionary of possible types.
+ *
+ *    - versions: The OpenFlow version or versions in which this message is
+ *      supported, e.g. "1.0" or "1.1" or "1.0+".
+ *
+ *    - number:
+ *         For OFPT, the 'type' in struct ofp_header.
+ *         For OFPST, the 'type' in struct ofp_stats_msg or ofp11_stats_msg.
+ *         For NXT, the 'subtype' in struct nicira_header.
+ *         For NXST, the 'subtype' in struct nicira10_stats_msg or
+ *           nicira11_stats_msg.
+ *
+ *    - arguments: The types of data that follow the OpenFlow headers (the
+ *      message "body").  This can be "void" if the message has no body.
+ *      Otherwise, it should be a comma-separated sequence of C types.  The
+ *      last type in the sequence can end with [] if the body ends in a
+ *      variable-length sequence.
+ *
+ *      The arguments are used to validate the lengths of messages when a
+ *      header is parsed.  Any message whose length isn't valid as a length of
+ *      the specified types will be rejected with OFPERR_OFPBRC_BAD_LEN.
+ *
+ *      A few OpenFlow messages, such as OFPT_PACKET_IN, intentionally end with
+ *      only part of a structure, up to some specified member.  The syntax "up
+ *      to <member>" indicates this, e.g. "struct ofp11_packet_in up to data".
+ */
+enum ofpraw {
+/* Standard messages. */
+
+    /* OFPT 1.0+ (0): uint8_t[]. */
+    OFPRAW_OFPT_HELLO,
+
+    /* OFPT 1.0+ (1): struct ofp_error_msg, uint8_t[]. */
+    OFPRAW_OFPT_ERROR,
+
+    /* OFPT 1.0+ (2): uint8_t[]. */
+    OFPRAW_OFPT_ECHO_REQUEST,
+
+    /* OFPT 1.0+ (3): uint8_t[]. */
+    OFPRAW_OFPT_ECHO_REPLY,
+
+    /* OFPT 1.0+ (5): void. */
+    OFPRAW_OFPT_FEATURES_REQUEST,
+
+    /* OFPT 1.0 (6): struct ofp_switch_features, struct ofp10_phy_port[]. */
+    OFPRAW_OFPT10_FEATURES_REPLY,
+    /* OFPT 1.1+ (6): struct ofp_switch_features, struct ofp11_port[]. */
+    OFPRAW_OFPT11_FEATURES_REPLY,
+
+    /* OFPT 1.0+ (7): void. */
+    OFPRAW_OFPT_GET_CONFIG_REQUEST,
+
+    /* OFPT 1.0+ (8): struct ofp_switch_config. */
+    OFPRAW_OFPT_GET_CONFIG_REPLY,
+
+    /* OFPT 1.0+ (9): struct ofp_switch_config. */
+    OFPRAW_OFPT_SET_CONFIG,
+
+    /* OFPT 1.0 (10): struct ofp_packet_in up to data, uint8_t[]. */
+    OFPRAW_OFPT10_PACKET_IN,
+    /* OFPT 1.1 (10): struct ofp11_packet_in up to data, uint8_t[]. */
+    OFPRAW_OFPT11_PACKET_IN,
+    /* OFPT 1.2 (10): struct ofp12_packet_in, uint8_t[]. */
+    OFPRAW_OFPT12_PACKET_IN,
+    /* NXT 1.0+ (17): struct nx_packet_in, uint8_t[]. */
+    OFPRAW_NXT_PACKET_IN,
+
+    /* OFPT 1.0 (11): struct ofp_flow_removed. */
+    OFPRAW_OFPT10_FLOW_REMOVED,
+    /* OFPT 1.1+ (11): struct ofp11_flow_removed, uint8_t[8][]. */
+    OFPRAW_OFPT11_FLOW_REMOVED,
+    /* NXT 1.0+ (14): struct nx_flow_removed, uint8_t[8][]. */
+    OFPRAW_NXT_FLOW_REMOVED,
+
+    /* OFPT 1.0 (12): struct ofp_port_status, struct ofp10_phy_port. */
+    OFPRAW_OFPT10_PORT_STATUS,
+    /* OFPT 1.1+ (12): struct ofp_port_status, struct ofp11_port. */
+    OFPRAW_OFPT11_PORT_STATUS,
+
+    /* OFPT 1.0 (13): struct ofp_packet_out, uint8_t[]. */
+    OFPRAW_OFPT10_PACKET_OUT,
+    /* OFPT 1.1+ (13): struct ofp11_packet_out, uint8_t[]. */
+    OFPRAW_OFPT11_PACKET_OUT,
+
+    /* OFPT 1.0 (14): struct ofp10_flow_mod, struct ofp_action_header[]. */
+    OFPRAW_OFPT10_FLOW_MOD,
+    /* OFPT 1.1+ (14): struct ofp11_flow_mod, struct ofp11_instruction[]. */
+    OFPRAW_OFPT11_FLOW_MOD,
+    /* NXT 1.0+ (13): struct nx_flow_mod, uint8_t[8][]. */
+    OFPRAW_NXT_FLOW_MOD,
+
+    /* OFPT 1.0 (15): struct ofp10_port_mod. */
+    OFPRAW_OFPT10_PORT_MOD,
+    /* OFPT 1.1+ (16): struct ofp11_port_mod. */
+    OFPRAW_OFPT11_PORT_MOD,
+
+    /* OFPT 1.0 (18): void. */
+    OFPRAW_OFPT10_BARRIER_REQUEST,
+    /* OFPT 1.1+ (20): void. */
+    OFPRAW_OFPT11_BARRIER_REQUEST,
+
+    /* OFPT 1.0 (19): void. */
+    OFPRAW_OFPT10_BARRIER_REPLY,
+    /* OFPT 1.1+ (21): void. */
+    OFPRAW_OFPT11_BARRIER_REPLY,
+
+/* Standard statistics. */
+
+    /* OFPST 1.0+ (0): void. */
+    OFPRAW_OFPST_DESC_REQUEST,
+
+    /* OFPST 1.0+ (0): struct ofp_desc_stats. */
+    OFPRAW_OFPST_DESC_REPLY,
+
+    /* OFPST 1.0 (1): struct ofp10_flow_stats_request. */
+    OFPRAW_OFPST10_FLOW_REQUEST,
+    /* OFPST 1.1+ (1): struct ofp11_flow_stats_request, uint8_t[8][]. */
+    OFPRAW_OFPST11_FLOW_REQUEST,
+    /* NXST 1.0 (0): struct nx_flow_stats_request, uint8_t[8][]. */
+    OFPRAW_NXST_FLOW_REQUEST,
+
+    /* OFPST 1.0 (1): uint8_t[]. */
+    OFPRAW_OFPST10_FLOW_REPLY,
+    /* OFPST 1.1+ (1): uint8_t[]. */
+    OFPRAW_OFPST11_FLOW_REPLY,
+    /* NXST 1.0 (0): uint8_t[]. */
+    OFPRAW_NXST_FLOW_REPLY,
+
+    /* OFPST 1.0 (2): struct ofp10_flow_stats_request. */
+    OFPRAW_OFPST10_AGGREGATE_REQUEST,
+    /* OFPST 1.1+ (2): struct ofp11_flow_stats_request, uint8_t[8][]. */
+    OFPRAW_OFPST11_AGGREGATE_REQUEST,
+    /* NXST 1.0 (1): struct nx_flow_stats_request, uint8_t[8][]. */
+    OFPRAW_NXST_AGGREGATE_REQUEST,
+
+    /* OFPST 1.0+ (2): struct ofp_aggregate_stats_reply. */
+    OFPRAW_OFPST_AGGREGATE_REPLY,
+    /* NXST 1.0 (1): struct ofp_aggregate_stats_reply. */
+    OFPRAW_NXST_AGGREGATE_REPLY,
+
+    /* OFPST 1.0-1.2 (3): void. */
+    OFPRAW_OFPST_TABLE_REQUEST,
+
+    /* OFPST 1.0 (3): struct ofp10_table_stats[]. */
+    OFPRAW_OFPST10_TABLE_REPLY,
+    /* OFPST 1.1 (3): struct ofp11_table_stats[]. */
+    OFPRAW_OFPST11_TABLE_REPLY,
+    /* OFPST 1.2 (3): struct ofp12_table_stats[]. */
+    OFPRAW_OFPST12_TABLE_REPLY,
+
+    /* OFPST 1.0 (4): struct ofp10_port_stats_request. */
+    OFPRAW_OFPST_PORT_REQUEST,
+
+    /* OFPST 1.0 (4): struct ofp10_port_stats[]. */
+    OFPRAW_OFPST_PORT_REPLY,
+
+    /* OFPST 1.0 (5): struct ofp10_queue_stats_request. */
+    OFPRAW_OFPST_QUEUE_REQUEST,
+
+    /* OFPST 1.0 (5): struct ofp10_queue_stats[]. */
+    OFPRAW_OFPST_QUEUE_REPLY,
+
+    /* OFPST 1.0 (13): void. */
+    OFPRAW_OFPST_PORT_DESC_REQUEST,
+
+    /* OFPST 1.0 (13): struct ofp10_phy_port[]. */
+    OFPRAW_OFPST_PORT_DESC_REPLY,
+
+/* Nicira extension messages.
+ *
+ * Nicira extensions that correspond to standard OpenFlow messages are listed
+ * alongside the standard versions above. */
+
+    /* NXT 1.0+ (10): struct nx_role_request. */
+    OFPRAW_NXT_ROLE_REQUEST,
+
+    /* NXT 1.0+ (11): struct nx_role_request. */
+    OFPRAW_NXT_ROLE_REPLY,
+
+    /* NXT 1.0+ (12): struct nx_set_flow_format. */
+    OFPRAW_NXT_SET_FLOW_FORMAT,
+
+    /* NXT 1.0+ (15): struct nx_flow_mod_table_id. */
+    OFPRAW_NXT_FLOW_MOD_TABLE_ID,
+
+    /* NXT 1.0+ (16): struct nx_set_packet_in_format. */
+    OFPRAW_NXT_SET_PACKET_IN_FORMAT,
+
+    /* NXT 1.0+ (18): void. */
+    OFPRAW_NXT_FLOW_AGE,
+
+    /* NXT 1.0+ (19): struct nx_async_config. */
+    OFPRAW_NXT_SET_ASYNC_CONFIG,
+
+    /* NXT 1.0+ (20): struct nx_controller_id. */
+    OFPRAW_NXT_SET_CONTROLLER_ID,
+
+    /* NXT 1.0+ (21): struct nx_flow_monitor_cancel. */
+    OFPRAW_NXT_FLOW_MONITOR_CANCEL,
+
+    /* NXT 1.0+ (22): void. */
+    OFPRAW_NXT_FLOW_MONITOR_PAUSED,
+
+    /* NXT 1.0+ (23): void. */
+    OFPRAW_NXT_FLOW_MONITOR_RESUMED,
+
+/* Nicira extension statistics.
+ *
+ * Nicira extension statistics that correspond to standard OpenFlow statistics
+ * are listed alongside the standard versions above. */
+
+    /* NXST 1.0 (2): uint8_t[8][]. */
+    OFPRAW_NXST_FLOW_MONITOR_REQUEST,
+
+    /* NXST 1.0 (2): uint8_t[8][]. */
+    OFPRAW_NXST_FLOW_MONITOR_REPLY,
+};
+
+/* Decoding messages into OFPRAW_* values. */
+enum ofperr ofpraw_decode(enum ofpraw *, const struct ofp_header *);
+enum ofperr ofpraw_pull(enum ofpraw *, struct ofpbuf *);
+enum ofpraw ofpraw_pull_assert(struct ofpbuf *);
+
+enum ofperr ofpraw_decode_partial(enum ofpraw *,
+                                  const struct ofp_header *, size_t length);
+
+/* Encoding messages using OFPRAW_* values. */
+struct ofpbuf *ofpraw_alloc(enum ofpraw, uint8_t ofp_version,
+                            size_t extra_tailroom);
+struct ofpbuf *ofpraw_alloc_xid(enum ofpraw, uint8_t ofp_version,
+                                ovs_be32 xid, size_t extra_tailroom);
+struct ofpbuf *ofpraw_alloc_reply(enum ofpraw,
+                                  const struct ofp_header *request,
+                                  size_t extra_tailroom);
+struct ofpbuf *ofpraw_alloc_stats_reply(const struct ofp_header *request,
+                                        size_t extra_tailroom);
+
+void ofpraw_put(enum ofpraw, uint8_t ofp_version, struct ofpbuf *);
+void ofpraw_put_xid(enum ofpraw, uint8_t ofp_version, ovs_be32 xid,
+                    struct ofpbuf *);
+void ofpraw_put_reply(enum ofpraw, const struct ofp_header *request,
+                      struct ofpbuf *);
+void ofpraw_put_stats_reply(const struct ofp_header *request, struct ofpbuf *);
+
+/* Information about OFPRAW_* values. */
+const char *ofpraw_get_name(enum ofpraw);
+enum ofpraw ofpraw_stats_request_to_reply(enum ofpraw, uint8_t version);
+\f
+/* Semantic identifiers for OpenFlow messages.
+ *
+ * Each OFPTYPE_* enumeration constant represents one or more concrete format
+ * of OpenFlow message.  When two variants of a message have essentially the
+ * same meaning, they are assigned a single OFPTYPE_* value.
+ *
+ * The comments here must follow a stylized form because the "extract-ofp-msgs"
+ * program parses them at build time to generate data tables.  The format is
+ * simply to list each OFPRAW_* enumeration constant for a given OFPTYPE_*,
+ * each followed by a period. */
+enum ofptype {
+    /* Immutable messages. */
+    OFPTYPE_HELLO,               /* OFPRAW_OFPT_HELLO. */
+    OFPTYPE_ERROR,               /* OFPRAW_OFPT_ERROR. */
+    OFPTYPE_ECHO_REQUEST,        /* OFPRAW_OFPT_ECHO_REQUEST. */
+    OFPTYPE_ECHO_REPLY,          /* OFPRAW_OFPT_ECHO_REPLY. */
+
+    /* Switch configuration messages. */
+    OFPTYPE_FEATURES_REQUEST,    /* OFPRAW_OFPT_FEATURES_REQUEST. */
+    OFPTYPE_FEATURES_REPLY,      /* OFPRAW_OFPT10_FEATURES_REPLY.
+                                  * OFPRAW_OFPT11_FEATURES_REPLY. */
+    OFPTYPE_GET_CONFIG_REQUEST,  /* OFPRAW_OFPT_GET_CONFIG_REQUEST. */
+    OFPTYPE_GET_CONFIG_REPLY,    /* OFPRAW_OFPT_GET_CONFIG_REPLY. */
+    OFPTYPE_SET_CONFIG,          /* OFPRAW_OFPT_SET_CONFIG. */
+
+    /* Asynchronous messages. */
+    OFPTYPE_PACKET_IN,           /* OFPRAW_OFPT10_PACKET_IN.
+                                  * OFPRAW_OFPT11_PACKET_IN.
+                                  * OFPRAW_OFPT12_PACKET_IN.
+                                  * OFPRAW_NXT_PACKET_IN. */
+    OFPTYPE_FLOW_REMOVED,        /* OFPRAW_OFPT10_FLOW_REMOVED.
+                                  * OFPRAW_OFPT11_FLOW_REMOVED.
+                                  * OFPRAW_NXT_FLOW_REMOVED. */
+    OFPTYPE_PORT_STATUS,         /* OFPRAW_OFPT10_PORT_STATUS.
+                                  * OFPRAW_OFPT11_PORT_STATUS. */
+
+    /* Controller command messages. */
+    OFPTYPE_PACKET_OUT,          /* OFPRAW_OFPT10_PACKET_OUT.
+                                  * OFPRAW_OFPT11_PACKET_OUT. */
+    OFPTYPE_FLOW_MOD,            /* OFPRAW_OFPT10_FLOW_MOD.
+                                  * OFPRAW_OFPT11_FLOW_MOD.
+                                  * OFPRAW_NXT_FLOW_MOD. */
+    OFPTYPE_PORT_MOD,            /* OFPRAW_OFPT10_PORT_MOD.
+                                  * OFPRAW_OFPT11_PORT_MOD. */
+
+    /* Barrier messages. */
+    OFPTYPE_BARRIER_REQUEST,     /* OFPRAW_OFPT10_BARRIER_REQUEST.
+                                  * OFPRAW_OFPT11_BARRIER_REQUEST. */
+    OFPTYPE_BARRIER_REPLY,       /* OFPRAW_OFPT10_BARRIER_REPLY.
+                                  * OFPRAW_OFPT11_BARRIER_REPLY. */
+
+    /* Statistics. */
+    OFPTYPE_DESC_STATS_REQUEST,      /* OFPRAW_OFPST_DESC_REQUEST. */
+    OFPTYPE_DESC_STATS_REPLY,        /* OFPRAW_OFPST_DESC_REPLY. */
+    OFPTYPE_FLOW_STATS_REQUEST,      /* OFPRAW_OFPST10_FLOW_REQUEST.
+                                      * OFPRAW_OFPST11_FLOW_REQUEST.
+                                      * OFPRAW_NXST_FLOW_REQUEST. */
+    OFPTYPE_FLOW_STATS_REPLY,        /* OFPRAW_OFPST10_FLOW_REPLY.
+                                      * OFPRAW_OFPST11_FLOW_REPLY.
+                                      * OFPRAW_NXST_FLOW_REPLY. */
+    OFPTYPE_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST10_AGGREGATE_REQUEST.
+                                      * OFPRAW_OFPST11_AGGREGATE_REQUEST.
+                                      * OFPRAW_NXST_AGGREGATE_REQUEST. */
+    OFPTYPE_AGGREGATE_STATS_REPLY,   /* OFPRAW_OFPST_AGGREGATE_REPLY.
+                                      * OFPRAW_NXST_AGGREGATE_REPLY. */
+    OFPTYPE_TABLE_STATS_REQUEST,     /* OFPRAW_OFPST_TABLE_REQUEST. */
+    OFPTYPE_TABLE_STATS_REPLY,       /* OFPRAW_OFPST10_TABLE_REPLY.
+                                      * OFPRAW_OFPST11_TABLE_REPLY.
+                                      * OFPRAW_OFPST12_TABLE_REPLY. */
+    OFPTYPE_PORT_STATS_REQUEST,      /* OFPRAW_OFPST_PORT_REQUEST. */
+    OFPTYPE_PORT_STATS_REPLY,        /* OFPRAW_OFPST_PORT_REPLY. */
+    OFPTYPE_QUEUE_STATS_REQUEST,     /* OFPRAW_OFPST_QUEUE_REQUEST. */
+    OFPTYPE_QUEUE_STATS_REPLY,       /* OFPRAW_OFPST_QUEUE_REPLY. */
+    OFPTYPE_PORT_DESC_STATS_REQUEST, /* OFPRAW_OFPST_PORT_DESC_REQUEST. */
+    OFPTYPE_PORT_DESC_STATS_REPLY,   /* OFPRAW_OFPST_PORT_DESC_REPLY. */
+
+    /* Nicira extensions. */
+    OFPTYPE_ROLE_REQUEST,         /* OFPRAW_NXT_ROLE_REQUEST. */
+    OFPTYPE_ROLE_REPLY,           /* OFPRAW_NXT_ROLE_REPLY. */
+    OFPTYPE_SET_FLOW_FORMAT,      /* OFPRAW_NXT_SET_FLOW_FORMAT. */
+    OFPTYPE_FLOW_MOD_TABLE_ID,    /* OFPRAW_NXT_FLOW_MOD_TABLE_ID. */
+    OFPTYPE_SET_PACKET_IN_FORMAT, /* OFPRAW_NXT_SET_PACKET_IN_FORMAT. */
+    OFPTYPE_FLOW_AGE,             /* OFPRAW_NXT_FLOW_AGE. */
+    OFPTYPE_SET_ASYNC_CONFIG,     /* OFPRAW_NXT_SET_ASYNC_CONFIG. */
+    OFPTYPE_SET_CONTROLLER_ID,    /* OFPRAW_NXT_SET_CONTROLLER_ID. */
+
+    /* Flow monitor extension. */
+    OFPTYPE_FLOW_MONITOR_STATS_REQUEST, /* OFPRAW_NXST_FLOW_MONITOR_REQUEST. */
+    OFPTYPE_FLOW_MONITOR_STATS_REPLY,   /* OFPRAW_NXST_FLOW_MONITOR_REPLY. */
+    OFPTYPE_FLOW_MONITOR_CANCEL,        /* OFPRAW_NXT_FLOW_MONITOR_CANCEL. */
+    OFPTYPE_FLOW_MONITOR_PAUSED,        /* OFPRAW_NXT_FLOW_MONITOR_PAUSED. */
+    OFPTYPE_FLOW_MONITOR_RESUMED,       /* OFPRAW_NXT_FLOW_MONITOR_RESUMED. */
+};
+
+/* Decoding messages into OFPTYPE_* values. */
+enum ofperr ofptype_decode(enum ofptype *, const struct ofp_header *);
+enum ofperr ofptype_pull(enum ofptype *, struct ofpbuf *);
+enum ofptype ofptype_from_ofpraw(enum ofpraw);
+\f
+/* OpenFlow message properties. */
+void ofpmsg_update_length(struct ofpbuf *);
+const void *ofpmsg_body(const struct ofp_header *);
+\f
+/* Multipart messages (aka "statistics").
+ *
+ * Individual OpenFlow messages are limited to 64 kB in size, but some messages
+ * need to be longer.  Therefore, multipart messages allow a longer message to
+ * be divided into multiple parts at some convenient boundary.  For example,
+ * limiting the response to a "flow dump" request to 64 kB would unreasonably
+ * limit the maximum number of flows in an OpenFlow switch, so a "flow dump" is
+ * expressed as a multipart request/reply pair, with the reply broken into
+ * pieces between flows.
+ *
+ * Multipart messages always consist of a request/reply pair.
+ *
+ * In OpenFlow 1.0, 1.1, and 1.2, requests must always fit in a single message,
+ * that is, only a multipart reply may have more than one part.  OpenFlow 1.3
+ * adds one multipart request.  This code does not yet support multipart
+ * requests. */
+
+/* Encoding multipart replies.
+ *
+ * These functions are useful for multipart replies that might really require
+ * more than one message.  A multipart message that is known in advance to fit
+ * within 64 kB doesn't need any special treatment, so you might as well use
+ * the ofpraw_alloc_*() functions.
+ *
+ * These functions work with a "struct list" of "struct ofpbuf"s, each of
+ * which represents one part of a multipart message. */
+void ofpmp_init(struct list *, const struct ofp_header *request);
+struct ofpbuf *ofpmp_reserve(struct list *, size_t len);
+void *ofpmp_append(struct list *, size_t len);
+void ofpmp_postappend(struct list *, size_t start_ofs);
+
+/* Decoding multipart replies. */
+uint16_t ofpmp_flags(const struct ofp_header *);
+bool ofpmp_more(const struct ofp_header *);
+
+#endif /* ofp-msgs.h */
index 3f7a021..0d904b1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "dynamic-string.h"
 #include "learn.h"
 #include "meta-flow.h"
-#include "netdev.h"
 #include "multipath.h"
+#include "netdev.h"
 #include "nx-match.h"
+#include "ofp-actions.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
@@ -41,6 +42,9 @@
 
 VLOG_DEFINE_THIS_MODULE(ofp_parse);
 
+static void ofp_fatal(const char *flow, bool verbose, const char *format, ...)
+    NO_RETURN;
+
 static uint8_t
 str_to_table_id(const char *str)
 {
@@ -119,107 +123,73 @@ str_to_ip(const char *str, ovs_be32 *ip)
     *ip = in_addr.s_addr;
 }
 
-static struct ofp_action_output *
-put_output_action(struct ofpbuf *b, uint16_t port)
-{
-    struct ofp_action_output *oao;
-
-    oao = ofputil_put_OFPAT10_OUTPUT(b);
-    oao->port = htons(port);
-    return oao;
-}
-
 static void
-parse_enqueue(struct ofpbuf *b, char *arg)
+parse_enqueue(char *arg, struct ofpbuf *ofpacts)
 {
     char *sp = NULL;
     char *port = strtok_r(arg, ":q", &sp);
     char *queue = strtok_r(NULL, "", &sp);
-    struct ofp_action_enqueue *oae;
+    struct ofpact_enqueue *enqueue;
 
     if (port == NULL || queue == NULL) {
         ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
     }
 
-    oae = ofputil_put_OFPAT10_ENQUEUE(b);
-    oae->port = htons(str_to_u32(port));
-    oae->queue_id = htonl(str_to_u32(queue));
+    enqueue = ofpact_put_ENQUEUE(ofpacts);
+    enqueue->port = str_to_u32(port);
+    enqueue->queue = str_to_u32(queue);
 }
 
 static void
-parse_output(struct ofpbuf *b, char *arg)
+parse_output(char *arg, struct ofpbuf *ofpacts)
 {
     if (strchr(arg, '[')) {
-        struct nx_action_output_reg *naor;
-        struct mf_subfield src;
-
-        mf_parse_subfield(&src, arg);
+        struct ofpact_output_reg *output_reg;
 
-        naor = ofputil_put_NXAST_OUTPUT_REG(b);
-        naor->ofs_nbits = nxm_encode_ofs_nbits(src.ofs, src.n_bits);
-        naor->src = htonl(src.field->nxm_header);
-        naor->max_len = htons(UINT16_MAX);
+        output_reg = ofpact_put_OUTPUT_REG(ofpacts);
+        mf_parse_subfield(&output_reg->src, arg);
+        output_reg->max_len = UINT16_MAX;
     } else {
-        put_output_action(b, str_to_u32(arg));
+        struct ofpact_output *output;
+
+        output = ofpact_put_OUTPUT(ofpacts);
+        output->port = str_to_u32(arg);
+        output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
     }
 }
 
 static void
-parse_resubmit(struct ofpbuf *b, char *arg)
+parse_resubmit(char *arg, struct ofpbuf *ofpacts)
 {
-    struct nx_action_resubmit *nar;
+    struct ofpact_resubmit *resubmit;
     char *in_port_s, *table_s;
-    uint16_t in_port;
-    uint8_t table;
+
+    resubmit = ofpact_put_RESUBMIT(ofpacts);
 
     in_port_s = strsep(&arg, ",");
     if (in_port_s && in_port_s[0]) {
-        if (!ofputil_port_from_string(in_port_s, &in_port)) {
-            in_port = str_to_u32(in_port_s);
+        if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) {
+            resubmit->in_port = str_to_u32(in_port_s);
         }
     } else {
-        in_port = OFPP_IN_PORT;
+        resubmit->in_port = OFPP_IN_PORT;
     }
 
     table_s = strsep(&arg, ",");
-    table = table_s && table_s[0] ? str_to_u32(table_s) : 255;
+    resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255;
 
-    if (in_port == OFPP_IN_PORT && table == 255) {
+    if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) {
         ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified "
                   " on resubmit");
     }
-
-    if (in_port != OFPP_IN_PORT && table == 255) {
-        nar = ofputil_put_NXAST_RESUBMIT(b);
-    } else {
-        nar = ofputil_put_NXAST_RESUBMIT_TABLE(b);
-        nar->table = table;
-    }
-    nar->in_port = htons(in_port);
 }
 
 static void
-parse_set_tunnel(struct ofpbuf *b, const char *arg)
+parse_note(const char *arg, struct ofpbuf *ofpacts)
 {
-    uint64_t tun_id = str_to_u64(arg);
-    if (tun_id > UINT32_MAX) {
-        ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(tun_id);
-    } else {
-        ofputil_put_NXAST_SET_TUNNEL(b)->tun_id = htonl(tun_id);
-    }
-}
-
-static void
-parse_note(struct ofpbuf *b, const char *arg)
-{
-    size_t start_ofs = b->size;
-    struct nx_action_note *nan;
-    int remainder;
-    size_t len;
+    struct ofpact_note *note;
 
-    nan = ofputil_put_NXAST_NOTE(b);
-
-    b->size -= sizeof nan->note;
+    note = ofpact_put_NOTE(ofpacts);
     while (*arg != '\0') {
         uint8_t byte;
         bool ok;
@@ -235,32 +205,27 @@ parse_note(struct ofpbuf *b, const char *arg)
         if (!ok) {
             ovs_fatal(0, "bad hex digit in `note' argument");
         }
-        ofpbuf_put(b, &byte, 1);
+        ofpbuf_put(ofpacts, &byte, 1);
 
-        arg += 2;
-    }
+        note = ofpacts->l2;
+        note->length++;
 
-    len = b->size - start_ofs;
-    remainder = len % OFP_ACTION_ALIGN;
-    if (remainder) {
-        ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder);
+        arg += 2;
     }
-    nan = (struct nx_action_note *)((char *)b->data + start_ofs);
-    nan->len = htons(b->size - start_ofs);
+    ofpact_update_len(ofpacts, &note->ofpact);
 }
 
 static void
 parse_fin_timeout(struct ofpbuf *b, char *arg)
 {
-    struct nx_action_fin_timeout *naft;
+    struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b);
     char *key, *value;
 
-    naft = ofputil_put_NXAST_FIN_TIMEOUT(b);
     while (ofputil_parse_key_value(&arg, &key, &value)) {
         if (!strcmp(key, "idle_timeout")) {
-            naft->fin_idle_timeout = htons(str_to_u16(value, key));
+            oft->fin_idle_timeout = str_to_u16(value, key);
         } else if (!strcmp(key, "hard_timeout")) {
-            naft->fin_hard_timeout = htons(str_to_u16(value, key));
+            oft->fin_hard_timeout = str_to_u16(value, key);
         } else {
             ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key);
         }
@@ -298,148 +263,218 @@ parse_controller(struct ofpbuf *b, char *arg)
     }
 
     if (reason == OFPR_ACTION && controller_id == 0) {
-        put_output_action(b, OFPP_CONTROLLER)->max_len = htons(max_len);
+        struct ofpact_output *output;
+
+        output = ofpact_put_OUTPUT(b);
+        output->port = OFPP_CONTROLLER;
+        output->max_len = max_len;
+    } else {
+        struct ofpact_controller *controller;
+
+        controller = ofpact_put_CONTROLLER(b);
+        controller->max_len = max_len;
+        controller->reason = reason;
+        controller->controller_id = controller_id;
+    }
+}
+
+static void
+parse_dec_ttl(struct ofpbuf *b, char *arg)
+{
+    struct ofpact_cnt_ids *ids;
+
+    ids = ofpact_put_DEC_TTL(b);
+
+    if (*arg == '\0') {
+        uint16_t id = 0;
+
+        ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
+        ofpbuf_put(b, &id, sizeof id);
+        ids = b->l2;
+        ids->n_controllers++;
     } else {
-        struct nx_action_controller *nac;
+        char *cntr;
+
+        ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS;
+        for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL;
+             cntr = strtok_r(NULL, ", ", &arg)) {
+            uint16_t id = atoi(cntr);
+
+            ofpbuf_put(b, &id, sizeof id);
+            ids = b->l2;
+            ids->n_controllers++;
+        }
+        if (!ids->n_controllers) {
+            ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller "
+                      "id.");
+        }
 
-        nac = ofputil_put_NXAST_CONTROLLER(b);
-        nac->max_len = htons(max_len);
-        nac->reason = reason;
-        nac->controller_id = htons(controller_id);
     }
+    ofpact_update_len(b, &ids->ofpact);
 }
 
 static void
 parse_named_action(enum ofputil_action_code code, const struct flow *flow,
-                   struct ofpbuf *b, char *arg)
+                   char *arg, struct ofpbuf *ofpacts)
 {
-    struct ofp_action_dl_addr *oada;
-    struct ofp_action_vlan_pcp *oavp;
-    struct ofp_action_vlan_vid *oavv;
-    struct ofp_action_nw_addr *oana;
-    struct ofp_action_tp_port *oata;
+    struct ofpact_tunnel *tunnel;
+    uint16_t vid;
+    ovs_be32 ip;
+    uint8_t pcp;
+    uint8_t tos;
 
     switch (code) {
+    case OFPUTIL_ACTION_INVALID:
+        NOT_REACHED();
+
     case OFPUTIL_OFPAT10_OUTPUT:
-        parse_output(b, arg);
+    case OFPUTIL_OFPAT11_OUTPUT:
+        parse_output(arg, ofpacts);
         break;
 
     case OFPUTIL_OFPAT10_SET_VLAN_VID:
-        oavv = ofputil_put_OFPAT10_SET_VLAN_VID(b);
-        oavv->vlan_vid = htons(str_to_u32(arg));
+    case OFPUTIL_OFPAT11_SET_VLAN_VID:
+        vid = str_to_u32(arg);
+        if (vid & ~VLAN_VID_MASK) {
+            ovs_fatal(0, "%s: not a valid VLAN VID", arg);
+        }
+        ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid;
         break;
 
     case OFPUTIL_OFPAT10_SET_VLAN_PCP:
-        oavp = ofputil_put_OFPAT10_SET_VLAN_PCP(b);
-        oavp->vlan_pcp = str_to_u32(arg);
+    case OFPUTIL_OFPAT11_SET_VLAN_PCP:
+        pcp = str_to_u32(arg);
+        if (pcp & ~7) {
+            ovs_fatal(0, "%s: not a valid VLAN PCP", arg);
+        }
+        ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
         break;
 
     case OFPUTIL_OFPAT10_STRIP_VLAN:
-        ofputil_put_OFPAT10_STRIP_VLAN(b);
+        ofpact_put_STRIP_VLAN(ofpacts);
         break;
 
     case OFPUTIL_OFPAT10_SET_DL_SRC:
+    case OFPUTIL_OFPAT11_SET_DL_SRC:
+        str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
+        break;
+
     case OFPUTIL_OFPAT10_SET_DL_DST:
-        oada = ofputil_put_action(code, b);
-        str_to_mac(arg, oada->dl_addr);
+    case OFPUTIL_OFPAT11_SET_DL_DST:
+        str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac);
         break;
 
     case OFPUTIL_OFPAT10_SET_NW_SRC:
+    case OFPUTIL_OFPAT11_SET_NW_SRC:
+        str_to_ip(arg, &ip);
+        ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip;
+        break;
+
     case OFPUTIL_OFPAT10_SET_NW_DST:
-        oana = ofputil_put_action(code, b);
-        str_to_ip(arg, &oana->nw_addr);
+    case OFPUTIL_OFPAT11_SET_NW_DST:
+        str_to_ip(arg, &ip);
+        ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip;
         break;
 
     case OFPUTIL_OFPAT10_SET_NW_TOS:
-        ofputil_put_OFPAT10_SET_NW_TOS(b)->nw_tos = str_to_u32(arg);
+    case OFPUTIL_OFPAT11_SET_NW_TOS:
+        tos = str_to_u32(arg);
+        if (tos & ~IP_DSCP_MASK) {
+            ovs_fatal(0, "%s: not a valid TOS", arg);
+        }
+        ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos;
         break;
 
     case OFPUTIL_OFPAT10_SET_TP_SRC:
+    case OFPUTIL_OFPAT11_SET_TP_SRC:
+        ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg);
+        break;
+
     case OFPUTIL_OFPAT10_SET_TP_DST:
-        oata = ofputil_put_action(code, b);
-        oata->tp_port = htons(str_to_u32(arg));
+    case OFPUTIL_OFPAT11_SET_TP_DST:
+        ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg);
         break;
 
     case OFPUTIL_OFPAT10_ENQUEUE:
-        parse_enqueue(b, arg);
+        parse_enqueue(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_RESUBMIT:
-        parse_resubmit(b, arg);
+        parse_resubmit(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_SET_TUNNEL:
-        parse_set_tunnel(b, arg);
+    case OFPUTIL_NXAST_SET_TUNNEL64:
+        tunnel = ofpact_put_SET_TUNNEL(ofpacts);
+        tunnel->ofpact.compat = code;
+        tunnel->tun_id = str_to_u64(arg);
         break;
 
     case OFPUTIL_NXAST_SET_QUEUE:
-        ofputil_put_NXAST_SET_QUEUE(b)->queue_id = htonl(str_to_u32(arg));
+        ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
         break;
 
     case OFPUTIL_NXAST_POP_QUEUE:
-        ofputil_put_NXAST_POP_QUEUE(b);
+        ofpact_put_POP_QUEUE(ofpacts);
         break;
 
     case OFPUTIL_NXAST_REG_MOVE:
-        nxm_parse_reg_move(ofputil_put_NXAST_REG_MOVE(b), arg);
+        nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg);
         break;
 
     case OFPUTIL_NXAST_REG_LOAD:
-        nxm_parse_reg_load(ofputil_put_NXAST_REG_LOAD(b), arg);
+        nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg);
         break;
 
     case OFPUTIL_NXAST_NOTE:
-        parse_note(b, arg);
-        break;
-
-    case OFPUTIL_NXAST_SET_TUNNEL64:
-        ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(str_to_u64(arg));
+        parse_note(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_MULTIPATH:
-        multipath_parse(ofputil_put_NXAST_MULTIPATH(b), arg);
+        multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
         break;
 
-    case OFPUTIL_NXAST_AUTOPATH:
-        autopath_parse(ofputil_put_NXAST_AUTOPATH(b), arg);
+    case OFPUTIL_NXAST_AUTOPATH__DEPRECATED:
+        autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg);
         break;
 
     case OFPUTIL_NXAST_BUNDLE:
-        bundle_parse(b, arg);
+        bundle_parse(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_BUNDLE_LOAD:
-        bundle_parse_load(b, arg);
+        bundle_parse_load(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_RESUBMIT_TABLE:
     case OFPUTIL_NXAST_OUTPUT_REG:
+    case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
         NOT_REACHED();
 
     case OFPUTIL_NXAST_LEARN:
-        learn_parse(b, arg, flow);
+        learn_parse(arg, flow, ofpacts);
         break;
 
     case OFPUTIL_NXAST_EXIT:
-        ofputil_put_NXAST_EXIT(b);
+        ofpact_put_EXIT(ofpacts);
         break;
 
     case OFPUTIL_NXAST_DEC_TTL:
-        ofputil_put_NXAST_DEC_TTL(b);
+        parse_dec_ttl(ofpacts, arg);
         break;
 
     case OFPUTIL_NXAST_FIN_TIMEOUT:
-        parse_fin_timeout(b, arg);
+        parse_fin_timeout(ofpacts, arg);
         break;
 
     case OFPUTIL_NXAST_CONTROLLER:
-        parse_controller(b, arg);
+        parse_controller(ofpacts, arg);
         break;
     }
 }
 
 static void
-str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
+str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
 {
     char *pos, *act, *arg;
     int n_actions;
@@ -452,10 +487,8 @@ str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
 
         code = ofputil_action_code_from_name(act);
         if (code >= 0) {
-            parse_named_action(code, flow, b, arg);
+            parse_named_action(code, flow, arg, ofpacts);
         } else if (!strcasecmp(act, "drop")) {
-            /* A drop action in OpenFlow occurs by just not setting
-             * an action. */
             if (n_actions) {
                 ovs_fatal(0, "Drop actions must not be preceded by other "
                           "actions");
@@ -465,12 +498,13 @@ str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
             }
             break;
         } else if (ofputil_port_from_string(act, &port)) {
-            put_output_action(b, port);
+            ofpact_put_OUTPUT(ofpacts)->port = port;
         } else {
             ovs_fatal(0, "Unknown action: %s", act);
         }
         n_actions++;
     }
+    ofpact_pad(ofpacts);
 }
 
 struct protocol {
@@ -589,6 +623,12 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
     fm->cookie = htonll(0);
     fm->cookie_mask = htonll(0);
+    if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) {
+        /* For modify, by default, don't update the cookie. */
+        fm->new_cookie = htonll(UINT64_MAX);
+    } else{
+        fm->new_cookie = htonll(0);
+    }
     fm->table_id = 0xff;
     fm->command = command;
     fm->idle_timeout = OFP_FLOW_PERMANENT;
@@ -643,17 +683,24 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                 fm->hard_timeout = str_to_u16(value, name);
             } else if (!strcmp(name, "cookie")) {
                 char *mask = strchr(value, '/');
+
                 if (mask) {
+                    /* A mask means we're searching for a cookie. */
                     if (command == OFPFC_ADD) {
                         ofp_fatal(str_, verbose, "flow additions cannot use "
                                   "a cookie mask");
                     }
                     *mask = '\0';
+                    fm->cookie = htonll(str_to_u64(value));
                     fm->cookie_mask = htonll(str_to_u64(mask+1));
                 } else {
-                    fm->cookie_mask = htonll(UINT64_MAX);
+                    /* No mask means that the cookie is being set. */
+                    if (command != OFPFC_ADD && command != OFPFC_MODIFY
+                            && command != OFPFC_MODIFY_STRICT) {
+                        ofp_fatal(str_, verbose, "cannot set cookie");
+                    }
+                    fm->new_cookie = htonll(str_to_u64(value));
                 }
-                fm->cookie = htonll(str_to_u64(value));
             } else if (mf_from_name(name)) {
                 parse_field(mf_from_name(name), value, &fm->cr);
             } else if (!strcmp(name, "duration")
@@ -667,31 +714,100 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             }
         }
     }
+    if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX)
+            && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) {
+        /* On modifies without a mask, we are supposed to add a flow if
+         * one does not exist.  If a cookie wasn't been specified, use a
+         * default of zero. */
+        fm->new_cookie = htonll(0);
+    }
     if (fields & F_ACTIONS) {
-        struct ofpbuf actions;
+        struct ofpbuf ofpacts;
 
-        ofpbuf_init(&actions, sizeof(union ofp_action));
-        str_to_action(&fm->cr.flow, act_str, &actions);
-        fm->actions = ofpbuf_steal_data(&actions);
-        fm->n_actions = actions.size / sizeof(union ofp_action);
+        ofpbuf_init(&ofpacts, 32);
+        str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts);
+        fm->ofpacts_len = ofpacts.size;
+        fm->ofpacts = ofpbuf_steal_data(&ofpacts);
     } else {
-        fm->actions = NULL;
-        fm->n_actions = 0;
+        fm->ofpacts_len = 0;
+        fm->ofpacts = NULL;
     }
 
     free(string);
 }
 
+/* Convert 'str_' (as described in the documentation for the "monitor" command
+ * in the ovs-ofctl man page) into 'fmr'. */
+void
+parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
+                           const char *str_)
+{
+    static uint32_t id;
+
+    char *string = xstrdup(str_);
+    char *save_ptr = NULL;
+    char *name;
+
+    fmr->id = id++;
+    fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY
+                  | NXFMF_OWN | NXFMF_ACTIONS);
+    fmr->out_port = OFPP_NONE;
+    fmr->table_id = 0xff;
+    cls_rule_init_catchall(&fmr->match, 0);
+
+    for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+        const struct protocol *p;
+
+        if (!strcmp(name, "!initial")) {
+            fmr->flags &= ~NXFMF_INITIAL;
+        } else if (!strcmp(name, "!add")) {
+            fmr->flags &= ~NXFMF_ADD;
+        } else if (!strcmp(name, "!delete")) {
+            fmr->flags &= ~NXFMF_DELETE;
+        } else if (!strcmp(name, "!modify")) {
+            fmr->flags &= ~NXFMF_MODIFY;
+        } else if (!strcmp(name, "!actions")) {
+            fmr->flags &= ~NXFMF_ACTIONS;
+        } else if (!strcmp(name, "!own")) {
+            fmr->flags &= ~NXFMF_OWN;
+        } else if (parse_protocol(name, &p)) {
+            cls_rule_set_dl_type(&fmr->match, htons(p->dl_type));
+            if (p->nw_proto) {
+                cls_rule_set_nw_proto(&fmr->match, p->nw_proto);
+            }
+        } else {
+            char *value;
+
+            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+            if (!value) {
+                ovs_fatal(0, "%s: field %s missing value", str_, name);
+            }
+
+            if (!strcmp(name, "table")) {
+                fmr->table_id = str_to_table_id(value);
+            } else if (!strcmp(name, "out_port")) {
+                fmr->out_port = atoi(value);
+            } else if (mf_from_name(name)) {
+                parse_field(mf_from_name(name), value, &fmr->match);
+            } else {
+                ovs_fatal(0, "%s: unknown keyword %s", str_, name);
+            }
+        }
+    }
+    free(string);
+}
+
 /* Parses 's' as a set of OpenFlow actions and appends the actions to
  * 'actions'.
  *
  * Prints an error on stderr and aborts the program if 's' syntax is
  * invalid. */
 void
-parse_ofp_actions(const char *s_, struct ofpbuf *actions)
+parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
 {
     char *s = xstrdup(s_);
-    str_to_action(NULL, s, actions);
+    str_to_ofpacts(NULL, s, ofpacts);
     free(s);
 }
 
@@ -755,3 +871,80 @@ parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
     fsr->out_port = fm.out_port;
     fsr->table_id = fm.table_id;
 }
+
+/* Parses a specification of a flow from 's' into 'flow'.  's' must take the
+ * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a
+ * mf_field.  Fields must be specified in a natural order for satisfying
+ * prerequisites.
+ *
+ * Returns NULL on success, otherwise a malloc()'d string that explains the
+ * problem. */
+char *
+parse_ofp_exact_flow(struct flow *flow, const char *s)
+{
+    char *pos, *key, *value_s;
+    char *error = NULL;
+    char *copy;
+
+    memset(flow, 0, sizeof *flow);
+
+    pos = copy = xstrdup(s);
+    while (ofputil_parse_key_value(&pos, &key, &value_s)) {
+        const struct protocol *p;
+        if (parse_protocol(key, &p)) {
+            if (flow->dl_type) {
+                error = xasprintf("%s: Ethernet type set multiple times", s);
+                goto exit;
+            }
+            flow->dl_type = htons(p->dl_type);
+
+            if (p->nw_proto) {
+                if (flow->nw_proto) {
+                    error = xasprintf("%s: network protocol set "
+                                      "multiple times", s);
+                    goto exit;
+                }
+                flow->nw_proto = p->nw_proto;
+            }
+        } else {
+            const struct mf_field *mf;
+            union mf_value value;
+            char *field_error;
+
+            mf = mf_from_name(key);
+            if (!mf) {
+                error = xasprintf("%s: unknown field %s", s, key);
+                goto exit;
+            }
+
+            if (!mf_are_prereqs_ok(mf, flow)) {
+                error = xasprintf("%s: prerequisites not met for setting %s",
+                                  s, key);
+                goto exit;
+            }
+
+            if (!mf_is_zero(mf, flow)) {
+                error = xasprintf("%s: field %s set multiple times", s, key);
+                goto exit;
+            }
+
+            field_error = mf_parse_value(mf, value_s, &value);
+            if (field_error) {
+                error = xasprintf("%s: bad value for %s (%s)",
+                                  s, key, field_error);
+                free(field_error);
+                goto exit;
+            }
+
+            mf_set_flow_value(mf, &value, flow);
+        }
+    }
+
+exit:
+    free(copy);
+
+    if (error) {
+        memset(flow, 0, sizeof *flow);
+    }
+    return error;
+}
index c552caa..d2d3c3c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <stdint.h>
 #include <stdio.h>
 
+struct flow;
 struct ofpbuf;
 struct ofputil_flow_mod;
+struct ofputil_flow_monitor_request;
 struct ofputil_flow_stats_request;
 
 void parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
@@ -39,6 +41,11 @@ void parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
                                       bool aggregate, const char *string);
 
 
-void parse_ofp_actions(const char *, struct ofpbuf *actions);
+void parse_ofpacts(const char *, struct ofpbuf *ofpacts);
+
+char *parse_ofp_exact_flow(struct flow *, const char *);
+
+void parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
+                                const char *);
 
 #endif /* ofp-parse.h */
index 23dadd9..99e6456 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -36,7 +36,9 @@
 #include "meta-flow.h"
 #include "netdev.h"
 #include "nx-match.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
+#include "ofp-msgs.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
@@ -106,19 +108,17 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
     ds_put_format(string, " total_len=%"PRIu16" in_port=", pin.total_len);
     ofputil_format_port(pin.fmd.in_port, string);
 
-    if (pin.fmd.tun_id_mask) {
+    if (pin.fmd.tun_id != htonll(0)) {
         ds_put_format(string, " tun_id=0x%"PRIx64, ntohll(pin.fmd.tun_id));
-        if (pin.fmd.tun_id_mask != htonll(UINT64_MAX)) {
-            ds_put_format(string, "/0x%"PRIx64, ntohll(pin.fmd.tun_id_mask));
-        }
+    }
+
+    if (pin.fmd.metadata != htonll(0)) {
+        ds_put_format(string, " metadata=0x%"PRIx64, ntohll(pin.fmd.metadata));
     }
 
     for (i = 0; i < FLOW_N_REGS; i++) {
-        if (pin.fmd.reg_masks[i]) {
+        if (pin.fmd.regs[i]) {
             ds_put_format(string, " reg%d=0x%"PRIx32, i, pin.fmd.regs[i]);
-            if (pin.fmd.reg_masks[i] != UINT32_MAX) {
-                ds_put_format(string, "/0x%"PRIx32, pin.fmd.reg_masks[i]);
-            }
         }
     }
 
@@ -147,275 +147,17 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
 }
 
 static void
-print_note(struct ds *string, const struct nx_action_note *nan)
-{
-    size_t len;
-    size_t i;
-
-    ds_put_cstr(string, "note:");
-    len = ntohs(nan->len) - offsetof(struct nx_action_note, note);
-    for (i = 0; i < len; i++) {
-        if (i) {
-            ds_put_char(string, '.');
-        }
-        ds_put_format(string, "%02"PRIx8, nan->note[i]);
-    }
-}
-
-static void
-ofp_print_action(struct ds *s, const union ofp_action *a,
-                 enum ofputil_action_code code)
-{
-    const struct ofp_action_enqueue *oae;
-    const struct ofp_action_dl_addr *oada;
-    const struct nx_action_set_tunnel64 *nast64;
-    const struct nx_action_set_tunnel *nast;
-    const struct nx_action_set_queue *nasq;
-    const struct nx_action_resubmit *nar;
-    const struct nx_action_reg_move *move;
-    const struct nx_action_reg_load *load;
-    const struct nx_action_multipath *nam;
-    const struct nx_action_autopath *naa;
-    const struct nx_action_output_reg *naor;
-    const struct nx_action_fin_timeout *naft;
-    const struct nx_action_controller *nac;
-    struct mf_subfield subfield;
-    uint16_t port;
-
-    switch (code) {
-    case OFPUTIL_OFPAT10_OUTPUT:
-        port = ntohs(a->output.port);
-        if (port < OFPP_MAX) {
-            ds_put_format(s, "output:%"PRIu16, port);
-        } else {
-            ofputil_format_port(port, s);
-            if (port == OFPP_CONTROLLER) {
-                if (a->output.max_len != htons(0)) {
-                    ds_put_format(s, ":%"PRIu16, ntohs(a->output.max_len));
-                } else {
-                    ds_put_cstr(s, ":all");
-                }
-            }
-        }
-        break;
-
-    case OFPUTIL_OFPAT10_ENQUEUE:
-        oae = (const struct ofp_action_enqueue *) a;
-        ds_put_format(s, "enqueue:");
-        ofputil_format_port(ntohs(oae->port), s);
-        ds_put_format(s, "q%"PRIu32, ntohl(oae->queue_id));
-        break;
-
-    case OFPUTIL_OFPAT10_SET_VLAN_VID:
-        ds_put_format(s, "mod_vlan_vid:%"PRIu16,
-                      ntohs(a->vlan_vid.vlan_vid));
-        break;
-
-    case OFPUTIL_OFPAT10_SET_VLAN_PCP:
-        ds_put_format(s, "mod_vlan_pcp:%"PRIu8, a->vlan_pcp.vlan_pcp);
-        break;
-
-    case OFPUTIL_OFPAT10_STRIP_VLAN:
-        ds_put_cstr(s, "strip_vlan");
-        break;
-
-    case OFPUTIL_OFPAT10_SET_DL_SRC:
-        oada = (const struct ofp_action_dl_addr *) a;
-        ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
-                      ETH_ADDR_ARGS(oada->dl_addr));
-        break;
-
-    case OFPUTIL_OFPAT10_SET_DL_DST:
-        oada = (const struct ofp_action_dl_addr *) a;
-        ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT,
-                      ETH_ADDR_ARGS(oada->dl_addr));
-        break;
-
-    case OFPUTIL_OFPAT10_SET_NW_SRC:
-        ds_put_format(s, "mod_nw_src:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr));
-        break;
-
-    case OFPUTIL_OFPAT10_SET_NW_DST:
-        ds_put_format(s, "mod_nw_dst:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr));
-        break;
-
-    case OFPUTIL_OFPAT10_SET_NW_TOS:
-        ds_put_format(s, "mod_nw_tos:%d", a->nw_tos.nw_tos);
-        break;
-
-    case OFPUTIL_OFPAT10_SET_TP_SRC:
-        ds_put_format(s, "mod_tp_src:%d", ntohs(a->tp_port.tp_port));
-        break;
-
-    case OFPUTIL_OFPAT10_SET_TP_DST:
-        ds_put_format(s, "mod_tp_dst:%d", ntohs(a->tp_port.tp_port));
-        break;
-
-    case OFPUTIL_NXAST_RESUBMIT:
-        nar = (struct nx_action_resubmit *)a;
-        ds_put_format(s, "resubmit:");
-        ofputil_format_port(ntohs(nar->in_port), s);
-        break;
-
-    case OFPUTIL_NXAST_RESUBMIT_TABLE:
-        nar = (struct nx_action_resubmit *)a;
-        ds_put_format(s, "resubmit(");
-        if (nar->in_port != htons(OFPP_IN_PORT)) {
-            ofputil_format_port(ntohs(nar->in_port), s);
-        }
-        ds_put_char(s, ',');
-        if (nar->table != 255) {
-            ds_put_format(s, "%"PRIu8, nar->table);
-        }
-        ds_put_char(s, ')');
-        break;
-
-    case OFPUTIL_NXAST_SET_TUNNEL:
-        nast = (struct nx_action_set_tunnel *)a;
-        ds_put_format(s, "set_tunnel:%#"PRIx32, ntohl(nast->tun_id));
-        break;
-
-    case OFPUTIL_NXAST_SET_QUEUE:
-        nasq = (struct nx_action_set_queue *)a;
-        ds_put_format(s, "set_queue:%u", ntohl(nasq->queue_id));
-        break;
-
-    case OFPUTIL_NXAST_POP_QUEUE:
-        ds_put_cstr(s, "pop_queue");
-        break;
-
-    case OFPUTIL_NXAST_NOTE:
-        print_note(s, (const struct nx_action_note *) a);
-        break;
-
-    case OFPUTIL_NXAST_REG_MOVE:
-        move = (const struct nx_action_reg_move *) a;
-        nxm_format_reg_move(move, s);
-        break;
-
-    case OFPUTIL_NXAST_REG_LOAD:
-        load = (const struct nx_action_reg_load *) a;
-        nxm_format_reg_load(load, s);
-        break;
-
-    case OFPUTIL_NXAST_SET_TUNNEL64:
-        nast64 = (const struct nx_action_set_tunnel64 *) a;
-        ds_put_format(s, "set_tunnel64:%#"PRIx64,
-                      ntohll(nast64->tun_id));
-        break;
-
-    case OFPUTIL_NXAST_MULTIPATH:
-        nam = (const struct nx_action_multipath *) a;
-        multipath_format(nam, s);
-        break;
-
-    case OFPUTIL_NXAST_AUTOPATH:
-        naa = (const struct nx_action_autopath *)a;
-        ds_put_format(s, "autopath(%u,", ntohl(naa->id));
-        nxm_decode(&subfield, naa->dst, naa->ofs_nbits);
-        mf_format_subfield(&subfield, s);
-        ds_put_char(s, ')');
-        break;
-
-    case OFPUTIL_NXAST_BUNDLE:
-    case OFPUTIL_NXAST_BUNDLE_LOAD:
-        bundle_format((const struct nx_action_bundle *) a, s);
-        break;
-
-    case OFPUTIL_NXAST_OUTPUT_REG:
-        naor = (const struct nx_action_output_reg *) a;
-        ds_put_cstr(s, "output:");
-        nxm_decode(&subfield, naor->src, naor->ofs_nbits);
-        mf_format_subfield(&subfield, s);
-        break;
-
-    case OFPUTIL_NXAST_LEARN:
-        learn_format((const struct nx_action_learn *) a, s);
-        break;
-
-    case OFPUTIL_NXAST_DEC_TTL:
-        ds_put_cstr(s, "dec_ttl");
-        break;
-
-    case OFPUTIL_NXAST_EXIT:
-        ds_put_cstr(s, "exit");
-        break;
-
-    case OFPUTIL_NXAST_FIN_TIMEOUT:
-        naft = (const struct nx_action_fin_timeout *) a;
-        ds_put_cstr(s, "fin_timeout(");
-        if (naft->fin_idle_timeout) {
-            ds_put_format(s, "idle_timeout=%"PRIu16",",
-                          ntohs(naft->fin_idle_timeout));
-        }
-        if (naft->fin_hard_timeout) {
-            ds_put_format(s, "hard_timeout=%"PRIu16",",
-                          ntohs(naft->fin_hard_timeout));
-        }
-        ds_chomp(s, ',');
-        ds_put_char(s, ')');
-        break;
-
-    case OFPUTIL_NXAST_CONTROLLER:
-        nac = (const struct nx_action_controller *) a;
-        ds_put_cstr(s, "controller(");
-        if (nac->reason != OFPR_ACTION) {
-            ds_put_format(s, "reason=%s,",
-                          ofputil_packet_in_reason_to_string(nac->reason));
-        }
-        if (nac->max_len != htons(UINT16_MAX)) {
-            ds_put_format(s, "max_len=%"PRIu16",", ntohs(nac->max_len));
-        }
-        if (nac->controller_id != htons(0)) {
-            ds_put_format(s, "id=%"PRIu16",", ntohs(nac->controller_id));
-        }
-        ds_chomp(s, ',');
-        ds_put_char(s, ')');
-        break;
-
-    default:
-        break;
-    }
-}
-
-void
-ofp_print_actions(struct ds *string, const union ofp_action *actions,
-                  size_t n_actions)
-{
-    const union ofp_action *a;
-    size_t left;
-
-    ds_put_cstr(string, "actions=");
-    if (!n_actions) {
-        ds_put_cstr(string, "drop");
-    }
-
-    OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
-        int code = ofputil_decode_action(a);
-        if (code >= 0) {
-            if (a != actions) {
-                ds_put_cstr(string, ",");
-            }
-            ofp_print_action(string, a, code);
-        } else {
-            ofp_print_error(string, -code);
-        }
-    }
-    if (left > 0) {
-        ds_put_format(string, " ***%zu leftover bytes following actions",
-                      left * sizeof *a);
-    }
-}
-
-static void
-ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
+ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
                      int verbosity)
 {
     struct ofputil_packet_out po;
+    struct ofpbuf ofpacts;
     enum ofperr error;
 
-    error = ofputil_decode_packet_out(&po, opo);
+    ofpbuf_init(&ofpacts, 64);
+    error = ofputil_decode_packet_out(&po, oh, &ofpacts);
     if (error) {
+        ofpbuf_uninit(&ofpacts);
         ofp_print_error(string, error);
         return;
     }
@@ -424,7 +166,7 @@ ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
     ofputil_format_port(po.in_port, string);
 
     ds_put_char(string, ' ');
-    ofp_print_actions(string, po.actions, po.n_actions);
+    ofpacts_format(po.ofpacts, po.ofpacts_len, string);
 
     if (po.buffer_id == UINT32_MAX) {
         ds_put_format(string, " data_len=%zu", po.packet_len);
@@ -438,6 +180,8 @@ ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
         ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id);
     }
     ds_put_char(string, '\n');
+
+    ofpbuf_uninit(&ofpacts);
 }
 
 /* qsort comparison function. */
@@ -454,7 +198,8 @@ compare_ports(const void *a_, const void *b_)
 
 static void
 ofp_print_bit_names(struct ds *string, uint32_t bits,
-                    const char *(*bit_to_name)(uint32_t bit))
+                    const char *(*bit_to_name)(uint32_t bit),
+                    char separator)
 {
     int n = 0;
     int i;
@@ -471,7 +216,7 @@ ofp_print_bit_names(struct ds *string, uint32_t bits,
             const char *name = bit_to_name(bit);
             if (name) {
                 if (n++) {
-                    ds_put_char(string, ' ');
+                    ds_put_char(string, separator);
                 }
                 ds_put_cstr(string, name);
                 bits &= ~bit;
@@ -480,8 +225,8 @@ ofp_print_bit_names(struct ds *string, uint32_t bits,
     }
 
     if (bits) {
-        if (n++) {
-            ds_put_char(string, ' ');
+        if (n) {
+            ds_put_char(string, separator);
         }
         ds_put_format(string, "0x%"PRIx32, bits);
     }
@@ -517,7 +262,7 @@ netdev_feature_to_name(uint32_t bit)
 static void
 ofp_print_port_features(struct ds *string, enum netdev_features features)
 {
-    ofp_print_bit_names(string, features, netdev_feature_to_name);
+    ofp_print_bit_names(string, features, netdev_feature_to_name, ' ');
     ds_put_char(string, '\n');
 }
 
@@ -542,7 +287,7 @@ ofputil_port_config_to_name(uint32_t bit)
 static void
 ofp_print_port_config(struct ds *string, enum ofputil_port_config config)
 {
-    ofp_print_bit_names(string, config, ofputil_port_config_to_name);
+    ofp_print_bit_names(string, config, ofputil_port_config_to_name, ' ');
     ds_put_char(string, '\n');
 }
 
@@ -587,10 +332,11 @@ ofp_print_port_state(struct ds *string, enum ofputil_port_state state)
                      : "STP_BLOCK"));
         state &= ~OFPUTIL_PS_STP_MASK;
         if (state) {
-            ofp_print_bit_names(string, state, ofputil_port_state_to_name);
+            ofp_print_bit_names(string, state, ofputil_port_state_to_name,
+                                ' ');
         }
     } else {
-        ofp_print_bit_names(string, state, ofputil_port_state_to_name);
+        ofp_print_bit_names(string, state, ofputil_port_state_to_name, ' ');
     }
     ds_put_char(string, '\n');
 }
@@ -642,6 +388,37 @@ ofp_print_phy_port(struct ds *string, const struct ofputil_phy_port *port)
                   port->max_speed / UINT32_C(1000));
 }
 
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', writes a detailed description of each port into
+ * 'string'. */
+static void
+ofp_print_phy_ports(struct ds *string, uint8_t ofp_version,
+                    struct ofpbuf *b)
+{
+    size_t n_ports;
+    struct ofputil_phy_port *ports;
+    enum ofperr error;
+    size_t i;
+
+    n_ports = ofputil_count_phy_ports(ofp_version, b);
+
+    ports = xmalloc(n_ports * sizeof *ports);
+    for (i = 0; i < n_ports; i++) {
+        error = ofputil_pull_phy_port(ofp_version, b, &ports[i]);
+        if (error) {
+            ofp_print_error(string, error);
+            goto exit;
+        }
+    }
+    qsort(ports, n_ports, sizeof *ports, compare_ports);
+    for (i = 0; i < n_ports; i++) {
+        ofp_print_phy_port(string, &ports[i]);
+    }
+
+exit:
+    free(ports);
+}
+
 static const char *
 ofputil_capabilities_to_name(uint32_t bit)
 {
@@ -656,6 +433,7 @@ ofputil_capabilities_to_name(uint32_t bit)
     case OFPUTIL_C_ARP_MATCH_IP: return "ARP_MATCH_IP";
     case OFPUTIL_C_STP:          return "STP";
     case OFPUTIL_C_GROUP_STATS:  return "GROUP_STATS";
+    case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED";
     }
 
     return NULL;
@@ -700,17 +478,13 @@ ofputil_action_bitmap_to_name(uint32_t bit)
 }
 
 static void
-ofp_print_switch_features(struct ds *string,
-                          const struct ofp_switch_features *osf)
+ofp_print_switch_features(struct ds *string, const struct ofp_header *oh)
 {
     struct ofputil_switch_features features;
-    struct ofputil_phy_port *ports;
     enum ofperr error;
     struct ofpbuf b;
-    size_t n_ports;
-    size_t i;
 
-    error = ofputil_decode_switch_features(osf, &features, &b);
+    error = ofputil_decode_switch_features(oh, &features, &b);
     if (error) {
         ofp_print_error(string, error);
         return;
@@ -722,29 +496,24 @@ ofp_print_switch_features(struct ds *string,
 
     ds_put_cstr(string, "capabilities: ");
     ofp_print_bit_names(string, features.capabilities,
-                        ofputil_capabilities_to_name);
-    ds_put_char(string, '\n');
-
-    ds_put_cstr(string, "actions: ");
-    ofp_print_bit_names(string, features.actions,
-                        ofputil_action_bitmap_to_name);
+                        ofputil_capabilities_to_name, ' ');
     ds_put_char(string, '\n');
 
-    n_ports = ofputil_count_phy_ports(osf);
-
-    ports = xmalloc(n_ports * sizeof *ports);
-    for (i = 0; i < n_ports; i++) {
-        error = ofputil_pull_switch_features_port(&b, &ports[i]);
-        if (error) {
-            ofp_print_error(string, error);
-            return;
-        }
-    }
-    qsort(ports, n_ports, sizeof *ports, compare_ports);
-    for (i = 0; i < n_ports; i++) {
-        ofp_print_phy_port(string, &ports[i]);
+    switch ((enum ofp_version)oh->version) {
+    case OFP10_VERSION:
+        ds_put_cstr(string, "actions: ");
+        ofp_print_bit_names(string, features.actions,
+                            ofputil_action_bitmap_to_name, ' ');
+        ds_put_char(string, '\n');
+        break;
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        break;
+    default:
+        NOT_REACHED();
     }
-    free(ports);
+
+    ofp_print_phy_ports(string, oh->version, &b);
 }
 
 static void
@@ -812,25 +581,25 @@ print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
 }
 
 void
-ofp_print_match(struct ds *f, const struct ofp_match *om, int verbosity)
+ofp10_match_print(struct ds *f, const struct ofp10_match *om, int verbosity)
 {
-    char *s = ofp_match_to_string(om, verbosity);
+    char *s = ofp10_match_to_string(om, verbosity);
     ds_put_cstr(f, s);
     free(s);
 }
 
 char *
-ofp_match_to_string(const struct ofp_match *om, int verbosity)
+ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
 {
     struct ds f = DS_EMPTY_INITIALIZER;
     uint32_t w = ntohl(om->wildcards);
     bool skip_type = false;
     bool skip_proto = false;
 
-    if (!(w & OFPFW_DL_TYPE)) {
+    if (!(w & OFPFW10_DL_TYPE)) {
         skip_type = true;
         if (om->dl_type == htons(ETH_TYPE_IP)) {
-            if (!(w & OFPFW_NW_PROTO)) {
+            if (!(w & OFPFW10_NW_PROTO)) {
                 skip_proto = true;
                 if (om->nw_proto == IPPROTO_ICMP) {
                     ds_put_cstr(&f, "icmp,");
@@ -851,44 +620,46 @@ ofp_match_to_string(const struct ofp_match *om, int verbosity)
             skip_type = false;
         }
     }
-    print_wild(&f, "in_port=", w & OFPFW_IN_PORT, verbosity,
+    print_wild(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
                "%d", ntohs(om->in_port));
-    print_wild(&f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
+    print_wild(&f, "dl_vlan=", w & OFPFW10_DL_VLAN, verbosity,
                "%d", ntohs(om->dl_vlan));
-    print_wild(&f, "dl_vlan_pcp=", w & OFPFW_DL_VLAN_PCP, verbosity,
+    print_wild(&f, "dl_vlan_pcp=", w & OFPFW10_DL_VLAN_PCP, verbosity,
                "%d", om->dl_vlan_pcp);
-    print_wild(&f, "dl_src=", w & OFPFW_DL_SRC, verbosity,
+    print_wild(&f, "dl_src=", w & OFPFW10_DL_SRC, verbosity,
                ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
-    print_wild(&f, "dl_dst=", w & OFPFW_DL_DST, verbosity,
+    print_wild(&f, "dl_dst=", w & OFPFW10_DL_DST, verbosity,
                ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
     if (!skip_type) {
-        print_wild(&f, "dl_type=", w & OFPFW_DL_TYPE, verbosity,
+        print_wild(&f, "dl_type=", w & OFPFW10_DL_TYPE, verbosity,
                    "0x%04x", ntohs(om->dl_type));
     }
     print_ip_netmask(&f, "nw_src=", om->nw_src,
-                     (w & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT, verbosity);
+                     (w & OFPFW10_NW_SRC_MASK) >> OFPFW10_NW_SRC_SHIFT,
+                     verbosity);
     print_ip_netmask(&f, "nw_dst=", om->nw_dst,
-                     (w & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT, verbosity);
+                     (w & OFPFW10_NW_DST_MASK) >> OFPFW10_NW_DST_SHIFT,
+                     verbosity);
     if (!skip_proto) {
         if (om->dl_type == htons(ETH_TYPE_ARP)) {
-            print_wild(&f, "arp_op=", w & OFPFW_NW_PROTO, verbosity,
+            print_wild(&f, "arp_op=", w & OFPFW10_NW_PROTO, verbosity,
                        "%u", om->nw_proto);
         } else {
-            print_wild(&f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity,
+            print_wild(&f, "nw_proto=", w & OFPFW10_NW_PROTO, verbosity,
                        "%u", om->nw_proto);
         }
     }
-    print_wild(&f, "nw_tos=", w & OFPFW_NW_TOS, verbosity,
+    print_wild(&f, "nw_tos=", w & OFPFW10_NW_TOS, verbosity,
                "%u", om->nw_tos);
     if (om->nw_proto == IPPROTO_ICMP) {
-        print_wild(&f, "icmp_type=", w & OFPFW_ICMP_TYPE, verbosity,
+        print_wild(&f, "icmp_type=", w & OFPFW10_ICMP_TYPE, verbosity,
                    "%d", ntohs(om->tp_src));
-        print_wild(&f, "icmp_code=", w & OFPFW_ICMP_CODE, verbosity,
+        print_wild(&f, "icmp_code=", w & OFPFW10_ICMP_CODE, verbosity,
                    "%d", ntohs(om->tp_dst));
     } else {
-        print_wild(&f, "tp_src=", w & OFPFW_TP_SRC, verbosity,
+        print_wild(&f, "tp_src=", w & OFPFW10_TP_SRC, verbosity,
                    "%d", ntohs(om->tp_src));
-        print_wild(&f, "tp_dst=", w & OFPFW_TP_DST, verbosity,
+        print_wild(&f, "tp_dst=", w & OFPFW10_TP_DST, verbosity,
                    "%d", ntohs(om->tp_dst));
     }
     if (ds_last(&f) == ',') {
@@ -898,15 +669,22 @@ ofp_match_to_string(const struct ofp_match *om, int verbosity)
 }
 
 static void
-ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
-                   enum ofputil_msg_code code, int verbosity)
+ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
 {
     struct ofputil_flow_mod fm;
+    struct ofpbuf ofpacts;
     bool need_priority;
     enum ofperr error;
+    enum ofpraw raw;
+    enum ofputil_protocol protocol;
+
+    protocol = ofputil_protocol_from_ofp_version(oh->version);
+    protocol = ofputil_protocol_set_tid(protocol, true);
 
-    error = ofputil_decode_flow_mod(&fm, oh, OFPUTIL_P_OF10_TID);
+    ofpbuf_init(&ofpacts, 64);
+    error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts);
     if (error) {
+        ofpbuf_uninit(&ofpacts);
         ofp_print_error(s, error);
         return;
     }
@@ -936,14 +714,15 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
     }
 
     ds_put_char(s, ' ');
-    if (verbosity >= 3 && code == OFPUTIL_OFPT_FLOW_MOD) {
-        const struct ofp_flow_mod *ofm = (const struct ofp_flow_mod *) oh;
-        ofp_print_match(s, &ofm->match, verbosity);
+    ofpraw_decode(&raw, oh);
+    if (verbosity >= 3 && raw == OFPRAW_OFPT10_FLOW_MOD) {
+        const struct ofp10_flow_mod *ofm = ofpmsg_body(oh);
+        ofp10_match_print(s, &ofm->match, verbosity);
 
         /* ofp_print_match() doesn't print priority. */
         need_priority = true;
-    } else if (verbosity >= 3 && code == OFPUTIL_NXT_FLOW_MOD) {
-        const struct nx_flow_mod *nfm = (const struct nx_flow_mod *) oh;
+    } else if (verbosity >= 3 && raw == OFPRAW_NXT_FLOW_MOD) {
+        const struct nx_flow_mod *nfm = ofpmsg_body(oh);
         const void *nxm = nfm + 1;
         char *nxm_s;
 
@@ -963,8 +742,12 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
     if (ds_last(s) != ' ') {
         ds_put_char(s, ' ');
     }
-    if (fm.cookie != htonll(0)) {
-        ds_put_format(s, "cookie:0x%"PRIx64" ", ntohll(fm.cookie));
+    if (fm.new_cookie != htonll(0)) {
+        ds_put_format(s, "cookie:0x%"PRIx64" ", ntohll(fm.new_cookie));
+    }
+    if (fm.cookie_mask != htonll(0)) {
+        ds_put_format(s, "cookie:0x%"PRIx64"/0x%"PRIx64" ",
+                ntohll(fm.cookie), ntohll(fm.cookie_mask));
     }
     if (fm.idle_timeout != OFP_FLOW_PERMANENT) {
         ds_put_format(s, "idle:%"PRIu16" ", fm.idle_timeout);
@@ -978,6 +761,11 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
     if (fm.buffer_id != UINT32_MAX) {
         ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id);
     }
+    if (fm.out_port != OFPP_NONE) {
+        ds_put_format(s, "out_port:");
+        ofputil_format_port(fm.out_port, s);
+        ds_put_char(s, ' ');
+    }
     if (fm.flags != 0) {
         uint16_t flags = fm.flags;
 
@@ -987,17 +775,18 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
         if (flags & OFPFF_CHECK_OVERLAP) {
             ds_put_cstr(s, "check_overlap ");
         }
-        if (flags & OFPFF_EMERG) {
+        if (flags & OFPFF10_EMERG) {
             ds_put_cstr(s, "emerg ");
         }
 
-        flags &= ~(OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF_EMERG);
+        flags &= ~(OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF10_EMERG);
         if (flags) {
             ds_put_format(s, "flags:0x%"PRIx16" ", flags);
         }
     }
 
-    ofp_print_actions(s, fm.actions, fm.n_actions);
+    ofpacts_format(fm.ofpacts, fm.ofpacts_len, s);
+    ofpbuf_uninit(&ofpacts);
 }
 
 static void
@@ -1027,6 +816,8 @@ ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason)
         return "delete";
     case OFPRR_GROUP_DELETE:
         return "group_delete";
+    case OFPRR_EVICTION:
+        return "eviction";
     default:
         sprintf(s, "%d", (int) reason);
         return s;
@@ -1056,8 +847,14 @@ ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
     }
     ds_put_cstr(string, " duration");
     ofp_print_duration(string, fr.duration_sec, fr.duration_nsec);
-    ds_put_format(string, " idle%"PRIu16" pkts%"PRIu64" bytes%"PRIu64"\n",
-         fr.idle_timeout, fr.packet_count, fr.byte_count);
+    ds_put_format(string, " idle%"PRIu16, fr.idle_timeout);
+    if (fr.hard_timeout) {
+        /* The hard timeout was only added in OF1.2, so only print it if it is
+         * actually in use to avoid gratuitous change to the formatting. */
+        ds_put_format(string, " hard%"PRIu16, fr.hard_timeout);
+    }
+    ds_put_format(string, " pkts%"PRIu64" bytes%"PRIu64"\n",
+                  fr.packet_count, fr.byte_count);
 }
 
 static void
@@ -1099,41 +896,38 @@ ofp_print_error(struct ds *string, enum ofperr error)
 }
 
 static void
-ofp_print_error_msg(struct ds *string, const struct ofp_error_msg *oem)
+ofp_print_error_msg(struct ds *string, const struct ofp_header *oh)
 {
-    size_t len = ntohs(oem->header.length);
-    size_t payload_ofs, payload_len;
-    const void *payload;
+    size_t len = ntohs(oh->length);
+    struct ofpbuf payload;
     enum ofperr error;
     char *s;
 
-    error = ofperr_decode_msg(&oem->header, &payload_ofs);
+    error = ofperr_decode_msg(oh, &payload);
     if (!error) {
         ds_put_cstr(string, "***decode error***");
-        ds_put_hex_dump(string, oem->data, len - sizeof *oem, 0, true);
+        ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true);
         return;
     }
 
     ds_put_format(string, " %s\n", ofperr_get_name(error));
 
-    payload = (const uint8_t *) oem + payload_ofs;
-    payload_len = len - payload_ofs;
     if (error == OFPERR_OFPHFC_INCOMPATIBLE || error == OFPERR_OFPHFC_EPERM) {
-        ds_put_printable(string, payload, payload_len);
+        ds_put_printable(string, payload.data, payload.size);
     } else {
-        s = ofp_to_string(payload, payload_len, 1);
+        s = ofp_to_string(payload.data, payload.size, 1);
         ds_put_cstr(string, s);
         free(s);
     }
 }
 
 static void
-ofp_print_port_status(struct ds *string, const struct ofp_port_status *ops)
+ofp_print_port_status(struct ds *string, const struct ofp_header *oh)
 {
     struct ofputil_port_status ps;
     enum ofperr error;
 
-    error = ofputil_decode_port_status(ops, &ps);
+    error = ofputil_decode_port_status(oh, &ps);
     if (error) {
         ofp_print_error(string, error);
         return;
@@ -1151,8 +945,10 @@ ofp_print_port_status(struct ds *string, const struct ofp_port_status *ops)
 }
 
 static void
-ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_desc_stats *ods)
+ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_header *oh)
 {
+    const struct ofp_desc_stats *ods = ofpmsg_body(oh);
+
     ds_put_char(string, '\n');
     ds_put_format(string, "Manufacturer: %.*s\n",
             (int) sizeof ods->mfr_desc, ods->mfr_desc);
@@ -1167,13 +963,12 @@ ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_desc_stats *ods)
 }
 
 static void
-ofp_print_flow_stats_request(struct ds *string,
-                             const struct ofp_stats_msg *osm)
+ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh)
 {
     struct ofputil_flow_stats_request fsr;
     enum ofperr error;
 
-    error = ofputil_decode_flow_stats_request(&fsr, &osm->header);
+    error = ofputil_decode_flow_stats_request(&fsr, oh);
     if (error) {
         ofp_print_error(string, error);
         return;
@@ -1196,71 +991,77 @@ ofp_print_flow_stats_request(struct ds *string,
     cls_rule_format(&fsr.match, string);
 }
 
+void
+ofp_print_flow_stats(struct ds *string, struct ofputil_flow_stats *fs)
+{
+    ds_put_format(string, " cookie=0x%"PRIx64", duration=",
+                  ntohll(fs->cookie));
+
+    ofp_print_duration(string, fs->duration_sec, fs->duration_nsec);
+    ds_put_format(string, ", table=%"PRIu8", ", fs->table_id);
+    ds_put_format(string, "n_packets=%"PRIu64", ", fs->packet_count);
+    ds_put_format(string, "n_bytes=%"PRIu64", ", fs->byte_count);
+    if (fs->idle_timeout != OFP_FLOW_PERMANENT) {
+        ds_put_format(string, "idle_timeout=%"PRIu16", ", fs->idle_timeout);
+    }
+    if (fs->hard_timeout != OFP_FLOW_PERMANENT) {
+        ds_put_format(string, "hard_timeout=%"PRIu16", ", fs->hard_timeout);
+    }
+    if (fs->idle_age >= 0) {
+        ds_put_format(string, "idle_age=%d, ", fs->idle_age);
+    }
+    if (fs->hard_age >= 0 && fs->hard_age != fs->duration_sec) {
+        ds_put_format(string, "hard_age=%d, ", fs->hard_age);
+    }
+
+    cls_rule_format(&fs->rule, string);
+    if (string->string[string->length - 1] != ' ') {
+        ds_put_char(string, ' ');
+    }
+
+    ofpacts_format(fs->ofpacts, fs->ofpacts_len, string);
+}
+
 static void
 ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
 {
+    struct ofpbuf ofpacts;
     struct ofpbuf b;
 
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpbuf_init(&ofpacts, 64);
     for (;;) {
         struct ofputil_flow_stats fs;
         int retval;
 
-        retval = ofputil_decode_flow_stats_reply(&fs, &b, true);
+        retval = ofputil_decode_flow_stats_reply(&fs, &b, true, &ofpacts);
         if (retval) {
             if (retval != EOF) {
                 ds_put_cstr(string, " ***parse error***");
             }
             break;
         }
-
         ds_put_char(string, '\n');
-
-        ds_put_format(string, " cookie=0x%"PRIx64", duration=",
-                      ntohll(fs.cookie));
-        ofp_print_duration(string, fs.duration_sec, fs.duration_nsec);
-        ds_put_format(string, ", table=%"PRIu8", ", fs.table_id);
-        ds_put_format(string, "n_packets=%"PRIu64", ", fs.packet_count);
-        ds_put_format(string, "n_bytes=%"PRIu64", ", fs.byte_count);
-        if (fs.idle_timeout != OFP_FLOW_PERMANENT) {
-            ds_put_format(string, "idle_timeout=%"PRIu16",", fs.idle_timeout);
-        }
-        if (fs.hard_timeout != OFP_FLOW_PERMANENT) {
-            ds_put_format(string, "hard_timeout=%"PRIu16",", fs.hard_timeout);
-        }
-        if (fs.idle_age >= 0) {
-            ds_put_format(string, "idle_age=%d,", fs.idle_age);
-        }
-        if (fs.hard_age >= 0 && fs.hard_age != fs.duration_sec) {
-            ds_put_format(string, "hard_age=%d,", fs.hard_age);
-        }
-
-        cls_rule_format(&fs.rule, string);
-        if (string->string[string->length - 1] != ' ') {
-            ds_put_char(string, ' ');
-        }
-        ofp_print_actions(string, fs.actions, fs.n_actions);
+        ofp_print_flow_stats(string, &fs);
      }
+    ofpbuf_uninit(&ofpacts);
 }
 
 static void
-ofp_print_ofpst_aggregate_reply(struct ds *string,
-                                const struct ofp_aggregate_stats_reply *asr)
+ofp_print_aggregate_stats_reply(struct ds *string, const struct ofp_header *oh)
 {
-    ds_put_format(string, " packet_count=%"PRIu64,
-                  ntohll(get_32aligned_be64(&asr->packet_count)));
-    ds_put_format(string, " byte_count=%"PRIu64,
-                  ntohll(get_32aligned_be64(&asr->byte_count)));
-    ds_put_format(string, " flow_count=%"PRIu32, ntohl(asr->flow_count));
-}
+    struct ofputil_aggregate_stats as;
+    enum ofperr error;
 
-static void
-ofp_print_nxst_aggregate_reply(struct ds *string,
-                               const struct nx_aggregate_stats_reply *nasr)
-{
-    ds_put_format(string, " packet_count=%"PRIu64, ntohll(nasr->packet_count));
-    ds_put_format(string, " byte_count=%"PRIu64, ntohll(nasr->byte_count));
-    ds_put_format(string, " flow_count=%"PRIu32, ntohl(nasr->flow_count));
+    error = ofputil_decode_aggregate_stats_reply(&as, oh);
+    if (error) {
+        ofp_print_error(string, error);
+        return;
+    }
+
+    ds_put_format(string, " packet_count=%"PRIu64, as.packet_count);
+    ds_put_format(string, " byte_count=%"PRIu64, as.byte_count);
+    ds_put_format(string, " flow_count=%"PRIu32, as.flow_count);
 }
 
 static void print_port_stat(struct ds *string, const char *leader,
@@ -1282,9 +1083,9 @@ static void print_port_stat(struct ds *string, const char *leader,
 }
 
 static void
-ofp_print_ofpst_port_request(struct ds *string,
-                             const struct ofp_port_stats_request *psr)
+ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh)
 {
+    const struct ofp10_port_stats_request *psr = ofpmsg_body(oh);
     ds_put_format(string, " port_no=%"PRIu16, ntohs(psr->port_no));
 }
 
@@ -1292,14 +1093,25 @@ static void
 ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
                            int verbosity)
 {
-    const struct ofp_port_stats *ps = ofputil_stats_body(oh);
-    size_t n = ofputil_stats_body_len(oh) / sizeof *ps;
+    struct ofp10_port_stats *ps;
+    struct ofpbuf b;
+    size_t n;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+
+    n = b.size / sizeof *ps;
     ds_put_format(string, " %zu ports\n", n);
     if (verbosity < 1) {
         return;
     }
 
-    for (; n--; ps++) {
+    for (;;) {
+        ps = ofpbuf_try_pull(&b, sizeof *ps);
+        if (!ps) {
+            return;
+        }
+
         ds_put_format(string, "  port %2"PRIu16": ", ntohs(ps->port_no));
 
         ds_put_cstr(string, "rx ");
@@ -1321,32 +1133,178 @@ ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
 }
 
 static void
-ofp_print_ofpst_table_reply(struct ds *string, const struct ofp_header *oh,
-                            int verbosity)
+ofp_print_one_ofpst_table_reply(struct ds *string, enum ofp_version ofp_version,
+                                const char *name, struct ofp12_table_stats *ts)
 {
-    const struct ofp_table_stats *ts = ofputil_stats_body(oh);
-    size_t n = ofputil_stats_body_len(oh) / sizeof *ts;
+    char name_[OFP_MAX_TABLE_NAME_LEN + 1];
+
+    ovs_strlcpy(name_, name, sizeof name_);
+
+    ds_put_format(string, "  %d: %-8s: ", ts->table_id, name_);
+    ds_put_format(string, "wild=0x%05"PRIx64", ", ntohll(ts->wildcards));
+    ds_put_format(string, "max=%6"PRIu32", ", ntohl(ts->max_entries));
+    ds_put_format(string, "active=%"PRIu32"\n", ntohl(ts->active_count));
+    ds_put_cstr(string, "               ");
+    ds_put_format(string, "lookup=%"PRIu64", ", ntohll(ts->lookup_count));
+    ds_put_format(string, "matched=%"PRIu64"\n", ntohll(ts->matched_count));
+
+    if (ofp_version < OFP11_VERSION) {
+        return;
+    }
+
+    ds_put_cstr(string, "               ");
+    ds_put_format(string, "match=0x%08"PRIx64", ", ntohll(ts->match));
+    ds_put_format(string, "instructions=0x%08"PRIx32", ",
+                  ntohl(ts->instructions));
+    ds_put_format(string, "config=0x%08"PRIx32"\n", ntohl(ts->config));
+    ds_put_cstr(string, "               ");
+    ds_put_format(string, "write_actions=0x%08"PRIx32", ",
+                  ntohl(ts->write_actions));
+    ds_put_format(string, "apply_actions=0x%08"PRIx32"\n",
+                  ntohl(ts->apply_actions));
+
+    if (ofp_version < OFP12_VERSION) {
+        return;
+    }
+
+    ds_put_cstr(string, "               ");
+    ds_put_format(string, "write_setfields=0x%016"PRIx64"\n",
+                  ntohll(ts->write_setfields));
+    ds_put_cstr(string, "               ");
+    ds_put_format(string, "apply_setfields=0x%016"PRIx64"\n",
+                  ntohll(ts->apply_setfields));
+    ds_put_cstr(string, "               ");
+    ds_put_format(string, "metadata_match=0x%016"PRIx64"\n",
+                  ntohll(ts->metadata_match));
+    ds_put_cstr(string, "               ");
+    ds_put_format(string, "metadata_write=0x%016"PRIx64"\n",
+                  ntohll(ts->metadata_write));
+}
+
+static void
+ofp_print_ofpst_table_reply12(struct ds *string, const struct ofp_header *oh,
+                              int verbosity)
+{
+    struct ofp12_table_stats *ts;
+    struct ofpbuf b;
+    size_t n;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+
+    n = b.size / sizeof *ts;
     ds_put_format(string, " %zu tables\n", n);
     if (verbosity < 1) {
         return;
     }
 
-    for (; n--; ts++) {
-        char name[OFP_MAX_TABLE_NAME_LEN + 1];
-        ovs_strlcpy(name, ts->name, sizeof name);
+    for (;;) {
+        ts = ofpbuf_try_pull(&b, sizeof *ts);
+        if (!ts) {
+            return;
+        }
 
-        ds_put_format(string, "  %d: %-8s: ", ts->table_id, name);
-        ds_put_format(string, "wild=0x%05"PRIx32", ", ntohl(ts->wildcards));
-        ds_put_format(string, "max=%6"PRIu32", ", ntohl(ts->max_entries));
-        ds_put_format(string, "active=%"PRIu32"\n", ntohl(ts->active_count));
-        ds_put_cstr(string, "               ");
-        ds_put_format(string, "lookup=%"PRIu64", ",
-                      ntohll(get_32aligned_be64(&ts->lookup_count)));
-        ds_put_format(string, "matched=%"PRIu64"\n",
-                      ntohll(get_32aligned_be64(&ts->matched_count)));
+        ofp_print_one_ofpst_table_reply(string, OFP12_VERSION, ts->name, ts);
      }
 }
 
+static void
+ofp_print_ofpst_table_reply11(struct ds *string, const struct ofp_header *oh,
+                              int verbosity)
+{
+    struct ofp11_table_stats *ts;
+    struct ofpbuf b;
+    size_t n;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+
+    n = b.size / sizeof *ts;
+    ds_put_format(string, " %zu tables\n", n);
+    if (verbosity < 1) {
+        return;
+    }
+
+    for (;;) {
+        struct ofp12_table_stats ts12;
+
+        ts = ofpbuf_try_pull(&b, sizeof *ts);
+        if (!ts) {
+            return;
+        }
+
+        ts12.table_id = ts->table_id;
+        ts12.wildcards = htonll(ntohl(ts->wildcards));
+        ts12.max_entries = ts->max_entries;
+        ts12.active_count = ts->active_count;
+        ts12.lookup_count = ts->lookup_count;
+        ts12.matched_count = ts->matched_count;
+        ts12.match = htonll(ntohl(ts->match));
+        ts12.instructions = ts->instructions;
+        ts12.config = ts->config;
+        ts12.write_actions = ts->write_actions;
+        ts12.apply_actions = ts->apply_actions;
+        ofp_print_one_ofpst_table_reply(string, OFP11_VERSION, ts->name, &ts12);
+     }
+}
+
+static void
+ofp_print_ofpst_table_reply10(struct ds *string, const struct ofp_header *oh,
+                              int verbosity)
+{
+    struct ofp10_table_stats *ts;
+    struct ofpbuf b;
+    size_t n;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+
+    n = b.size / sizeof *ts;
+    ds_put_format(string, " %zu tables\n", n);
+    if (verbosity < 1) {
+        return;
+    }
+
+    for (;;) {
+        struct ofp12_table_stats ts12;
+
+        ts = ofpbuf_try_pull(&b, sizeof *ts);
+        if (!ts) {
+            return;
+        }
+
+        ts12.table_id = ts->table_id;
+        ts12.wildcards = htonll(ntohl(ts->wildcards));
+        ts12.max_entries = ts->max_entries;
+        ts12.active_count = ts->active_count;
+        ts12.lookup_count = get_32aligned_be64(&ts->lookup_count);
+        ts12.matched_count = get_32aligned_be64(&ts->matched_count);
+        ofp_print_one_ofpst_table_reply(string, OFP10_VERSION, ts->name, &ts12);
+     }
+}
+
+static void
+ofp_print_ofpst_table_reply(struct ds *string, const struct ofp_header *oh,
+                            int verbosity)
+{
+    switch ((enum ofp_version)oh->version) {
+    case OFP12_VERSION:
+        ofp_print_ofpst_table_reply12(string, oh, verbosity);
+        break;
+
+    case OFP11_VERSION:
+        ofp_print_ofpst_table_reply11(string, oh, verbosity);
+        break;
+
+    case OFP10_VERSION:
+        ofp_print_ofpst_table_reply10(string, oh, verbosity);
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+}
+
 static void
 ofp_print_queue_name(struct ds *string, uint32_t queue_id)
 {
@@ -1358,9 +1316,10 @@ ofp_print_queue_name(struct ds *string, uint32_t queue_id)
 }
 
 static void
-ofp_print_ofpst_queue_request(struct ds *string,
-                              const struct ofp_queue_stats_request *qsr)
+ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh)
 {
+    const struct ofp10_queue_stats_request *qsr = ofpmsg_body(oh);
+
     ds_put_cstr(string, "port=");
     ofputil_format_port(ntohs(qsr->port_no), string);
 
@@ -1372,14 +1331,25 @@ static void
 ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh,
                             int verbosity)
 {
-    const struct ofp_queue_stats *qs = ofputil_stats_body(oh);
-    size_t n = ofputil_stats_body_len(oh) / sizeof *qs;
+    struct ofp10_queue_stats *qs;
+    struct ofpbuf b;
+    size_t n;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+
+    n = b.size / sizeof *qs;
     ds_put_format(string, " %zu queues\n", n);
     if (verbosity < 1) {
         return;
     }
 
-    for (; n--; qs++) {
+    for (;;) {
+        qs = ofpbuf_try_pull(&b, sizeof *qs);
+        if (!qs) {
+            return;
+        }
+
         ds_put_cstr(string, "  port ");
         ofputil_format_port(ntohs(qs->port_no), string);
         ds_put_cstr(string, " queue ");
@@ -1392,25 +1362,34 @@ ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh,
     }
 }
 
+static void
+ofp_print_ofpst_port_desc_reply(struct ds *string,
+                                const struct ofp_header *oh)
+{
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+    ds_put_char(string, '\n');
+    ofp_print_phy_ports(string, oh->version, &b);
+}
+
 static void
 ofp_print_stats_request(struct ds *string, const struct ofp_header *oh)
 {
-    const struct ofp_stats_msg *srq = (const struct ofp_stats_msg *) oh;
+    uint16_t flags = ofpmp_flags(oh);
 
-    if (srq->flags) {
-        ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***",
-                      ntohs(srq->flags));
+    if (flags) {
+        ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags);
     }
 }
 
 static void
 ofp_print_stats_reply(struct ds *string, const struct ofp_header *oh)
 {
-    const struct ofp_stats_msg *srp = (const struct ofp_stats_msg *) oh;
-
-    if (srp->flags) {
-        uint16_t flags = ntohs(srp->flags);
+    uint16_t flags = ofpmp_flags(oh);
 
+    if (flags) {
         ds_put_cstr(string, " flags=");
         if (flags & OFPSF_REPLY_MORE) {
             ds_put_cstr(string, "[more]");
@@ -1564,185 +1543,332 @@ ofp_print_nxt_set_controller_id(struct ds *string,
 }
 
 static void
-ofp_to_string__(const struct ofp_header *oh,
-                const struct ofputil_msg_type *type, struct ds *string,
-                int verbosity)
+ofp_print_nxt_flow_monitor_cancel(struct ds *string,
+                                  const struct ofp_header *oh)
 {
-    enum ofputil_msg_code code;
-    const void *msg = oh;
+    ds_put_format(string, " id=%"PRIu32,
+                  ofputil_decode_flow_monitor_cancel(oh));
+}
+
+static const char *
+nx_flow_monitor_flags_to_name(uint32_t bit)
+{
+    enum nx_flow_monitor_flags fmf = bit;
+
+    switch (fmf) {
+    case NXFMF_INITIAL: return "initial";
+    case NXFMF_ADD: return "add";
+    case NXFMF_DELETE: return "delete";
+    case NXFMF_MODIFY: return "modify";
+    case NXFMF_ACTIONS: return "actions";
+    case NXFMF_OWN: return "own";
+    }
+
+    return NULL;
+}
+
+static void
+ofp_print_nxst_flow_monitor_request(struct ds *string,
+                                    const struct ofp_header *oh)
+{
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    for (;;) {
+        struct ofputil_flow_monitor_request request;
+        int retval;
+
+        retval = ofputil_decode_flow_monitor_request(&request, &b);
+        if (retval) {
+            if (retval != EOF) {
+                ofp_print_error(string, retval);
+            }
+            return;
+        }
+
+        ds_put_format(string, "\n id=%"PRIu32" flags=", request.id);
+        ofp_print_bit_names(string, request.flags,
+                            nx_flow_monitor_flags_to_name, ',');
+
+        if (request.out_port != OFPP_NONE) {
+            ds_put_cstr(string, " out_port=");
+            ofputil_format_port(request.out_port, string);
+        }
+
+        if (request.table_id != 0xff) {
+            ds_put_format(string, " table=%"PRIu8, request.table_id);
+        }
+
+        ds_put_char(string, ' ');
+        cls_rule_format(&request.match, string);
+        ds_chomp(string, ' ');
+    }
+}
+
+static void
+ofp_print_nxst_flow_monitor_reply(struct ds *string,
+                                  const struct ofp_header *oh)
+{
+    uint64_t ofpacts_stub[1024 / 8];
+    struct ofpbuf ofpacts;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+    for (;;) {
+        struct ofputil_flow_update update;
+        struct cls_rule match;
+        int retval;
+
+        update.match = &match;
+        retval = ofputil_decode_flow_update(&update, &b, &ofpacts);
+        if (retval) {
+            if (retval != EOF) {
+                ofp_print_error(string, retval);
+            }
+            ofpbuf_uninit(&ofpacts);
+            return;
+        }
+
+        ds_put_cstr(string, "\n event=");
+        switch (update.event) {
+        case NXFME_ADDED:
+            ds_put_cstr(string, "ADDED");
+            break;
+
+        case NXFME_DELETED:
+            ds_put_format(string, "DELETED reason=%s",
+                          ofp_flow_removed_reason_to_string(update.reason));
+            break;
+
+        case NXFME_MODIFIED:
+            ds_put_cstr(string, "MODIFIED");
+            break;
+
+        case NXFME_ABBREV:
+            ds_put_format(string, "ABBREV xid=0x%"PRIx32, ntohl(update.xid));
+            continue;
+        }
+
+        ds_put_format(string, " table=%"PRIu8, update.table_id);
+        if (update.idle_timeout != OFP_FLOW_PERMANENT) {
+            ds_put_format(string, " idle_timeout=%"PRIu16,
+                          update.idle_timeout);
+        }
+        if (update.hard_timeout != OFP_FLOW_PERMANENT) {
+            ds_put_format(string, " hard_timeout=%"PRIu16,
+                          update.hard_timeout);
+        }
+        ds_put_format(string, " cookie=%#"PRIx64, ntohll(update.cookie));
 
-    ds_put_cstr(string, ofputil_msg_type_name(type));
+        ds_put_char(string, ' ');
+        cls_rule_format(update.match, string);
+
+        if (update.ofpacts_len) {
+            if (string->string[string->length - 1] != ' ') {
+                ds_put_char(string, ' ');
+            }
+            ofpacts_format(update.ofpacts, update.ofpacts_len, string);
+        }
+    }
+}
+
+void
+ofp_print_version(const struct ofp_header *oh,
+                  struct ds *string)
+{
     switch (oh->version) {
     case OFP10_VERSION:
         break;
     case OFP11_VERSION:
         ds_put_cstr(string, " (OF1.1)");
         break;
+    case OFP12_VERSION:
+        ds_put_cstr(string, " (OF1.2)");
+        break;
     default:
         ds_put_format(string, " (OF 0x%02"PRIx8")", oh->version);
         break;
     }
     ds_put_format(string, " (xid=0x%"PRIx32"):", ntohl(oh->xid));
+}
 
-    code = ofputil_msg_type_code(type);
-    switch (code) {
-    case OFPUTIL_MSG_INVALID:
-        break;
+static void
+ofp_header_to_string__(const struct ofp_header *oh, enum ofpraw raw,
+                       struct ds *string)
+{
+    ds_put_cstr(string, ofpraw_get_name(raw));
+    ofp_print_version(oh, string);
+}
+
+static void
+ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
+                struct ds *string, int verbosity)
+{
+    const void *msg = oh;
 
-    case OFPUTIL_OFPT_HELLO:
+    ofp_header_to_string__(oh, raw, string);
+    switch (ofptype_from_ofpraw(raw)) {
+    case OFPTYPE_HELLO:
         ds_put_char(string, '\n');
         ds_put_hex_dump(string, oh + 1, ntohs(oh->length) - sizeof *oh,
                         0, true);
         break;
 
-    case OFPUTIL_OFPT_ERROR:
-        ofp_print_error_msg(string, msg);
+    case OFPTYPE_ERROR:
+        ofp_print_error_msg(string, oh);
         break;
 
-    case OFPUTIL_OFPT_ECHO_REQUEST:
-    case OFPUTIL_OFPT_ECHO_REPLY:
+    case OFPTYPE_ECHO_REQUEST:
+    case OFPTYPE_ECHO_REPLY:
         ofp_print_echo(string, oh, verbosity);
         break;
 
-    case OFPUTIL_OFPT_FEATURES_REQUEST:
-        break;
-
-    case OFPUTIL_OFPT_FEATURES_REPLY:
-        ofp_print_switch_features(string, msg);
+    case OFPTYPE_FEATURES_REQUEST:
         break;
 
-    case OFPUTIL_OFPT_GET_CONFIG_REQUEST:
+    case OFPTYPE_FEATURES_REPLY:
+        ofp_print_switch_features(string, oh);
         break;
 
-    case OFPUTIL_OFPT_GET_CONFIG_REPLY:
-    case OFPUTIL_OFPT_SET_CONFIG:
-        ofp_print_switch_config(string, msg);
+    case OFPTYPE_GET_CONFIG_REQUEST:
         break;
 
-    case OFPUTIL_OFPT_PACKET_IN:
-    case OFPUTIL_NXT_PACKET_IN:
-        ofp_print_packet_in(string, msg, verbosity);
+    case OFPTYPE_GET_CONFIG_REPLY:
+    case OFPTYPE_SET_CONFIG:
+        ofp_print_switch_config(string, ofpmsg_body(oh));
         break;
 
-    case OFPUTIL_OFPT_FLOW_REMOVED:
-    case OFPUTIL_NXT_FLOW_REMOVED:
-        ofp_print_flow_removed(string, msg);
+    case OFPTYPE_PACKET_IN:
+        ofp_print_packet_in(string, oh, verbosity);
         break;
 
-    case OFPUTIL_OFPT_PORT_STATUS:
-        ofp_print_port_status(string, msg);
+    case OFPTYPE_FLOW_REMOVED:
+        ofp_print_flow_removed(string, oh);
         break;
 
-    case OFPUTIL_OFPT_PACKET_OUT:
-        ofp_print_packet_out(string, msg, verbosity);
+    case OFPTYPE_PORT_STATUS:
+        ofp_print_port_status(string, oh);
         break;
 
-    case OFPUTIL_OFPT_FLOW_MOD:
-    case OFPUTIL_NXT_FLOW_MOD:
-        ofp_print_flow_mod(string, msg, code, verbosity);
+    case OFPTYPE_PACKET_OUT:
+        ofp_print_packet_out(string, oh, verbosity);
         break;
 
-    case OFPUTIL_OFPT_PORT_MOD:
-        ofp_print_port_mod(string, msg);
+    case OFPTYPE_FLOW_MOD:
+        ofp_print_flow_mod(string, oh, verbosity);
         break;
 
-    case OFPUTIL_OFPT_BARRIER_REQUEST:
-    case OFPUTIL_OFPT_BARRIER_REPLY:
+    case OFPTYPE_PORT_MOD:
+        ofp_print_port_mod(string, oh);
         break;
 
-    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
-    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
-        /* XXX */
+    case OFPTYPE_BARRIER_REQUEST:
+    case OFPTYPE_BARRIER_REPLY:
         break;
 
-    case OFPUTIL_OFPST_DESC_REQUEST:
+    case OFPTYPE_DESC_STATS_REQUEST:
+    case OFPTYPE_PORT_DESC_STATS_REQUEST:
         ofp_print_stats_request(string, oh);
         break;
 
-    case OFPUTIL_OFPST_FLOW_REQUEST:
-    case OFPUTIL_NXST_FLOW_REQUEST:
-    case OFPUTIL_OFPST_AGGREGATE_REQUEST:
-    case OFPUTIL_NXST_AGGREGATE_REQUEST:
+    case OFPTYPE_FLOW_STATS_REQUEST:
+    case OFPTYPE_AGGREGATE_STATS_REQUEST:
         ofp_print_stats_request(string, oh);
-        ofp_print_flow_stats_request(string, msg);
+        ofp_print_flow_stats_request(string, oh);
         break;
 
-    case OFPUTIL_OFPST_TABLE_REQUEST:
+    case OFPTYPE_TABLE_STATS_REQUEST:
         ofp_print_stats_request(string, oh);
         break;
 
-    case OFPUTIL_OFPST_PORT_REQUEST:
+    case OFPTYPE_PORT_STATS_REQUEST:
         ofp_print_stats_request(string, oh);
-        ofp_print_ofpst_port_request(string, msg);
+        ofp_print_ofpst_port_request(string, oh);
         break;
 
-    case OFPUTIL_OFPST_QUEUE_REQUEST:
+    case OFPTYPE_QUEUE_STATS_REQUEST:
         ofp_print_stats_request(string, oh);
-        ofp_print_ofpst_queue_request(string, msg);
+        ofp_print_ofpst_queue_request(string, oh);
         break;
 
-    case OFPUTIL_OFPST_DESC_REPLY:
+    case OFPTYPE_DESC_STATS_REPLY:
         ofp_print_stats_reply(string, oh);
-        ofp_print_ofpst_desc_reply(string, msg);
+        ofp_print_ofpst_desc_reply(string, oh);
         break;
 
-    case OFPUTIL_OFPST_FLOW_REPLY:
-    case OFPUTIL_NXST_FLOW_REPLY:
+    case OFPTYPE_FLOW_STATS_REPLY:
         ofp_print_stats_reply(string, oh);
         ofp_print_flow_stats_reply(string, oh);
         break;
 
-    case OFPUTIL_OFPST_QUEUE_REPLY:
+    case OFPTYPE_QUEUE_STATS_REPLY:
         ofp_print_stats_reply(string, oh);
         ofp_print_ofpst_queue_reply(string, oh, verbosity);
         break;
 
-    case OFPUTIL_OFPST_PORT_REPLY:
+    case OFPTYPE_PORT_STATS_REPLY:
         ofp_print_stats_reply(string, oh);
         ofp_print_ofpst_port_reply(string, oh, verbosity);
         break;
 
-    case OFPUTIL_OFPST_TABLE_REPLY:
+    case OFPTYPE_TABLE_STATS_REPLY:
         ofp_print_stats_reply(string, oh);
         ofp_print_ofpst_table_reply(string, oh, verbosity);
         break;
 
-    case OFPUTIL_OFPST_AGGREGATE_REPLY:
+    case OFPTYPE_AGGREGATE_STATS_REPLY:
         ofp_print_stats_reply(string, oh);
-        ofp_print_ofpst_aggregate_reply(string, msg);
+        ofp_print_aggregate_stats_reply(string, oh);
         break;
 
-    case OFPUTIL_NXT_ROLE_REQUEST:
-    case OFPUTIL_NXT_ROLE_REPLY:
-        ofp_print_nxt_role_message(string, msg);
+    case OFPTYPE_PORT_DESC_STATS_REPLY:
+        ofp_print_stats_reply(string, oh);
+        ofp_print_ofpst_port_desc_reply(string, oh);
         break;
 
-    case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
-        ofp_print_nxt_flow_mod_table_id(string, msg);
+    case OFPTYPE_ROLE_REQUEST:
+    case OFPTYPE_ROLE_REPLY:
+        ofp_print_nxt_role_message(string, ofpmsg_body(oh));
         break;
 
-    case OFPUTIL_NXT_SET_FLOW_FORMAT:
-        ofp_print_nxt_set_flow_format(string, msg);
+    case OFPTYPE_FLOW_MOD_TABLE_ID:
+        ofp_print_nxt_flow_mod_table_id(string, ofpmsg_body(oh));
         break;
 
-    case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
-        ofp_print_nxt_set_packet_in_format(string, msg);
+    case OFPTYPE_SET_FLOW_FORMAT:
+        ofp_print_nxt_set_flow_format(string, ofpmsg_body(oh));
         break;
 
-    case OFPUTIL_NXT_FLOW_AGE:
+    case OFPTYPE_SET_PACKET_IN_FORMAT:
+        ofp_print_nxt_set_packet_in_format(string, ofpmsg_body(oh));
         break;
 
-    case OFPUTIL_NXT_SET_CONTROLLER_ID:
-        ofp_print_nxt_set_controller_id(string, msg);
+    case OFPTYPE_FLOW_AGE:
         break;
 
-    case OFPUTIL_NXT_SET_ASYNC_CONFIG:
-        ofp_print_nxt_set_async_config(string, msg);
+    case OFPTYPE_SET_CONTROLLER_ID:
+        ofp_print_nxt_set_controller_id(string, ofpmsg_body(oh));
         break;
 
-    case OFPUTIL_NXST_AGGREGATE_REPLY:
-        ofp_print_stats_reply(string, oh);
-        ofp_print_nxst_aggregate_reply(string, msg);
+    case OFPTYPE_SET_ASYNC_CONFIG:
+        ofp_print_nxt_set_async_config(string, ofpmsg_body(oh));
+        break;
+
+    case OFPTYPE_FLOW_MONITOR_CANCEL:
+        ofp_print_nxt_flow_monitor_cancel(string, msg);
+        break;
+
+    case OFPTYPE_FLOW_MONITOR_PAUSED:
+    case OFPTYPE_FLOW_MONITOR_RESUMED:
+        break;
+
+    case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+        ofp_print_nxst_flow_monitor_request(string, msg);
+        break;
+
+    case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
+        ofp_print_nxst_flow_monitor_reply(string, msg);
         break;
     }
 }
@@ -1763,6 +1889,15 @@ ofp_to_string(const void *oh_, size_t len, int verbosity)
         ds_put_format(&string, "OpenFlow packet too short (only %zu bytes):\n",
                       len);
     } else if (ntohs(oh->length) > len) {
+        enum ofperr error;
+        enum ofpraw raw;
+
+        error = ofpraw_decode_partial(&raw, oh, len);
+        if (!error) {
+            ofp_header_to_string__(oh, raw, &string);
+            ds_put_char(&string, '\n');
+        }
+
         ds_put_format(&string,
                       "(***truncated to %zu bytes from %"PRIu16"***)\n",
                       len, ntohs(oh->length));
@@ -1771,12 +1906,12 @@ ofp_to_string(const void *oh_, size_t len, int verbosity)
                       "(***only uses %"PRIu16" bytes out of %zu***)\n",
                       ntohs(oh->length), len);
     } else {
-        const struct ofputil_msg_type *type;
         enum ofperr error;
+        enum ofpraw raw;
 
-        error = ofputil_decode_msg_type(oh, &type);
+        error = ofpraw_decode(&raw, oh);
         if (!error) {
-            ofp_to_string__(oh, type, &string, verbosity);
+            ofp_to_string__(oh, raw, &string, verbosity);
             if (verbosity >= 5) {
                 if (ds_last(&string) != '\n') {
                     ds_put_char(&string, '\n');
index ad204b1..825e139 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <stdint.h>
 #include <stdio.h>
 
-struct ofp_flow_mod;
-struct ofp_match;
 struct ds;
-union ofp_action;
+struct ofp10_match;
+struct ofp_flow_mod;
+struct ofp_header;
+struct ofputil_flow_stats;
 
 #ifdef  __cplusplus
 extern "C" {
@@ -34,13 +35,15 @@ extern "C" {
 void ofp_print(FILE *, const void *, size_t, int verbosity);
 void ofp_print_packet(FILE *stream, const void *data, size_t len);
 
-void ofp_print_actions(struct ds *, const union ofp_action *, size_t);
-void ofp_print_match(struct ds *, const struct ofp_match *, int verbosity);
+void ofp10_match_print(struct ds *, const struct ofp10_match *, int verbosity);
 
 char *ofp_to_string(const void *, size_t, int verbosity);
-char *ofp_match_to_string(const struct ofp_match *, int verbosity);
+char *ofp10_match_to_string(const struct ofp10_match *, int verbosity);
 char *ofp_packet_to_string(const void *data, size_t len);
 
+void ofp_print_flow_stats(struct ds *, struct ofputil_flow_stats *);
+void ofp_print_version(const struct ofp_header *, struct ds *);
+
 #ifdef  __cplusplus
 }
 #endif
index ae9b30d..ce9bb74 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,7 +32,9 @@
 #include "multipath.h"
 #include "netdev.h"
 #include "nx-match.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
+#include "ofp-msgs.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "packets.h"
@@ -64,26 +66,27 @@ ofputil_wcbits_to_netmask(int wcbits)
 }
 
 /* Given the IP netmask 'netmask', returns the number of bits of the IP address
- * that it wildcards, that is, the number of 0-bits in 'netmask'.  'netmask'
- * must be a CIDR netmask (see ip_is_cidr()). */
+ * that it wildcards, that is, the number of 0-bits in 'netmask', a number
+ * between 0 and 32 inclusive.
+ *
+ * If 'netmask' is not a CIDR netmask (see ip_is_cidr()), the return value will
+ * still be in the valid range but isn't otherwise meaningful. */
 int
 ofputil_netmask_to_wcbits(ovs_be32 netmask)
 {
     return 32 - ip_count_cidr_bits(netmask);
 }
 
-/* A list of the FWW_* and OFPFW_ bits that have the same value, meaning, and
+/* A list of the FWW_* and OFPFW10_ bits that have the same value, meaning, and
  * name. */
 #define WC_INVARIANT_LIST \
     WC_INVARIANT_BIT(IN_PORT) \
-    WC_INVARIANT_BIT(DL_SRC) \
-    WC_INVARIANT_BIT(DL_DST) \
     WC_INVARIANT_BIT(DL_TYPE) \
     WC_INVARIANT_BIT(NW_PROTO)
 
 /* Verify that all of the invariant bits (as defined on WC_INVARIANT_LIST)
  * actually have the same names and values. */
-#define WC_INVARIANT_BIT(NAME) BUILD_ASSERT_DECL(FWW_##NAME == OFPFW_##NAME);
+#define WC_INVARIANT_BIT(NAME) BUILD_ASSERT_DECL(FWW_##NAME == OFPFW10_##NAME);
     WC_INVARIANT_LIST
 #undef WC_INVARIANT_BIT
 
@@ -95,65 +98,65 @@ static const flow_wildcards_t WC_INVARIANTS = 0
 #undef WC_INVARIANT_BIT
 ;
 
-/* Converts the wildcard in 'ofpfw' into a flow_wildcards in 'wc' for use in
- * struct cls_rule.  It is the caller's responsibility to handle the special
- * case where the flow match's dl_vlan is set to OFP_VLAN_NONE. */
+/* Converts the OpenFlow 1.0 wildcards in 'ofpfw' (OFPFW10_*) into a
+ * flow_wildcards in 'wc' for use in struct cls_rule.  It is the caller's
+ * responsibility to handle the special case where the flow match's dl_vlan is
+ * set to OFP_VLAN_NONE. */
 void
-ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc)
+ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
     /* Initialize most of rule->wc. */
     flow_wildcards_init_catchall(wc);
     wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
 
-    /* Wildcard fields that aren't defined by ofp_match or tun_id. */
-    wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_ECN | FWW_NW_TTL
-                      | FWW_IPV6_LABEL);
+    /* Wildcard fields that aren't defined by ofp10_match. */
+    wc->wildcards |= FWW_NW_ECN | FWW_NW_TTL;
 
-    if (ofpfw & OFPFW_NW_TOS) {
+    if (ofpfw & OFPFW10_NW_TOS) {
         /* OpenFlow 1.0 defines a TOS wildcard, but it's much later in
          * the enum than we can use. */
         wc->wildcards |= FWW_NW_DSCP;
     }
 
-    wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT);
-    wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT);
+    wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_SRC_SHIFT);
+    wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_DST_SHIFT);
 
-    if (!(ofpfw & OFPFW_TP_SRC)) {
+    if (!(ofpfw & OFPFW10_TP_SRC)) {
         wc->tp_src_mask = htons(UINT16_MAX);
     }
-    if (!(ofpfw & OFPFW_TP_DST)) {
+    if (!(ofpfw & OFPFW10_TP_DST)) {
         wc->tp_dst_mask = htons(UINT16_MAX);
     }
 
-    if (ofpfw & OFPFW_DL_DST) {
-        /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, but
-         * Open vSwitch breaks the Ethernet destination into bits as FWW_DL_DST
-         * and FWW_ETH_MCAST. */
-        wc->wildcards |= FWW_ETH_MCAST;
+    if (!(ofpfw & OFPFW10_DL_SRC)) {
+        memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN);
+    }
+    if (!(ofpfw & OFPFW10_DL_DST)) {
+        memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN);
     }
 
     /* VLAN TCI mask. */
-    if (!(ofpfw & OFPFW_DL_VLAN_PCP)) {
+    if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) {
         wc->vlan_tci_mask |= htons(VLAN_PCP_MASK | VLAN_CFI);
     }
-    if (!(ofpfw & OFPFW_DL_VLAN)) {
+    if (!(ofpfw & OFPFW10_DL_VLAN)) {
         wc->vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
     }
 }
 
-/* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
- * 'priority'. */
+/* Converts the ofp10_match in 'match' into a cls_rule in 'rule', with the
+ * given 'priority'. */
 void
-ofputil_cls_rule_from_match(const struct ofp_match *match,
-                            unsigned int priority, struct cls_rule *rule)
+ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *match,
+                                  unsigned int priority, struct cls_rule *rule)
 {
-    uint32_t ofpfw = ntohl(match->wildcards) & OFPFW_ALL;
+    uint32_t ofpfw = ntohl(match->wildcards) & OFPFW10_ALL;
 
     /* Initialize rule->priority, rule->wc. */
     rule->priority = !ofpfw ? UINT16_MAX : priority;
-    ofputil_wildcard_from_openflow(ofpfw, &rule->wc);
+    ofputil_wildcard_from_ofpfw10(ofpfw, &rule->wc);
 
     /* Initialize most of rule->flow. */
     rule->flow.nw_src = match->nw_src;
@@ -168,12 +171,13 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
     rule->flow.nw_proto = match->nw_proto;
 
     /* Translate VLANs. */
-    if (!(ofpfw & OFPFW_DL_VLAN) && match->dl_vlan == htons(OFP_VLAN_NONE)) {
+    if (!(ofpfw & OFPFW10_DL_VLAN) &&
+        match->dl_vlan == htons(OFP10_VLAN_NONE)) {
         /* Match only packets without 802.1Q header.
          *
-         * When OFPFW_DL_VLAN_PCP is wildcarded, this is obviously correct.
+         * When OFPFW10_DL_VLAN_PCP is wildcarded, this is obviously correct.
          *
-         * If OFPFW_DL_VLAN_PCP is matched, the flow match is contradictory,
+         * If OFPFW10_DL_VLAN_PCP is matched, the flow match is contradictory,
          * because we can't have a specific PCP without an 802.1Q header.
          * However, older versions of OVS treated this as matching packets
          * withut an 802.1Q header, so we do here too. */
@@ -192,44 +196,54 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
     cls_rule_zero_wildcarded_fields(rule);
 }
 
-/* Convert 'rule' into the OpenFlow match structure 'match'. */
+/* Convert 'rule' into the OpenFlow 1.0 match structure 'match'. */
 void
-ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
+ofputil_cls_rule_to_ofp10_match(const struct cls_rule *rule,
+                                struct ofp10_match *match)
 {
     const struct flow_wildcards *wc = &rule->wc;
     uint32_t ofpfw;
 
     /* Figure out most OpenFlow wildcards. */
     ofpfw = (OVS_FORCE uint32_t) (wc->wildcards & WC_INVARIANTS);
-    ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT;
-    ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT;
+    ofpfw |= (ofputil_netmask_to_wcbits(wc->nw_src_mask)
+              << OFPFW10_NW_SRC_SHIFT);
+    ofpfw |= (ofputil_netmask_to_wcbits(wc->nw_dst_mask)
+              << OFPFW10_NW_DST_SHIFT);
     if (wc->wildcards & FWW_NW_DSCP) {
-        ofpfw |= OFPFW_NW_TOS;
+        ofpfw |= OFPFW10_NW_TOS;
     }
     if (!wc->tp_src_mask) {
-        ofpfw |= OFPFW_TP_SRC;
+        ofpfw |= OFPFW10_TP_SRC;
     }
     if (!wc->tp_dst_mask) {
-        ofpfw |= OFPFW_TP_DST;
+        ofpfw |= OFPFW10_TP_DST;
+    }
+    if (eth_addr_is_zero(wc->dl_src_mask)) {
+        ofpfw |= OFPFW10_DL_SRC;
+    }
+    if (eth_addr_is_zero(wc->dl_dst_mask)) {
+        ofpfw |= OFPFW10_DL_DST;
     }
 
     /* Translate VLANs. */
     match->dl_vlan = htons(0);
     match->dl_vlan_pcp = 0;
     if (rule->wc.vlan_tci_mask == htons(0)) {
-        ofpfw |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP;
+        ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP;
     } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
                && !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
-        match->dl_vlan = htons(OFP_VLAN_NONE);
+        match->dl_vlan = htons(OFP10_VLAN_NONE);
+        ofpfw |= OFPFW10_DL_VLAN_PCP;
     } else {
         if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
-            ofpfw |= OFPFW_DL_VLAN;
+            ofpfw |= OFPFW10_DL_VLAN;
         } else {
             match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci));
         }
 
         if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) {
-            ofpfw |= OFPFW_DL_VLAN_PCP;
+            ofpfw |= OFPFW10_DL_VLAN_PCP;
         } else {
             match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci);
         }
@@ -251,604 +265,318 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
     memset(match->pad2, '\0', sizeof match->pad2);
 }
 
-/* Given a 'dl_type' value in the format used in struct flow, returns the
- * corresponding 'dl_type' value for use in an OpenFlow ofp_match structure. */
-ovs_be16
-ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type)
-{
-    return (flow_dl_type == htons(FLOW_DL_TYPE_NONE)
-            ? htons(OFP_DL_TYPE_NOT_ETH_TYPE)
-            : flow_dl_type);
-}
-
-/* Given a 'dl_type' value in the format used in an OpenFlow ofp_match
- * structure, returns the corresponding 'dl_type' value for use in struct
- * flow. */
-ovs_be16
-ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type)
-{
-    return (ofp_dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
-            ? htons(FLOW_DL_TYPE_NONE)
-            : ofp_dl_type);
-}
-
-/* Returns a transaction ID to use for an outgoing OpenFlow message. */
-static ovs_be32
-alloc_xid(void)
+enum ofperr
+ofputil_pull_ofp11_match(struct ofpbuf *buf, unsigned int priority,
+                         struct cls_rule *rule, uint16_t *padded_match_len)
 {
-    static uint32_t next_xid = 1;
-    return htonl(next_xid++);
-}
-\f
-/* Basic parsing of OpenFlow messages. */
-
-struct ofputil_msg_type {
-    enum ofputil_msg_code code; /* OFPUTIL_*. */
-    uint8_t ofp_version;        /* An OpenFlow version or 0 for "any". */
-    uint32_t value;             /* OFPT_*, OFPST_*, NXT_*, or NXST_*. */
-    const char *name;           /* e.g. "OFPT_FLOW_REMOVED". */
-    unsigned int min_size;      /* Minimum total message size in bytes. */
-    /* 0 if 'min_size' is the exact size that the message must be.  Otherwise,
-     * the message may exceed 'min_size' by an even multiple of this value. */
-    unsigned int extra_multiple;
-};
+    struct ofp11_match_header *omh = buf->data;
+    uint16_t match_len;
 
-/* Represents a malformed OpenFlow message. */
-static const struct ofputil_msg_type ofputil_invalid_type = {
-    OFPUTIL_MSG_INVALID, 0, 0, "OFPUTIL_MSG_INVALID", 0, 0
-};
+    if (buf->size < sizeof *omh) {
+        return OFPERR_OFPBMC_BAD_LEN;
+    }
 
-struct ofputil_msg_category {
-    const char *name;           /* e.g. "OpenFlow message" */
-    const struct ofputil_msg_type *types;
-    size_t n_types;
-    enum ofperr missing_error;  /* Error value for missing type. */
-};
+    match_len = ntohs(omh->length);
 
-static enum ofperr
-ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size)
-{
-    switch (type->extra_multiple) {
-    case 0:
-        if (size != type->min_size) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
-                         "length %u (expected length %u)",
-                         type->name, size, type->min_size);
-            return OFPERR_OFPBRC_BAD_LEN;
-        }
-        return 0;
+    switch (ntohs(omh->type)) {
+    case OFPMT_STANDARD: {
+        struct ofp11_match *om;
 
-    case 1:
-        if (size < type->min_size) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
-                         "length %u (expected length at least %u bytes)",
-                         type->name, size, type->min_size);
-            return OFPERR_OFPBRC_BAD_LEN;
+        if (match_len != sizeof *om || buf->size < sizeof *om) {
+            return OFPERR_OFPBMC_BAD_LEN;
         }
-        return 0;
-
-    default:
-        if (size < type->min_size
-            || (size - type->min_size) % type->extra_multiple) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
-                         "length %u (must be exactly %u bytes or longer "
-                         "by an integer multiple of %u bytes)",
-                         type->name, size,
-                         type->min_size, type->extra_multiple);
-            return OFPERR_OFPBRC_BAD_LEN;
+        om = ofpbuf_pull(buf, sizeof *om);
+        if (padded_match_len) {
+            *padded_match_len = match_len;
         }
-        return 0;
+        return ofputil_cls_rule_from_ofp11_match(om, priority, rule);
     }
-}
-
-static enum ofperr
-ofputil_lookup_openflow_message(const struct ofputil_msg_category *cat,
-                                uint8_t version, uint32_t value,
-                                const struct ofputil_msg_type **typep)
-{
-    const struct ofputil_msg_type *type;
 
-    for (type = cat->types; type < &cat->types[cat->n_types]; type++) {
-        if (type->value == value
-            && (!type->ofp_version || version == type->ofp_version)) {
-            *typep = type;
-            return 0;
+    case OFPMT_OXM:
+        if (padded_match_len) {
+            *padded_match_len = ROUND_UP(match_len, 8);
         }
-    }
+        return oxm_pull_match(buf, priority, rule);
 
-    VLOG_WARN_RL(&bad_ofmsg_rl, "received %s of unknown type %"PRIu32,
-                 cat->name, value);
-    return cat->missing_error;
+    default:
+        return OFPERR_OFPBMC_BAD_TYPE;
+    }
 }
 
-static enum ofperr
-ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
-                      const struct ofputil_msg_type **typep)
-{
-    static const struct ofputil_msg_type nxt_messages[] = {
-        { OFPUTIL_NXT_ROLE_REQUEST, OFP10_VERSION,
-          NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST",
-          sizeof(struct nx_role_request), 0 },
-
-        { OFPUTIL_NXT_ROLE_REPLY, OFP10_VERSION,
-          NXT_ROLE_REPLY, "NXT_ROLE_REPLY",
-          sizeof(struct nx_role_request), 0 },
-
-        { OFPUTIL_NXT_SET_FLOW_FORMAT, OFP10_VERSION,
-          NXT_SET_FLOW_FORMAT, "NXT_SET_FLOW_FORMAT",
-          sizeof(struct nx_set_flow_format), 0 },
-
-        { OFPUTIL_NXT_SET_PACKET_IN_FORMAT, OFP10_VERSION,
-          NXT_SET_PACKET_IN_FORMAT, "NXT_SET_PACKET_IN_FORMAT",
-          sizeof(struct nx_set_packet_in_format), 0 },
-
-        { OFPUTIL_NXT_PACKET_IN, OFP10_VERSION,
-          NXT_PACKET_IN, "NXT_PACKET_IN",
-          sizeof(struct nx_packet_in), 1 },
-
-        { OFPUTIL_NXT_FLOW_MOD, OFP10_VERSION,
-          NXT_FLOW_MOD, "NXT_FLOW_MOD",
-          sizeof(struct nx_flow_mod), 8 },
-
-        { OFPUTIL_NXT_FLOW_REMOVED, OFP10_VERSION,
-          NXT_FLOW_REMOVED, "NXT_FLOW_REMOVED",
-          sizeof(struct nx_flow_removed), 8 },
-
-        { OFPUTIL_NXT_FLOW_MOD_TABLE_ID, OFP10_VERSION,
-          NXT_FLOW_MOD_TABLE_ID, "NXT_FLOW_MOD_TABLE_ID",
-          sizeof(struct nx_flow_mod_table_id), 0 },
-
-        { OFPUTIL_NXT_FLOW_AGE, OFP10_VERSION,
-          NXT_FLOW_AGE, "NXT_FLOW_AGE",
-          sizeof(struct nicira_header), 0 },
-
-        { OFPUTIL_NXT_SET_ASYNC_CONFIG, OFP10_VERSION,
-          NXT_SET_ASYNC_CONFIG, "NXT_SET_ASYNC_CONFIG",
-          sizeof(struct nx_async_config), 0 },
-
-        { OFPUTIL_NXT_SET_CONTROLLER_ID, OFP10_VERSION,
-          NXT_SET_CONTROLLER_ID, "NXT_SET_CONTROLLER_ID",
-          sizeof(struct nx_controller_id), 0 },
-    };
+/* Converts the ofp11_match in 'match' into a cls_rule in 'rule', with the
+ * given 'priority'.  Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match,
+                                  unsigned int priority,
+                                  struct cls_rule *rule)
+{
+    uint16_t wc = ntohl(match->wildcards);
+    uint8_t dl_src_mask[ETH_ADDR_LEN];
+    uint8_t dl_dst_mask[ETH_ADDR_LEN];
+    bool ipv4, arp;
+    int i;
 
-    static const struct ofputil_msg_category nxt_category = {
-        "Nicira extension message",
-        nxt_messages, ARRAY_SIZE(nxt_messages),
-        OFPERR_OFPBRC_BAD_SUBTYPE
-    };
+    cls_rule_init_catchall(rule, priority);
 
-    const struct ofp_vendor_header *ovh;
-    const struct nicira_header *nh;
+    if (!(wc & OFPFW11_IN_PORT)) {
+        uint16_t ofp_port;
+        enum ofperr error;
 
-    if (length < sizeof(struct ofp_vendor_header)) {
-        if (length == ntohs(oh->length)) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor message");
+        error = ofputil_port_from_ofp11(match->in_port, &ofp_port);
+        if (error) {
+            return OFPERR_OFPBMC_BAD_VALUE;
         }
-        return OFPERR_OFPBRC_BAD_LEN;
+        cls_rule_set_in_port(rule, ofp_port);
     }
 
-    ovh = (const struct ofp_vendor_header *) oh;
-    if (ovh->vendor != htonl(NX_VENDOR_ID)) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor message for unknown "
-                     "vendor %"PRIx32, ntohl(ovh->vendor));
-        return OFPERR_OFPBRC_BAD_VENDOR;
+    for (i = 0; i < ETH_ADDR_LEN; i++) {
+        dl_src_mask[i] = ~match->dl_src_mask[i];
     }
+    cls_rule_set_dl_src_masked(rule, match->dl_src, dl_src_mask);
 
-    if (length < sizeof(struct nicira_header)) {
-        if (length == ntohs(oh->length)) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "received Nicira vendor message of "
-                         "length %u (expected at least %zu)",
-                         ntohs(ovh->header.length),
-                         sizeof(struct nicira_header));
-        }
-        return OFPERR_OFPBRC_BAD_LEN;
+    for (i = 0; i < ETH_ADDR_LEN; i++) {
+        dl_dst_mask[i] = ~match->dl_dst_mask[i];
     }
+    cls_rule_set_dl_dst_masked(rule, match->dl_dst, dl_dst_mask);
 
-    nh = (const struct nicira_header *) oh;
-    return ofputil_lookup_openflow_message(&nxt_category, oh->version,
-                                           ntohl(nh->subtype), typep);
-}
-
-static enum ofperr
-check_nxstats_msg(const struct ofp_header *oh, size_t length)
-{
-    const struct ofp_stats_msg *osm = (const struct ofp_stats_msg *) oh;
-    ovs_be32 vendor;
+    if (!(wc & OFPFW11_DL_VLAN)) {
+        if (match->dl_vlan == htons(OFPVID11_NONE)) {
+            /* Match only packets without a VLAN tag. */
+            rule->flow.vlan_tci = htons(0);
+            rule->wc.vlan_tci_mask = htons(UINT16_MAX);
+        } else {
+            if (match->dl_vlan == htons(OFPVID11_ANY)) {
+                /* Match any packet with a VLAN tag regardless of VID. */
+                rule->flow.vlan_tci = htons(VLAN_CFI);
+                rule->wc.vlan_tci_mask = htons(VLAN_CFI);
+            } else if (ntohs(match->dl_vlan) < 4096) {
+                /* Match only packets with the specified VLAN VID. */
+                rule->flow.vlan_tci = htons(VLAN_CFI) | match->dl_vlan;
+                rule->wc.vlan_tci_mask = htons(VLAN_CFI | VLAN_VID_MASK);
+            } else {
+                /* Invalid VID. */
+                return OFPERR_OFPBMC_BAD_VALUE;
+            }
 
-    if (length < sizeof(struct ofp_vendor_stats_msg)) {
-        if (length == ntohs(oh->length)) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor stats message");
+            if (!(wc & OFPFW11_DL_VLAN_PCP)) {
+                if (match->dl_vlan_pcp <= 7) {
+                    rule->flow.vlan_tci |= htons(match->dl_vlan_pcp
+                                                 << VLAN_PCP_SHIFT);
+                    rule->wc.vlan_tci_mask |= htons(VLAN_PCP_MASK);
+                } else {
+                    /* Invalid PCP. */
+                    return OFPERR_OFPBMC_BAD_VALUE;
+                }
+            }
         }
-        return OFPERR_OFPBRC_BAD_LEN;
-    }
-
-    memcpy(&vendor, osm + 1, sizeof vendor);
-    if (vendor != htonl(NX_VENDOR_ID)) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor stats message for "
-                     "unknown vendor %"PRIx32, ntohl(vendor));
-        return OFPERR_OFPBRC_BAD_VENDOR;
     }
 
-    if (length < sizeof(struct nicira_stats_msg)) {
-        if (length == ntohs(osm->header.length)) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "truncated Nicira stats message");
-        }
-        return OFPERR_OFPBRC_BAD_LEN;
+    if (!(wc & OFPFW11_DL_TYPE)) {
+        cls_rule_set_dl_type(rule,
+                             ofputil_dl_type_from_openflow(match->dl_type));
     }
 
-    return 0;
-}
-
-static enum ofperr
-ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length,
-                            const struct ofputil_msg_type **typep)
-{
-    static const struct ofputil_msg_type nxst_requests[] = {
-        { OFPUTIL_NXST_FLOW_REQUEST, OFP10_VERSION,
-          NXST_FLOW, "NXST_FLOW request",
-          sizeof(struct nx_flow_stats_request), 8 },
-
-        { OFPUTIL_NXST_AGGREGATE_REQUEST, OFP10_VERSION,
-          NXST_AGGREGATE, "NXST_AGGREGATE request",
-          sizeof(struct nx_aggregate_stats_request), 8 },
-    };
-
-    static const struct ofputil_msg_category nxst_request_category = {
-        "Nicira extension statistics request",
-        nxst_requests, ARRAY_SIZE(nxst_requests),
-        OFPERR_OFPBRC_BAD_SUBTYPE
-    };
+    ipv4 = rule->flow.dl_type == htons(ETH_TYPE_IP);
+    arp = rule->flow.dl_type == htons(ETH_TYPE_ARP);
 
-    const struct nicira_stats_msg *nsm;
-    enum ofperr error;
+    if (ipv4 && !(wc & OFPFW11_NW_TOS)) {
+        if (match->nw_tos & ~IP_DSCP_MASK) {
+            /* Invalid TOS. */
+            return OFPERR_OFPBMC_BAD_VALUE;
+        }
 
-    error = check_nxstats_msg(oh, length);
-    if (error) {
-        return error;
+        cls_rule_set_nw_dscp(rule, match->nw_tos);
     }
 
-    nsm = (struct nicira_stats_msg *) oh;
-    return ofputil_lookup_openflow_message(&nxst_request_category, oh->version,
-                                           ntohl(nsm->subtype), typep);
-}
-
-static enum ofperr
-ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length,
-                          const struct ofputil_msg_type **typep)
-{
-    static const struct ofputil_msg_type nxst_replies[] = {
-        { OFPUTIL_NXST_FLOW_REPLY, OFP10_VERSION,
-          NXST_FLOW, "NXST_FLOW reply",
-          sizeof(struct nicira_stats_msg), 8 },
+    if (ipv4 || arp) {
+        if (!(wc & OFPFW11_NW_PROTO)) {
+            cls_rule_set_nw_proto(rule, match->nw_proto);
+        }
+        cls_rule_set_nw_src_masked(rule, match->nw_src, ~match->nw_src_mask);
+        cls_rule_set_nw_dst_masked(rule, match->nw_dst, ~match->nw_dst_mask);
+    }
 
-        { OFPUTIL_NXST_AGGREGATE_REPLY, OFP10_VERSION,
-          NXST_AGGREGATE, "NXST_AGGREGATE reply",
-          sizeof(struct nx_aggregate_stats_reply), 0 },
-    };
+#define OFPFW11_TP_ALL (OFPFW11_TP_SRC | OFPFW11_TP_DST)
+    if (ipv4 && (wc & OFPFW11_TP_ALL) != OFPFW11_TP_ALL) {
+        switch (rule->flow.nw_proto) {
+        case IPPROTO_ICMP:
+            /* "A.2.3 Flow Match Structures" in OF1.1 says:
+             *
+             *    The tp_src and tp_dst fields will be ignored unless the
+             *    network protocol specified is as TCP, UDP or SCTP.
+             *
+             * but I'm pretty sure we should support ICMP too, otherwise
+             * that's a regression from OF1.0. */
+            if (!(wc & OFPFW11_TP_SRC)) {
+                uint16_t icmp_type = ntohs(match->tp_src);
+                if (icmp_type < 0x100) {
+                    cls_rule_set_icmp_type(rule, icmp_type);
+                } else {
+                    return OFPERR_OFPBMC_BAD_FIELD;
+                }
+            }
+            if (!(wc & OFPFW11_TP_DST)) {
+                uint16_t icmp_code = ntohs(match->tp_dst);
+                if (icmp_code < 0x100) {
+                    cls_rule_set_icmp_code(rule, icmp_code);
+                } else {
+                    return OFPERR_OFPBMC_BAD_FIELD;
+                }
+            }
+            break;
 
-    static const struct ofputil_msg_category nxst_reply_category = {
-        "Nicira extension statistics reply",
-        nxst_replies, ARRAY_SIZE(nxst_replies),
-        OFPERR_OFPBRC_BAD_SUBTYPE
-    };
+        case IPPROTO_TCP:
+        case IPPROTO_UDP:
+            if (!(wc & (OFPFW11_TP_SRC))) {
+                cls_rule_set_tp_src(rule, match->tp_src);
+            }
+            if (!(wc & (OFPFW11_TP_DST))) {
+                cls_rule_set_tp_dst(rule, match->tp_dst);
+            }
+            break;
 
-    const struct nicira_stats_msg *nsm;
-    enum ofperr error;
+        case IPPROTO_SCTP:
+            /* We don't support SCTP and it seems that we should tell the
+             * controller, since OF1.1 implementations are supposed to. */
+            return OFPERR_OFPBMC_BAD_FIELD;
 
-    error = check_nxstats_msg(oh, length);
-    if (error) {
-        return error;
+        default:
+            /* OF1.1 says explicitly to ignore this. */
+            break;
+        }
     }
 
-    nsm = (struct nicira_stats_msg *) oh;
-    return ofputil_lookup_openflow_message(&nxst_reply_category, oh->version,
-                                           ntohl(nsm->subtype), typep);
-}
+    if (rule->flow.dl_type == htons(ETH_TYPE_MPLS) ||
+        rule->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+        enum { OFPFW11_MPLS_ALL = OFPFW11_MPLS_LABEL | OFPFW11_MPLS_TC };
 
-static enum ofperr
-check_stats_msg(const struct ofp_header *oh, size_t length)
-{
-    if (length < sizeof(struct ofp_stats_msg)) {
-        if (length == ntohs(oh->length)) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "truncated stats message");
+        if ((wc & OFPFW11_MPLS_ALL) != OFPFW11_MPLS_ALL) {
+            /* MPLS not supported. */
+            return OFPERR_OFPBMC_BAD_TAG;
         }
-        return OFPERR_OFPBRC_BAD_LEN;
+    }
+
+    if (match->metadata_mask != htonll(UINT64_MAX)) {
+        cls_rule_set_metadata_masked(rule, match->metadata,
+                                     ~match->metadata_mask);
     }
 
     return 0;
 }
 
-static enum ofperr
-ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length,
-                             const struct ofputil_msg_type **typep)
+/* Convert 'rule' into the OpenFlow 1.1 match structure 'match'. */
+void
+ofputil_cls_rule_to_ofp11_match(const struct cls_rule *rule,
+                                struct ofp11_match *match)
 {
-    static const struct ofputil_msg_type ofpst_requests[] = {
-        { OFPUTIL_OFPST_DESC_REQUEST, OFP10_VERSION,
-          OFPST_DESC, "OFPST_DESC request",
-          sizeof(struct ofp_stats_msg), 0 },
-
-        { OFPUTIL_OFPST_FLOW_REQUEST, OFP10_VERSION,
-          OFPST_FLOW, "OFPST_FLOW request",
-          sizeof(struct ofp_flow_stats_request), 0 },
-
-        { OFPUTIL_OFPST_AGGREGATE_REQUEST, OFP10_VERSION,
-          OFPST_AGGREGATE, "OFPST_AGGREGATE request",
-          sizeof(struct ofp_flow_stats_request), 0 },
-
-        { OFPUTIL_OFPST_TABLE_REQUEST, OFP10_VERSION,
-          OFPST_TABLE, "OFPST_TABLE request",
-          sizeof(struct ofp_stats_msg), 0 },
-
-        { OFPUTIL_OFPST_PORT_REQUEST, OFP10_VERSION,
-          OFPST_PORT, "OFPST_PORT request",
-          sizeof(struct ofp_port_stats_request), 0 },
-
-        { OFPUTIL_OFPST_QUEUE_REQUEST, OFP10_VERSION,
-          OFPST_QUEUE, "OFPST_QUEUE request",
-          sizeof(struct ofp_queue_stats_request), 0 },
-
-        { 0, 0,
-          OFPST_VENDOR, "OFPST_VENDOR request",
-          sizeof(struct ofp_vendor_stats_msg), 1 },
-    };
-
-    static const struct ofputil_msg_category ofpst_request_category = {
-        "OpenFlow statistics",
-        ofpst_requests, ARRAY_SIZE(ofpst_requests),
-        OFPERR_OFPBRC_BAD_STAT
-    };
+    uint32_t wc = 0;
+    int i;
 
-    const struct ofp_stats_msg *request = (const struct ofp_stats_msg *) oh;
-    enum ofperr error;
+    memset(match, 0, sizeof *match);
+    match->omh.type = htons(OFPMT_STANDARD);
+    match->omh.length = htons(OFPMT11_STANDARD_LENGTH);
 
-    error = check_stats_msg(oh, length);
-    if (error) {
-        return error;
+    if (rule->wc.wildcards & FWW_IN_PORT) {
+        wc |= OFPFW11_IN_PORT;
+    } else {
+        match->in_port = ofputil_port_to_ofp11(rule->flow.in_port);
     }
 
-    error = ofputil_lookup_openflow_message(&ofpst_request_category,
-                                            oh->version, ntohs(request->type),
-                                            typep);
-    if (!error && request->type == htons(OFPST_VENDOR)) {
-        error = ofputil_decode_nxst_request(oh, length, typep);
+    memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
+    for (i = 0; i < ETH_ADDR_LEN; i++) {
+        match->dl_src_mask[i] = ~rule->wc.dl_src_mask[i];
     }
-    return error;
-}
-
-static enum ofperr
-ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length,
-                           const struct ofputil_msg_type **typep)
-{
-    static const struct ofputil_msg_type ofpst_replies[] = {
-        { OFPUTIL_OFPST_DESC_REPLY, OFP10_VERSION,
-          OFPST_DESC, "OFPST_DESC reply",
-          sizeof(struct ofp_desc_stats), 0 },
-
-        { OFPUTIL_OFPST_FLOW_REPLY, OFP10_VERSION,
-          OFPST_FLOW, "OFPST_FLOW reply",
-          sizeof(struct ofp_stats_msg), 1 },
 
-        { OFPUTIL_OFPST_AGGREGATE_REPLY, OFP10_VERSION,
-          OFPST_AGGREGATE, "OFPST_AGGREGATE reply",
-          sizeof(struct ofp_aggregate_stats_reply), 0 },
-
-        { OFPUTIL_OFPST_TABLE_REPLY, OFP10_VERSION,
-          OFPST_TABLE, "OFPST_TABLE reply",
-          sizeof(struct ofp_stats_msg), sizeof(struct ofp_table_stats) },
-
-        { OFPUTIL_OFPST_PORT_REPLY, OFP10_VERSION,
-          OFPST_PORT, "OFPST_PORT reply",
-          sizeof(struct ofp_stats_msg), sizeof(struct ofp_port_stats) },
-
-        { OFPUTIL_OFPST_QUEUE_REPLY, OFP10_VERSION,
-          OFPST_QUEUE, "OFPST_QUEUE reply",
-          sizeof(struct ofp_stats_msg), sizeof(struct ofp_queue_stats) },
-
-        { 0, 0,
-          OFPST_VENDOR, "OFPST_VENDOR reply",
-          sizeof(struct ofp_vendor_stats_msg), 1 },
-    };
-
-    static const struct ofputil_msg_category ofpst_reply_category = {
-        "OpenFlow statistics",
-        ofpst_replies, ARRAY_SIZE(ofpst_replies),
-        OFPERR_OFPBRC_BAD_STAT
-    };
+    memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN);
+    for (i = 0; i < ETH_ADDR_LEN; i++) {
+        match->dl_dst_mask[i] = ~rule->wc.dl_dst_mask[i];
+    }
 
-    const struct ofp_stats_msg *reply = (const struct ofp_stats_msg *) oh;
-    enum ofperr error;
+    if (rule->wc.vlan_tci_mask == htons(0)) {
+        wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP;
+    } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
+               && !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
+        match->dl_vlan = htons(OFPVID11_NONE);
+        wc |= OFPFW11_DL_VLAN_PCP;
+    } else {
+        if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
+            match->dl_vlan = htons(OFPVID11_ANY);
+        } else {
+            match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci));
+        }
 
-    error = check_stats_msg(oh, length);
-    if (error) {
-        return error;
+        if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) {
+            wc |= OFPFW11_DL_VLAN_PCP;
+        } else {
+            match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci);
+        }
     }
 
-    error = ofputil_lookup_openflow_message(&ofpst_reply_category, oh->version,
-                                           ntohs(reply->type), typep);
-    if (!error && reply->type == htons(OFPST_VENDOR)) {
-        error = ofputil_decode_nxst_reply(oh, length, typep);
+    if (rule->wc.wildcards & FWW_DL_TYPE) {
+        wc |= OFPFW11_DL_TYPE;
+    } else {
+        match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type);
     }
-    return error;
-}
-
-static enum ofperr
-ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length,
-                          const struct ofputil_msg_type **typep)
-{
-    static const struct ofputil_msg_type ofpt_messages[] = {
-        { OFPUTIL_OFPT_HELLO, OFP10_VERSION,
-          OFPT_HELLO, "OFPT_HELLO",
-          sizeof(struct ofp_hello), 1 },
-
-        { OFPUTIL_OFPT_ERROR, 0,
-          OFPT_ERROR, "OFPT_ERROR",
-          sizeof(struct ofp_error_msg), 1 },
-
-        { OFPUTIL_OFPT_ECHO_REQUEST, OFP10_VERSION,
-          OFPT_ECHO_REQUEST, "OFPT_ECHO_REQUEST",
-          sizeof(struct ofp_header), 1 },
-
-        { OFPUTIL_OFPT_ECHO_REPLY, OFP10_VERSION,
-          OFPT_ECHO_REPLY, "OFPT_ECHO_REPLY",
-          sizeof(struct ofp_header), 1 },
-
-        { OFPUTIL_OFPT_FEATURES_REQUEST, OFP10_VERSION,
-          OFPT_FEATURES_REQUEST, "OFPT_FEATURES_REQUEST",
-          sizeof(struct ofp_header), 0 },
-
-        { OFPUTIL_OFPT_FEATURES_REPLY, OFP10_VERSION,
-          OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY",
-          sizeof(struct ofp_switch_features), sizeof(struct ofp10_phy_port) },
-        { OFPUTIL_OFPT_FEATURES_REPLY, OFP11_VERSION,
-          OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY",
-          sizeof(struct ofp_switch_features), sizeof(struct ofp11_port) },
-
-        { OFPUTIL_OFPT_GET_CONFIG_REQUEST, OFP10_VERSION,
-          OFPT_GET_CONFIG_REQUEST, "OFPT_GET_CONFIG_REQUEST",
-          sizeof(struct ofp_header), 0 },
-
-        { OFPUTIL_OFPT_GET_CONFIG_REPLY, OFP10_VERSION,
-          OFPT_GET_CONFIG_REPLY, "OFPT_GET_CONFIG_REPLY",
-          sizeof(struct ofp_switch_config), 0 },
-
-        { OFPUTIL_OFPT_SET_CONFIG, OFP10_VERSION,
-          OFPT_SET_CONFIG, "OFPT_SET_CONFIG",
-          sizeof(struct ofp_switch_config), 0 },
-
-        { OFPUTIL_OFPT_PACKET_IN, OFP10_VERSION,
-          OFPT_PACKET_IN, "OFPT_PACKET_IN",
-          offsetof(struct ofp_packet_in, data), 1 },
-
-        { OFPUTIL_OFPT_FLOW_REMOVED, OFP10_VERSION,
-          OFPT_FLOW_REMOVED, "OFPT_FLOW_REMOVED",
-          sizeof(struct ofp_flow_removed), 0 },
-
-        { OFPUTIL_OFPT_PORT_STATUS, OFP10_VERSION,
-          OFPT_PORT_STATUS, "OFPT_PORT_STATUS",
-          sizeof(struct ofp_port_status) + sizeof(struct ofp10_phy_port), 0 },
-        { OFPUTIL_OFPT_PORT_STATUS, OFP11_VERSION,
-          OFPT_PORT_STATUS, "OFPT_PORT_STATUS",
-          sizeof(struct ofp_port_status) + sizeof(struct ofp11_port), 0 },
-
-        { OFPUTIL_OFPT_PACKET_OUT, OFP10_VERSION,
-          OFPT10_PACKET_OUT, "OFPT_PACKET_OUT",
-          sizeof(struct ofp_packet_out), 1 },
-
-        { OFPUTIL_OFPT_FLOW_MOD, OFP10_VERSION,
-          OFPT10_FLOW_MOD, "OFPT_FLOW_MOD",
-          sizeof(struct ofp_flow_mod), 1 },
-
-        { OFPUTIL_OFPT_PORT_MOD, OFP10_VERSION,
-          OFPT10_PORT_MOD, "OFPT_PORT_MOD",
-          sizeof(struct ofp10_port_mod), 0 },
-        { OFPUTIL_OFPT_PORT_MOD, OFP11_VERSION,
-          OFPT11_PORT_MOD, "OFPT_PORT_MOD",
-          sizeof(struct ofp11_port_mod), 0 },
-
-        { 0, OFP10_VERSION,
-          OFPT10_STATS_REQUEST, "OFPT_STATS_REQUEST",
-          sizeof(struct ofp_stats_msg), 1 },
-
-        { 0, OFP10_VERSION,
-          OFPT10_STATS_REPLY, "OFPT_STATS_REPLY",
-          sizeof(struct ofp_stats_msg), 1 },
-
-        { OFPUTIL_OFPT_BARRIER_REQUEST, OFP10_VERSION,
-          OFPT10_BARRIER_REQUEST, "OFPT_BARRIER_REQUEST",
-          sizeof(struct ofp_header), 0 },
-
-        { OFPUTIL_OFPT_BARRIER_REPLY, OFP10_VERSION,
-          OFPT10_BARRIER_REPLY, "OFPT_BARRIER_REPLY",
-          sizeof(struct ofp_header), 0 },
-
-        { 0, 0,
-          OFPT_VENDOR, "OFPT_VENDOR",
-          sizeof(struct ofp_vendor_header), 1 },
-    };
-
-    static const struct ofputil_msg_category ofpt_category = {
-        "OpenFlow message",
-        ofpt_messages, ARRAY_SIZE(ofpt_messages),
-        OFPERR_OFPBRC_BAD_TYPE
-    };
 
-    enum ofperr error;
+    if (rule->wc.wildcards & FWW_NW_DSCP) {
+        wc |= OFPFW11_NW_TOS;
+    } else {
+        match->nw_tos = rule->flow.nw_tos & IP_DSCP_MASK;
+    }
 
-    error = ofputil_lookup_openflow_message(&ofpt_category, oh->version,
-                                            oh->type, typep);
-    if (!error) {
-        switch ((oh->version << 8) | oh->type) {
-        case (OFP10_VERSION << 8) | OFPT_VENDOR:
-        case (OFP11_VERSION << 8) | OFPT_VENDOR:
-            error = ofputil_decode_vendor(oh, length, typep);
-            break;
+    if (rule->wc.wildcards & FWW_NW_PROTO) {
+        wc |= OFPFW11_NW_PROTO;
+    } else {
+        match->nw_proto = rule->flow.nw_proto;
+    }
 
-        case (OFP10_VERSION << 8) | OFPT10_STATS_REQUEST:
-        case (OFP11_VERSION << 8) | OFPT11_STATS_REQUEST:
-            error = ofputil_decode_ofpst_request(oh, length, typep);
-            break;
+    match->nw_src = rule->flow.nw_src;
+    match->nw_src_mask = ~rule->wc.nw_src_mask;
+    match->nw_dst = rule->flow.nw_dst;
+    match->nw_dst_mask = ~rule->wc.nw_dst_mask;
 
-        case (OFP10_VERSION << 8) | OFPT10_STATS_REPLY:
-        case (OFP11_VERSION << 8) | OFPT11_STATS_REPLY:
-            error = ofputil_decode_ofpst_reply(oh, length, typep);
+    if (!rule->wc.tp_src_mask) {
+        wc |= OFPFW11_TP_SRC;
+    } else {
+        match->tp_src = rule->flow.tp_src;
+    }
 
-        default:
-            break;
-        }
+    if (!rule->wc.tp_dst_mask) {
+        wc |= OFPFW11_TP_DST;
+    } else {
+        match->tp_dst = rule->flow.tp_dst;
     }
-    return error;
-}
 
-/* Decodes the message type represented by 'oh'.  Returns 0 if successful or an
- * OpenFlow error code on failure.  Either way, stores in '*typep' a type
- * structure that can be inspected with the ofputil_msg_type_*() functions.
- *
- * oh->length must indicate the correct length of the message (and must be at
- * least sizeof(struct ofp_header)).
- *
- * Success indicates that 'oh' is at least as long as the minimum-length
- * message of its type. */
-enum ofperr
-ofputil_decode_msg_type(const struct ofp_header *oh,
-                        const struct ofputil_msg_type **typep)
-{
-    size_t length = ntohs(oh->length);
-    enum ofperr error;
+    /* MPLS not supported. */
+    wc |= OFPFW11_MPLS_LABEL;
+    wc |= OFPFW11_MPLS_TC;
 
-    error = ofputil_decode_msg_type__(oh, length, typep);
-    if (!error) {
-        error = ofputil_check_length(*typep, length);
-    }
-    if (error) {
-        *typep = &ofputil_invalid_type;
-    }
-    return error;
+    match->metadata = rule->flow.metadata;
+    match->metadata_mask = ~rule->wc.metadata_mask;
+
+    match->wildcards = htonl(wc);
 }
 
-/* Decodes the message type represented by 'oh', of which only the first
- * 'length' bytes are available.  Returns 0 if successful or an OpenFlow error
- * code on failure.  Either way, stores in '*typep' a type structure that can
- * be inspected with the ofputil_msg_type_*() functions.  */
-enum ofperr
-ofputil_decode_msg_type_partial(const struct ofp_header *oh, size_t length,
-                                const struct ofputil_msg_type **typep)
+/* Given a 'dl_type' value in the format used in struct flow, returns the
+ * corresponding 'dl_type' value for use in an ofp10_match or ofp11_match
+ * structure. */
+ovs_be16
+ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type)
 {
-    enum ofperr error;
-
-    error = (length >= sizeof *oh
-             ? ofputil_decode_msg_type__(oh, length, typep)
-             : OFPERR_OFPBRC_BAD_LEN);
-    if (error) {
-        *typep = &ofputil_invalid_type;
-    }
-    return error;
+    return (flow_dl_type == htons(FLOW_DL_TYPE_NONE)
+            ? htons(OFP_DL_TYPE_NOT_ETH_TYPE)
+            : flow_dl_type);
 }
 
-/* Returns an OFPUTIL_* message type code for 'type'. */
-enum ofputil_msg_code
-ofputil_msg_type_code(const struct ofputil_msg_type *type)
+/* Given a 'dl_type' value in the format used in an ofp10_match or ofp11_match
+ * structure, returns the corresponding 'dl_type' value for use in struct
+ * flow. */
+ovs_be16
+ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type)
 {
-    return type->code;
+    return (ofp_dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
+            ? htons(FLOW_DL_TYPE_NONE)
+            : ofp_dl_type);
 }
 \f
 /* Protocols. */
@@ -879,17 +607,22 @@ size_t ofputil_n_flow_dump_protocols = ARRAY_SIZE(ofputil_flow_dump_protocols);
  * 1.0, 0x02 for OpenFlow 1.1).  Returns 0 if 'version' is not supported or
  * outside the valid range.  */
 enum ofputil_protocol
-ofputil_protocol_from_ofp_version(int version)
+ofputil_protocol_from_ofp_version(enum ofp_version version)
 {
     switch (version) {
-    case OFP10_VERSION: return OFPUTIL_P_OF10;
-    default: return 0;
+    case OFP10_VERSION:
+        return OFPUTIL_P_OF10;
+    case OFP12_VERSION:
+        return OFPUTIL_P_OF12;
+    case OFP11_VERSION:
+    default:
+        return 0;
     }
 }
 
-/* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION or
- * OFP11_VERSION) that corresponds to 'protocol'. */
-uint8_t
+/* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION,
+ * OFP11_VERSION or OFP12_VERSION) that corresponds to 'protocol'. */
+enum ofp_version
 ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol)
 {
     switch (protocol) {
@@ -898,6 +631,8 @@ ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol)
     case OFPUTIL_P_NXM:
     case OFPUTIL_P_NXM_TID:
         return OFP10_VERSION;
+    case OFPUTIL_P_OF12:
+        return OFP12_VERSION;
     }
 
     NOT_REACHED();
@@ -933,6 +668,9 @@ ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable)
     case OFPUTIL_P_NXM_TID:
         return enable ? OFPUTIL_P_NXM_TID : OFPUTIL_P_NXM;
 
+    case OFPUTIL_P_OF12:
+        return OFPUTIL_P_OF12;
+
     default:
         NOT_REACHED();
     }
@@ -964,6 +702,9 @@ ofputil_protocol_set_base(enum ofputil_protocol cur,
     case OFPUTIL_P_NXM_TID:
         return ofputil_protocol_set_tid(OFPUTIL_P_NXM, tid);
 
+    case OFPUTIL_P_OF12:
+        return ofputil_protocol_set_tid(OFPUTIL_P_OF12, tid);
+
     default:
         NOT_REACHED();
     }
@@ -991,6 +732,9 @@ ofputil_protocol_to_string(enum ofputil_protocol protocol)
 
     case OFPUTIL_P_OF10_TID:
         return "OpenFlow10+table_id";
+
+    case OFPUTIL_P_OF12:
+        return NULL;
     }
 
     /* Check abbreviations. */
@@ -1166,15 +910,26 @@ ofputil_usable_protocols(const struct cls_rule *rule)
 {
     const struct flow_wildcards *wc = &rule->wc;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
 
-    /* Only NXM supports separately wildcards the Ethernet multicast bit. */
-    if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
+    /* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
+    if (!eth_mask_is_exact(wc->dl_src_mask)
+        && !eth_addr_is_zero(wc->dl_src_mask)) {
+        return OFPUTIL_P_NXM_ANY;
+    }
+    if (!eth_mask_is_exact(wc->dl_dst_mask)
+        && !eth_addr_is_zero(wc->dl_dst_mask)) {
+        return OFPUTIL_P_NXM_ANY;
+    }
+
+    /* NXM and OF1.1+ support matching metadata. */
+    if (wc->metadata_mask != htonll(0)) {
         return OFPUTIL_P_NXM_ANY;
     }
 
     /* Only NXM supports matching ARP hardware addresses. */
-    if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) {
+    if (!eth_addr_is_zero(wc->arp_sha_mask) ||
+        !eth_addr_is_zero(wc->arp_tha_mask)) {
         return OFPUTIL_P_NXM_ANY;
     }
 
@@ -1200,7 +955,7 @@ ofputil_usable_protocols(const struct cls_rule *rule)
     }
 
     /* Only NXM supports matching IPv6 flow label. */
-    if (!(wc->wildcards & FWW_IPV6_LABEL)) {
+    if (wc->ipv6_label_mask) {
         return OFPUTIL_P_NXM_ANY;
     }
 
@@ -1214,6 +969,11 @@ ofputil_usable_protocols(const struct cls_rule *rule)
         return OFPUTIL_P_NXM_ANY;
     }
 
+    /* Only NXM supports non-CIDR IPv4 address masks. */
+    if (!ip_is_cidr(wc->nw_src_mask) || !ip_is_cidr(wc->nw_dst_mask)) {
+        return OFPUTIL_P_NXM_ANY;
+    }
+
     /* Only NXM supports bitwise matching on transport port. */
     if ((wc->tp_src_mask && wc->tp_src_mask != htons(UINT16_MAX)) ||
         (wc->tp_dst_mask && wc->tp_dst_mask != htons(UINT16_MAX))) {
@@ -1251,6 +1011,9 @@ ofputil_encode_set_protocol(enum ofputil_protocol current,
         case OFPUTIL_P_OF10:
             return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10);
 
+        case OFPUTIL_P_OF12:
+            return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW12);
+
         case OFPUTIL_P_OF10_TID:
         case OFPUTIL_P_NXM_TID:
             NOT_REACHED();
@@ -1280,7 +1043,8 @@ ofputil_encode_nx_set_flow_format(enum nx_flow_format nxff)
 
     assert(ofputil_nx_flow_format_is_valid(nxff));
 
-    sff = make_nxmsg(sizeof *sff, NXT_SET_FLOW_FORMAT, &msg);
+    msg = ofpraw_alloc(OFPRAW_NXT_SET_FLOW_FORMAT, OFP10_VERSION, 0);
+    sff = ofpbuf_put_zeros(msg, sizeof *sff);
     sff->format = htonl(nxff);
 
     return msg;
@@ -1298,6 +1062,9 @@ ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format)
     case NXFF_NXM:
         return OFPUTIL_P_NXM;
 
+    case NXFF_OPENFLOW12:
+        return OFPUTIL_P_OF12;
+
     default:
         return 0;
     }
@@ -1320,6 +1087,8 @@ ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format)
         return "openflow10";
     case NXFF_NXM:
         return "nxm";
+    case NXFF_OPENFLOW12:
+        return "openflow12";
     default:
         NOT_REACHED();
     }
@@ -1331,7 +1100,8 @@ ofputil_make_set_packet_in_format(enum nx_packet_in_format packet_in_format)
     struct nx_set_packet_in_format *spif;
     struct ofpbuf *msg;
 
-    spif = make_nxmsg(sizeof *spif, NXT_SET_PACKET_IN_FORMAT, &msg);
+    msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT, OFP10_VERSION, 0);
+    spif = ofpbuf_put_zeros(msg, sizeof *spif);
     spif->format = htonl(packet_in_format);
 
     return msg;
@@ -1345,7 +1115,8 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
     struct nx_flow_mod_table_id *nfmti;
     struct ofpbuf *msg;
 
-    nfmti = make_nxmsg(sizeof *nfmti, NXT_FLOW_MOD_TABLE_ID, &msg);
+    msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD_TABLE_ID, OFP10_VERSION, 0);
+    nfmti = ofpbuf_put_zeros(msg, sizeof *nfmti);
     nfmti->set = flow_mod_table_id;
     return msg;
 }
@@ -1354,154 +1125,226 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
  * flow_mod in 'fm'.  Returns 0 if successful, otherwise an OpenFlow error
  * code.
  *
- * Does not validate the flow_mod actions. */
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of 'oh''s actions.
+ * The caller must initialize 'ofpacts' and retains ownership of it.
+ * 'fm->ofpacts' will point into the 'ofpacts' buffer.
+ *
+ * Does not validate the flow_mod actions.  The caller should do that, with
+ * ofpacts_check(). */
 enum ofperr
 ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                         const struct ofp_header *oh,
-                        enum ofputil_protocol protocol)
+                        enum ofputil_protocol protocol,
+                        struct ofpbuf *ofpacts)
 {
-    const struct ofputil_msg_type *type;
     uint16_t command;
     struct ofpbuf b;
+    enum ofpraw raw;
 
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
-
-    ofputil_decode_msg_type(oh, &type);
-    if (ofputil_msg_type_code(type) == OFPUTIL_OFPT_FLOW_MOD) {
-        /* Standard OpenFlow flow_mod. */
-        const struct ofp_flow_mod *ofm;
-        uint16_t priority;
+    raw = ofpraw_pull_assert(&b);
+    if (raw == OFPRAW_OFPT11_FLOW_MOD) {
+        /* Standard OpenFlow 1.1 flow_mod. */
+        const struct ofp11_flow_mod *ofm;
         enum ofperr error;
 
-        /* Dissect the message. */
         ofm = ofpbuf_pull(&b, sizeof *ofm);
-        error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
+
+        error = ofputil_pull_ofp11_match(&b, ntohs(ofm->priority), &fm->cr,
+                                         NULL);
         if (error) {
             return error;
         }
 
-        /* Set priority based on original wildcards.  Normally we'd allow
-         * ofputil_cls_rule_from_match() to do this for us, but
-         * ofputil_normalize_rule() can put wildcards where the original flow
-         * didn't have them. */
-        priority = ntohs(ofm->priority);
-        if (!(ofm->match.wildcards & htonl(OFPFW_ALL))) {
-            priority = UINT16_MAX;
+        error = ofpacts_pull_openflow11_instructions(&b, b.size, ofpacts);
+        if (error) {
+            return error;
         }
 
-        /* Translate the rule. */
-        ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr);
-        ofputil_normalize_rule(&fm->cr);
-
         /* Translate the message. */
-        fm->cookie = ofm->cookie;
-        fm->cookie_mask = htonll(UINT64_MAX);
-        command = ntohs(ofm->command);
+        if (ofm->command == OFPFC_ADD) {
+            fm->cookie = htonll(0);
+            fm->cookie_mask = htonll(0);
+            fm->new_cookie = ofm->cookie;
+        } else {
+            /* XXX */
+            fm->cookie = ofm->cookie;
+            fm->cookie_mask = ofm->cookie_mask;
+            fm->new_cookie = htonll(UINT64_MAX);
+        }
+        fm->command = ofm->command;
+        fm->table_id = ofm->table_id;
         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->flags = ntohs(ofm->flags);
-    } else if (ofputil_msg_type_code(type) == OFPUTIL_NXT_FLOW_MOD) {
-        /* Nicira extended flow_mod. */
-        const struct nx_flow_mod *nfm;
-        enum ofperr error;
-
-        /* Dissect the message. */
-        nfm = ofpbuf_pull(&b, sizeof *nfm);
-        error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
-                              &fm->cr, &fm->cookie, &fm->cookie_mask);
+        error = ofputil_port_from_ofp11(ofm->out_port, &fm->out_port);
         if (error) {
             return error;
         }
-        error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
-        if (error) {
-            return error;
+        if (ofm->out_group != htonl(OFPG_ANY)) {
+            return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED;
         }
+        fm->flags = ntohs(ofm->flags);
+    } else {
+        if (raw == OFPRAW_OFPT10_FLOW_MOD) {
+            /* Standard OpenFlow 1.0 flow_mod. */
+            const struct ofp10_flow_mod *ofm;
+            uint16_t priority;
+            enum ofperr error;
+
+            /* Get the ofp10_flow_mod. */
+            ofm = ofpbuf_pull(&b, sizeof *ofm);
+
+            /* Set priority based on original wildcards.  Normally we'd allow
+             * ofputil_cls_rule_from_match() to do this for us, but
+             * ofputil_normalize_rule() can put wildcards where the original
+             * flow didn't have them. */
+            priority = ntohs(ofm->priority);
+            if (!(ofm->match.wildcards & htonl(OFPFW10_ALL))) {
+                priority = UINT16_MAX;
+            }
 
-        /* Translate the message. */
-        command = ntohs(nfm->command);
-        if (command == OFPFC_ADD) {
-            if (fm->cookie_mask) {
-                /* The "NXM_NX_COOKIE*" matches are not valid for flow
-                 * additions.  Additions must use the "cookie" field of
-                 * the "nx_flow_mod" structure. */
-                return OFPERR_NXBRC_NXM_INVALID;
-            } else {
-                fm->cookie = nfm->cookie;
-                fm->cookie_mask = htonll(UINT64_MAX);
+            /* Translate the rule. */
+            ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr);
+            ofputil_normalize_rule(&fm->cr);
+
+            /* Now get the actions. */
+            error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+            if (error) {
+                return error;
             }
-        }
-        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->flags = ntohs(nfm->flags);
-    } else {
-        NOT_REACHED();
-    }
 
-    if (protocol & OFPUTIL_P_TID) {
-        fm->command = command & 0xff;
-        fm->table_id = command >> 8;
-    } else {
-        fm->command = command;
-        fm->table_id = 0xff;
+            /* Translate the message. */
+            command = ntohs(ofm->command);
+            fm->cookie = htonll(0);
+            fm->cookie_mask = htonll(0);
+            fm->new_cookie = ofm->cookie;
+            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->flags = ntohs(ofm->flags);
+        } else if (raw == OFPRAW_NXT_FLOW_MOD) {
+            /* Nicira extended flow_mod. */
+            const struct nx_flow_mod *nfm;
+            enum ofperr error;
+
+            /* Dissect the message. */
+            nfm = ofpbuf_pull(&b, sizeof *nfm);
+            error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
+                                  &fm->cr, &fm->cookie, &fm->cookie_mask);
+            if (error) {
+                return error;
+            }
+            error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+            if (error) {
+                return error;
+            }
+
+            /* Translate the message. */
+            command = ntohs(nfm->command);
+            if ((command & 0xff) == OFPFC_ADD && fm->cookie_mask) {
+                /* Flow additions may only set a new cookie, not match an
+                 * existing cookie. */
+                return OFPERR_NXBRC_NXM_INVALID;
+            }
+            fm->new_cookie = nfm->cookie;
+            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->flags = ntohs(nfm->flags);
+        } else {
+            NOT_REACHED();
+        }
+
+        if (protocol & OFPUTIL_P_TID) {
+            fm->command = command & 0xff;
+            fm->table_id = command >> 8;
+        } else {
+            fm->command = command;
+            fm->table_id = 0xff;
+        }
     }
 
+    fm->ofpacts = ofpacts->data;
+    fm->ofpacts_len = ofpacts->size;
+
     return 0;
 }
 
+static ovs_be16
+ofputil_tid_command(const struct ofputil_flow_mod *fm,
+                    enum ofputil_protocol protocol)
+{
+    return htons(protocol & OFPUTIL_P_TID
+                 ? (fm->command & 0xff) | (fm->table_id << 8)
+                 : fm->command);
+}
+
 /* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
- * 'protocol' and returns the message.
- *
- * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
- * enabled, false otherwise. */
+ * 'protocol' and returns the message. */
 struct ofpbuf *
 ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
                         enum ofputil_protocol protocol)
 {
-    size_t actions_len = fm->n_actions * sizeof *fm->actions;
-    struct ofp_flow_mod *ofm;
-    struct nx_flow_mod *nfm;
     struct ofpbuf *msg;
-    uint16_t command;
-    int match_len;
-
-    command = (protocol & OFPUTIL_P_TID
-               ? (fm->command & 0xff) | (fm->table_id << 8)
-               : fm->command);
 
     switch (protocol) {
+    case OFPUTIL_P_OF12: {
+        struct ofp11_flow_mod *ofm;
+
+        msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, OFP12_VERSION,
+                           NXM_TYPICAL_LEN + fm->ofpacts_len);
+        ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
+        ofm->cookie = fm->new_cookie;
+        ofm->cookie_mask = fm->cookie_mask;
+        ofm->table_id = fm->table_id;
+        ofm->command = fm->command;
+        ofm->idle_timeout = htons(fm->idle_timeout);
+        ofm->hard_timeout = htons(fm->hard_timeout);
+        ofm->priority = htons(fm->cr.priority);
+        ofm->buffer_id = htonl(fm->buffer_id);
+        ofm->out_port = ofputil_port_to_ofp11(fm->out_port);
+        ofm->out_group = htonl(OFPG11_ANY);
+        ofm->flags = htons(fm->flags);
+        oxm_put_match(msg, &fm->cr);
+        ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg);
+        break;
+    }
+
     case OFPUTIL_P_OF10:
-    case OFPUTIL_P_OF10_TID:
-        msg = ofpbuf_new(sizeof *ofm + actions_len);
-        ofm = put_openflow(sizeof *ofm, OFPT10_FLOW_MOD, msg);
-        ofputil_cls_rule_to_match(&fm->cr, &ofm->match);
-        ofm->cookie = fm->cookie;
-        ofm->command = htons(command);
+    case OFPUTIL_P_OF10_TID: {
+        struct ofp10_flow_mod *ofm;
+
+        msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION,
+                           fm->ofpacts_len);
+        ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
+        ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match);
+        ofm->cookie = fm->new_cookie;
+        ofm->command = ofputil_tid_command(fm, protocol);
         ofm->idle_timeout = htons(fm->idle_timeout);
         ofm->hard_timeout = htons(fm->hard_timeout);
         ofm->priority = htons(fm->cr.priority);
         ofm->buffer_id = htonl(fm->buffer_id);
         ofm->out_port = htons(fm->out_port);
         ofm->flags = htons(fm->flags);
+        ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
         break;
+    }
 
     case OFPUTIL_P_NXM:
-    case OFPUTIL_P_NXM_TID:
-        msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len);
-        put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg);
-        nfm = msg->data;
-        nfm->command = htons(command);
-        if (command == OFPFC_ADD) {
-            nfm->cookie = fm->cookie;
-            match_len = nx_put_match(msg, &fm->cr, 0, 0);
-        } else {
-            nfm->cookie = 0;
-            match_len = nx_put_match(msg, &fm->cr,
-                                     fm->cookie, fm->cookie_mask);
-        }
+    case OFPUTIL_P_NXM_TID: {
+        struct nx_flow_mod *nfm;
+        int match_len;
+
+        msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD, OFP10_VERSION,
+                           NXM_TYPICAL_LEN + fm->ofpacts_len);
+        nfm = ofpbuf_put_zeros(msg, sizeof *nfm);
+        nfm->command = ofputil_tid_command(fm, protocol);
+        nfm->cookie = fm->new_cookie;
+        match_len = nx_put_match(msg, &fm->cr, fm->cookie, fm->cookie_mask);
+        nfm = msg->l3;
         nfm->idle_timeout = htons(fm->idle_timeout);
         nfm->hard_timeout = htons(fm->hard_timeout);
         nfm->priority = htons(fm->cr.priority);
@@ -1509,14 +1352,15 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         nfm->out_port = htons(fm->out_port);
         nfm->flags = htons(fm->flags);
         nfm->match_len = htons(match_len);
+        ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
         break;
+    }
 
     default:
         NOT_REACHED();
     }
 
-    ofpbuf_put(msg, fm->actions, actions_len);
-    update_openflow_length(msg);
+    ofpmsg_update_length(msg);
     return msg;
 }
 
@@ -1540,7 +1384,9 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
         if (fm->table_id != 0xff) {
             usable_protocols &= OFPUTIL_P_TID;
         }
-        if (fm->command != OFPFC_ADD && fm->cookie_mask != htonll(0)) {
+
+        /* Matching of the cookie is only supported through NXM. */
+        if (fm->cookie_mask != htonll(0)) {
             usable_protocols &= OFPUTIL_P_NXM_ANY;
         }
     }
@@ -1550,15 +1396,12 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
 }
 
 static enum ofperr
-ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
-                                  const struct ofp_header *oh,
-                                  bool aggregate)
+ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
+                                    const struct ofp10_flow_stats_request *ofsr,
+                                    bool aggregate)
 {
-    const struct ofp_flow_stats_request *ofsr =
-        (const struct ofp_flow_stats_request *) oh;
-
     fsr->aggregate = aggregate;
-    ofputil_cls_rule_from_match(&ofsr->match, 0, &fsr->match);
+    ofputil_cls_rule_from_ofp10_match(&ofsr->match, 0, &fsr->match);
     fsr->out_port = ntohs(ofsr->out_port);
     fsr->table_id = ofsr->table_id;
     fsr->cookie = fsr->cookie_mask = htonll(0);
@@ -1566,24 +1409,47 @@ ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
     return 0;
 }
 
+static enum ofperr
+ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
+                                    struct ofpbuf *b, bool aggregate)
+{
+    const struct ofp11_flow_stats_request *ofsr;
+    enum ofperr error;
+
+    ofsr = ofpbuf_pull(b, sizeof *ofsr);
+    fsr->aggregate = aggregate;
+    fsr->table_id = ofsr->table_id;
+    error = ofputil_port_from_ofp11(ofsr->out_port, &fsr->out_port);
+    if (error) {
+        return error;
+    }
+    if (ofsr->out_group != htonl(OFPG11_ANY)) {
+        return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED;
+    }
+    fsr->cookie = ofsr->cookie;
+    fsr->cookie_mask = ofsr->cookie_mask;
+    error = ofputil_pull_ofp11_match(b, 0, &fsr->match, NULL);
+    if (error) {
+        return error;
+    }
+
+    return 0;
+}
+
 static enum ofperr
 ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
-                                 const struct ofp_header *oh,
-                                 bool aggregate)
+                                 struct ofpbuf *b, bool aggregate)
 {
     const struct nx_flow_stats_request *nfsr;
-    struct ofpbuf b;
     enum ofperr error;
 
-    ofpbuf_use_const(&b, oh, ntohs(oh->length));
-
-    nfsr = ofpbuf_pull(&b, sizeof *nfsr);
-    error = nx_pull_match(&b, ntohs(nfsr->match_len), 0, &fsr->match,
+    nfsr = ofpbuf_pull(b, sizeof *nfsr);
+    error = nx_pull_match(b, ntohs(nfsr->match_len), 0, &fsr->match,
                           &fsr->cookie, &fsr->cookie_mask);
     if (error) {
         return error;
     }
-    if (b.size) {
+    if (b->size) {
         return OFPERR_OFPBRC_BAD_LEN;
     }
 
@@ -1601,26 +1467,29 @@ enum ofperr
 ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
                                   const struct ofp_header *oh)
 {
-    const struct ofputil_msg_type *type;
+    enum ofpraw raw;
     struct ofpbuf b;
-    int code;
 
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    switch ((int) raw) {
+    case OFPRAW_OFPST10_FLOW_REQUEST:
+        return ofputil_decode_ofpst10_flow_request(fsr, b.data, false);
 
-    ofputil_decode_msg_type(oh, &type);
-    code = ofputil_msg_type_code(type);
-    switch (code) {
-    case OFPUTIL_OFPST_FLOW_REQUEST:
-        return ofputil_decode_ofpst_flow_request(fsr, oh, false);
+    case OFPRAW_OFPST10_AGGREGATE_REQUEST:
+        return ofputil_decode_ofpst10_flow_request(fsr, b.data, true);
 
-    case OFPUTIL_OFPST_AGGREGATE_REQUEST:
-        return ofputil_decode_ofpst_flow_request(fsr, oh, true);
+    case OFPRAW_OFPST11_FLOW_REQUEST:
+        return ofputil_decode_ofpst11_flow_request(fsr, &b, false);
 
-    case OFPUTIL_NXST_FLOW_REQUEST:
-        return ofputil_decode_nxst_flow_request(fsr, oh, false);
+    case OFPRAW_OFPST11_AGGREGATE_REQUEST:
+        return ofputil_decode_ofpst11_flow_request(fsr, &b, true);
 
-    case OFPUTIL_NXST_AGGREGATE_REQUEST:
-        return ofputil_decode_nxst_flow_request(fsr, oh, true);
+    case OFPRAW_NXST_FLOW_REQUEST:
+        return ofputil_decode_nxst_flow_request(fsr, &b, false);
+
+    case OFPRAW_NXST_AGGREGATE_REQUEST:
+        return ofputil_decode_nxst_flow_request(fsr, &b, true);
 
     default:
         /* Hey, the caller lied. */
@@ -1636,16 +1505,36 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
                                   enum ofputil_protocol protocol)
 {
     struct ofpbuf *msg;
+    enum ofpraw raw;
 
     switch (protocol) {
+    case OFPUTIL_P_OF12: {
+        struct ofp11_flow_stats_request *ofsr;
+
+        raw = (fsr->aggregate
+               ? OFPRAW_OFPST11_AGGREGATE_REQUEST
+               : OFPRAW_OFPST11_FLOW_REQUEST);
+        msg = ofpraw_alloc(raw, OFP12_VERSION, NXM_TYPICAL_LEN);
+        ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+        ofsr->table_id = fsr->table_id;
+        ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
+        ofsr->out_group = htonl(OFPG11_ANY);
+        ofsr->cookie = fsr->cookie;
+        ofsr->cookie_mask = fsr->cookie_mask;
+        oxm_put_match(msg, &fsr->match);
+        break;
+    }
+
     case OFPUTIL_P_OF10:
     case OFPUTIL_P_OF10_TID: {
-        struct ofp_flow_stats_request *ofsr;
-        int type;
-
-        type = fsr->aggregate ? OFPST_AGGREGATE : OFPST_FLOW;
-        ofsr = ofputil_make_stats_request(sizeof *ofsr, type, 0, &msg);
-        ofputil_cls_rule_to_match(&fsr->match, &ofsr->match);
+        struct ofp10_flow_stats_request *ofsr;
+
+        raw = (fsr->aggregate
+               ? OFPRAW_OFPST10_AGGREGATE_REQUEST
+               : OFPRAW_OFPST10_FLOW_REQUEST);
+        msg = ofpraw_alloc(raw, OFP10_VERSION, 0);
+        ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+        ofputil_cls_rule_to_ofp10_match(&fsr->match, &ofsr->match);
         ofsr->table_id = fsr->table_id;
         ofsr->out_port = htons(fsr->out_port);
         break;
@@ -1655,14 +1544,16 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
     case OFPUTIL_P_NXM_TID: {
         struct nx_flow_stats_request *nfsr;
         int match_len;
-        int subtype;
 
-        subtype = fsr->aggregate ? NXST_AGGREGATE : NXST_FLOW;
-        ofputil_make_stats_request(sizeof *nfsr, OFPST_VENDOR, subtype, &msg);
+        raw = (fsr->aggregate
+               ? OFPRAW_NXST_AGGREGATE_REQUEST
+               : OFPRAW_NXST_FLOW_REQUEST);
+        msg = ofpraw_alloc(raw, OFP10_VERSION, NXM_TYPICAL_LEN);
+        ofpbuf_put_zeros(msg, sizeof *nfsr);
         match_len = nx_put_match(msg, &fsr->match,
                                  fsr->cookie, fsr->cookie_mask);
 
-        nfsr = msg->data;
+        nfsr = msg->l3;
         nfsr->out_port = htons(fsr->out_port);
         nfsr->match_len = htons(match_len);
         nfsr->table_id = fsr->table_id;
@@ -1707,33 +1598,73 @@ ofputil_flow_stats_request_usable_protocols(
  * 'flow_age_extension' as true so that the contents of 'msg' determine the
  * 'idle_age' and 'hard_age' members in 'fs'.
  *
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the flow stats
+ * reply's actions.  The caller must initialize 'ofpacts' and retains ownership
+ * of it.  'fs->ofpacts' will point into the 'ofpacts' buffer.
+ *
  * Returns 0 if successful, EOF if no replies were left in this 'msg',
  * otherwise a positive errno value. */
 int
 ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
                                 struct ofpbuf *msg,
-                                bool flow_age_extension)
+                                bool flow_age_extension,
+                                struct ofpbuf *ofpacts)
 {
-    const struct ofputil_msg_type *type;
-    int code;
+    enum ofperr error;
+    enum ofpraw raw;
 
-    ofputil_decode_msg_type(msg->l2 ? msg->l2 : msg->data, &type);
-    code = ofputil_msg_type_code(type);
-    if (!msg->l2) {
-        msg->l2 = msg->data;
-        if (code == OFPUTIL_OFPST_FLOW_REPLY) {
-            ofpbuf_pull(msg, sizeof(struct ofp_stats_msg));
-        } else if (code == OFPUTIL_NXST_FLOW_REPLY) {
-            ofpbuf_pull(msg, sizeof(struct nicira_stats_msg));
-        } else {
-            NOT_REACHED();
-        }
+    error = (msg->l2
+             ? ofpraw_decode(&raw, msg->l2)
+             : ofpraw_pull(&raw, msg));
+    if (error) {
+        return error;
     }
 
     if (!msg->size) {
         return EOF;
-    } else if (code == OFPUTIL_OFPST_FLOW_REPLY) {
-        const struct ofp_flow_stats *ofs;
+    } else if (raw == OFPRAW_OFPST11_FLOW_REPLY) {
+        const struct ofp11_flow_stats *ofs;
+        size_t length;
+        uint16_t padded_match_len;
+
+        ofs = ofpbuf_try_pull(msg, sizeof *ofs);
+        if (!ofs) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %zu leftover "
+                         "bytes at end", msg->size);
+            return EINVAL;
+        }
+
+        length = ntohs(ofs->length);
+        if (length < sizeof *ofs) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid "
+                         "length %zu", length);
+            return EINVAL;
+        }
+
+        if (ofputil_pull_ofp11_match(msg, ntohs(ofs->priority), &fs->rule,
+                                     &padded_match_len)) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match");
+            return EINVAL;
+        }
+
+        if (ofpacts_pull_openflow11_instructions(msg, length - sizeof *ofs -
+                                                 padded_match_len, ofpacts)) {
+            VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions");
+            return EINVAL;
+        }
+
+        fs->table_id = ofs->table_id;
+        fs->duration_sec = ntohl(ofs->duration_sec);
+        fs->duration_nsec = ntohl(ofs->duration_nsec);
+        fs->idle_timeout = ntohs(ofs->idle_timeout);
+        fs->hard_timeout = ntohs(ofs->hard_timeout);
+        fs->idle_age = -1;
+        fs->hard_age = -1;
+        fs->cookie = ofs->cookie;
+        fs->packet_count = ntohll(ofs->packet_count);
+        fs->byte_count = ntohll(ofs->byte_count);
+    } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) {
+        const struct ofp10_flow_stats *ofs;
         size_t length;
 
         ofs = ofpbuf_try_pull(msg, sizeof *ofs);
@@ -1750,14 +1681,13 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
             return EINVAL;
         }
 
-        if (ofputil_pull_actions(msg, length - sizeof *ofs,
-                                 &fs->actions, &fs->n_actions)) {
+        if (ofpacts_pull_openflow10(msg, length - sizeof *ofs, ofpacts)) {
             return EINVAL;
         }
 
         fs->cookie = get_32aligned_be64(&ofs->cookie);
-        ofputil_cls_rule_from_match(&ofs->match, ntohs(ofs->priority),
-                                    &fs->rule);
+        ofputil_cls_rule_from_ofp10_match(&ofs->match, ntohs(ofs->priority),
+                                          &fs->rule);
         fs->table_id = ofs->table_id;
         fs->duration_sec = ntohl(ofs->duration_sec);
         fs->duration_nsec = ntohl(ofs->duration_nsec);
@@ -1767,9 +1697,9 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         fs->hard_age = -1;
         fs->packet_count = ntohll(get_32aligned_be64(&ofs->packet_count));
         fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count));
-    } else if (code == OFPUTIL_NXST_FLOW_REPLY) {
+    } else if (raw == OFPRAW_NXST_FLOW_REPLY) {
         const struct nx_flow_stats *nfs;
-        size_t match_len, length;
+        size_t match_len, actions_len, length;
 
         nfs = ofpbuf_try_pull(msg, sizeof *nfs);
         if (!nfs) {
@@ -1790,9 +1720,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
             return EINVAL;
         }
 
-        if (ofputil_pull_actions(msg,
-                                 length - sizeof *nfs - ROUND_UP(match_len, 8),
-                                 &fs->actions, &fs->n_actions)) {
+        actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
+        if (ofpacts_pull_openflow10(msg, actions_len, ofpacts)) {
             return EINVAL;
         }
 
@@ -1818,6 +1747,9 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         NOT_REACHED();
     }
 
+    fs->ofpacts = ofpacts->data;
+    fs->ofpacts_len = ofpacts->size;
+
     return 0;
 }
 
@@ -1838,19 +1770,43 @@ void
 ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
                                 struct list *replies)
 {
-    size_t act_len = fs->n_actions * sizeof *fs->actions;
-    const struct ofp_stats_msg *osm;
+    struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+    size_t start_ofs = reply->size;
+    enum ofpraw raw;
+
+    ofpraw_decode_partial(&raw, reply->data, reply->size);
+    if (raw == OFPRAW_OFPST11_FLOW_REPLY) {
+        struct ofp11_flow_stats *ofs;
+
+        ofpbuf_put_uninit(reply, sizeof *ofs);
+        oxm_put_match(reply, &fs->rule);
+        ofpacts_put_openflow11_instructions(fs->ofpacts, fs->ofpacts_len,
+                                            reply);
+
+        ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
+        ofs->length = htons(reply->size - start_ofs);
+        ofs->table_id = fs->table_id;
+        ofs->pad = 0;
+        ofs->duration_sec = htonl(fs->duration_sec);
+        ofs->duration_nsec = htonl(fs->duration_nsec);
+        ofs->priority = htons(fs->rule.priority);
+        ofs->idle_timeout = htons(fs->idle_timeout);
+        ofs->hard_timeout = htons(fs->hard_timeout);
+        memset(ofs->pad2, 0, sizeof ofs->pad2);
+        ofs->cookie = fs->cookie;
+        ofs->packet_count = htonll(unknown_to_zero(fs->packet_count));
+        ofs->byte_count = htonll(unknown_to_zero(fs->byte_count));
+    } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) {
+        struct ofp10_flow_stats *ofs;
 
-    osm = ofpbuf_from_list(list_back(replies))->data;
-    if (osm->type == htons(OFPST_FLOW)) {
-        size_t len = offsetof(struct ofp_flow_stats, actions) + act_len;
-        struct ofp_flow_stats *ofs;
+        ofpbuf_put_uninit(reply, sizeof *ofs);
+        ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
 
-        ofs = ofputil_append_stats_reply(len, replies);
-        ofs->length = htons(len);
+        ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
+        ofs->length = htons(reply->size - start_ofs);
         ofs->table_id = fs->table_id;
         ofs->pad = 0;
-        ofputil_cls_rule_to_match(&fs->rule, &ofs->match);
+        ofputil_cls_rule_to_ofp10_match(&fs->rule, &ofs->match);
         ofs->duration_sec = htonl(fs->duration_sec);
         ofs->duration_nsec = htonl(fs->duration_nsec);
         ofs->priority = htons(fs->rule.priority);
@@ -1862,17 +1818,16 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
                            htonll(unknown_to_zero(fs->packet_count)));
         put_32aligned_be64(&ofs->byte_count,
                            htonll(unknown_to_zero(fs->byte_count)));
-        memcpy(ofs->actions, fs->actions, act_len);
-    } else if (osm->type == htons(OFPST_VENDOR)) {
+    } else if (raw == OFPRAW_NXST_FLOW_REPLY) {
         struct nx_flow_stats *nfs;
-        struct ofpbuf *msg;
-        size_t start_len;
+        int match_len;
 
-        msg = ofputil_reserve_stats_reply(
-            sizeof *nfs + NXM_MAX_LEN + act_len, replies);
-        start_len = msg->size;
+        ofpbuf_put_uninit(reply, sizeof *nfs);
+        match_len = nx_put_match(reply, &fs->rule, 0, 0);
+        ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
 
-        nfs = ofpbuf_put_uninit(msg, sizeof *nfs);
+        nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs);
+        nfs->length = htons(reply->size - start_ofs);
         nfs->table_id = fs->table_id;
         nfs->pad = 0;
         nfs->duration_sec = htonl(fs->duration_sec);
@@ -1886,50 +1841,66 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         nfs->hard_age = htons(fs->hard_age < 0 ? 0
                               : fs->hard_age < UINT16_MAX ? fs->hard_age + 1
                               : UINT16_MAX);
-        nfs->match_len = htons(nx_put_match(msg, &fs->rule, 0, 0));
+        nfs->match_len = htons(match_len);
         nfs->cookie = fs->cookie;
         nfs->packet_count = htonll(fs->packet_count);
         nfs->byte_count = htonll(fs->byte_count);
-        ofpbuf_put(msg, fs->actions, act_len);
-        nfs->length = htons(msg->size - start_len);
     } else {
         NOT_REACHED();
     }
+
+    ofpmp_postappend(replies, start_ofs);
 }
 
 /* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
- * NXST_AGGREGATE reply according to 'protocol', and returns the message. */
+ * NXST_AGGREGATE reply matching 'request', and returns the message. */
 struct ofpbuf *
 ofputil_encode_aggregate_stats_reply(
     const struct ofputil_aggregate_stats *stats,
-    const struct ofp_stats_msg *request)
+    const struct ofp_header *request)
 {
+    struct ofp_aggregate_stats_reply *asr;
+    uint64_t packet_count;
+    uint64_t byte_count;
     struct ofpbuf *msg;
+    enum ofpraw raw;
 
-    if (request->type == htons(OFPST_AGGREGATE)) {
-        struct ofp_aggregate_stats_reply *asr;
-
-        asr = ofputil_make_stats_reply(sizeof *asr, request, &msg);
-        put_32aligned_be64(&asr->packet_count,
-                           htonll(unknown_to_zero(stats->packet_count)));
-        put_32aligned_be64(&asr->byte_count,
-                           htonll(unknown_to_zero(stats->byte_count)));
-        asr->flow_count = htonl(stats->flow_count);
-    } else if (request->type == htons(OFPST_VENDOR)) {
-        struct nx_aggregate_stats_reply *nasr;
-
-        nasr = ofputil_make_stats_reply(sizeof *nasr, request, &msg);
-        assert(nasr->nsm.subtype == htonl(NXST_AGGREGATE));
-        nasr->packet_count = htonll(stats->packet_count);
-        nasr->byte_count = htonll(stats->byte_count);
-        nasr->flow_count = htonl(stats->flow_count);
+    ofpraw_decode(&raw, request);
+    if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) {
+        packet_count = unknown_to_zero(stats->packet_count);
+        byte_count = unknown_to_zero(stats->byte_count);
     } else {
-        NOT_REACHED();
+        packet_count = stats->packet_count;
+        byte_count = stats->byte_count;
     }
 
+    msg = ofpraw_alloc_stats_reply(request, 0);
+    asr = ofpbuf_put_zeros(msg, sizeof *asr);
+    put_32aligned_be64(&asr->packet_count, htonll(packet_count));
+    put_32aligned_be64(&asr->byte_count, htonll(byte_count));
+    asr->flow_count = htonl(stats->flow_count);
+
     return msg;
 }
 
+enum ofperr
+ofputil_decode_aggregate_stats_reply(struct ofputil_aggregate_stats *stats,
+                                     const struct ofp_header *reply)
+{
+    struct ofp_aggregate_stats_reply *asr;
+    struct ofpbuf msg;
+
+    ofpbuf_use_const(&msg, reply, ntohs(reply->length));
+    ofpraw_pull_assert(&msg);
+
+    asr = msg.l3;
+    stats->packet_count = ntohll(get_32aligned_be64(&asr->packet_count));
+    stats->byte_count = ntohll(get_32aligned_be64(&asr->byte_count));
+    stats->flow_count = ntohl(asr->flow_count);
+
+    return 0;
+}
+
 /* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an
  * abstract ofputil_flow_removed in 'fr'.  Returns 0 if successful, otherwise
  * an OpenFlow error code. */
@@ -1937,31 +1908,51 @@ enum ofperr
 ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
                             const struct ofp_header *oh)
 {
-    const struct ofputil_msg_type *type;
-    enum ofputil_msg_code code;
+    enum ofpraw raw;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
+        const struct ofp12_flow_removed *ofr;
+        enum ofperr error;
 
-    ofputil_decode_msg_type(oh, &type);
-    code = ofputil_msg_type_code(type);
-    if (code == OFPUTIL_OFPT_FLOW_REMOVED) {
+        ofr = ofpbuf_pull(&b, sizeof *ofr);
+
+        error = ofputil_pull_ofp11_match(&b, ntohs(ofr->priority),
+                                         &fr->rule, NULL);
+        if (error) {
+            return error;
+        }
+
+        fr->cookie = ofr->cookie;
+        fr->reason = ofr->reason;
+        /* XXX: ofr->table_id is ignored */
+        fr->duration_sec = ntohl(ofr->duration_sec);
+        fr->duration_nsec = ntohl(ofr->duration_nsec);
+        fr->idle_timeout = ntohs(ofr->idle_timeout);
+        fr->hard_timeout = ntohs(ofr->hard_timeout);
+        fr->packet_count = ntohll(ofr->packet_count);
+        fr->byte_count = ntohll(ofr->byte_count);
+    } else if (raw == OFPRAW_OFPT10_FLOW_REMOVED) {
         const struct ofp_flow_removed *ofr;
 
-        ofr = (const struct ofp_flow_removed *) oh;
-        ofputil_cls_rule_from_match(&ofr->match, ntohs(ofr->priority),
-                                    &fr->rule);
+        ofr = ofpbuf_pull(&b, sizeof *ofr);
+
+        ofputil_cls_rule_from_ofp10_match(&ofr->match, ntohs(ofr->priority),
+                                          &fr->rule);
         fr->cookie = ofr->cookie;
         fr->reason = ofr->reason;
         fr->duration_sec = ntohl(ofr->duration_sec);
         fr->duration_nsec = ntohl(ofr->duration_nsec);
         fr->idle_timeout = ntohs(ofr->idle_timeout);
+        fr->hard_timeout = 0;
         fr->packet_count = ntohll(ofr->packet_count);
         fr->byte_count = ntohll(ofr->byte_count);
-    } else if (code == OFPUTIL_NXT_FLOW_REMOVED) {
+    } else if (raw == OFPRAW_NXT_FLOW_REMOVED) {
         struct nx_flow_removed *nfr;
-        struct ofpbuf b;
         int error;
 
-        ofpbuf_use_const(&b, oh, ntohs(oh->length));
-
         nfr = ofpbuf_pull(&b, sizeof *nfr);
         error = nx_pull_match(&b, ntohs(nfr->match_len), ntohs(nfr->priority),
                               &fr->rule, NULL, NULL);
@@ -1977,6 +1968,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
         fr->duration_sec = ntohl(nfr->duration_sec);
         fr->duration_nsec = ntohl(nfr->duration_nsec);
         fr->idle_timeout = ntohs(nfr->idle_timeout);
+        fr->hard_timeout = 0;
         fr->packet_count = ntohll(nfr->packet_count);
         fr->byte_count = ntohll(nfr->byte_count);
     } else {
@@ -1996,13 +1988,35 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     struct ofpbuf *msg;
 
     switch (protocol) {
+    case OFPUTIL_P_OF12: {
+        struct ofp12_flow_removed *ofr;
+
+        msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
+                               ofputil_protocol_to_ofp_version(protocol),
+                               htonl(0), NXM_TYPICAL_LEN);
+        ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
+        ofr->cookie = fr->cookie;
+        ofr->priority = htons(fr->rule.priority);
+        ofr->reason = fr->reason;
+        ofr->table_id = 0;
+        ofr->duration_sec = htonl(fr->duration_sec);
+        ofr->duration_nsec = htonl(fr->duration_nsec);
+        ofr->idle_timeout = htons(fr->idle_timeout);
+        ofr->hard_timeout = htons(fr->hard_timeout);
+        ofr->packet_count = htonll(fr->packet_count);
+        ofr->byte_count = htonll(fr->byte_count);
+        oxm_put_match(msg, &fr->rule);
+        break;
+    }
+
     case OFPUTIL_P_OF10:
     case OFPUTIL_P_OF10_TID: {
         struct ofp_flow_removed *ofr;
 
-        ofr = make_openflow_xid(sizeof *ofr, OFPT_FLOW_REMOVED, htonl(0),
-                                &msg);
-        ofputil_cls_rule_to_match(&fr->rule, &ofr->match);
+        msg = ofpraw_alloc_xid(OFPRAW_OFPT10_FLOW_REMOVED, OFP10_VERSION,
+                               htonl(0), 0);
+        ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
+        ofputil_cls_rule_to_ofp10_match(&fr->rule, &ofr->match);
         ofr->cookie = fr->cookie;
         ofr->priority = htons(fr->rule.priority);
         ofr->reason = fr->reason;
@@ -2019,10 +2033,12 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         struct nx_flow_removed *nfr;
         int match_len;
 
-        make_nxmsg_xid(sizeof *nfr, NXT_FLOW_REMOVED, htonl(0), &msg);
+        msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_REMOVED, OFP10_VERSION,
+                               htonl(0), NXM_TYPICAL_LEN);
+        nfr = ofpbuf_put_zeros(msg, sizeof *nfr);
         match_len = nx_put_match(msg, &fr->rule, 0, 0);
 
-        nfr = msg->data;
+        nfr = msg->l3;
         nfr->cookie = fr->cookie;
         nfr->priority = htons(fr->rule.priority);
         nfr->reason = fr->reason;
@@ -2042,39 +2058,73 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     return msg;
 }
 
-int
+static void
+ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin,
+                                struct cls_rule *rule,
+                                struct ofpbuf *b)
+{
+    pin->packet = b->data;
+    pin->packet_len = b->size;
+
+    pin->fmd.in_port = rule->flow.in_port;
+    pin->fmd.tun_id = rule->flow.tun_id;
+    pin->fmd.metadata = rule->flow.metadata;
+    memcpy(pin->fmd.regs, rule->flow.regs, sizeof pin->fmd.regs);
+}
+
+enum ofperr
 ofputil_decode_packet_in(struct ofputil_packet_in *pin,
                          const struct ofp_header *oh)
 {
-    const struct ofputil_msg_type *type;
-    enum ofputil_msg_code code;
+    enum ofpraw raw;
+    struct ofpbuf b;
 
-    ofputil_decode_msg_type(oh, &type);
-    code = ofputil_msg_type_code(type);
     memset(pin, 0, sizeof *pin);
 
-    if (code == OFPUTIL_OFPT_PACKET_IN) {
-        const struct ofp_packet_in *opi = (const struct ofp_packet_in *) oh;
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    if (raw == OFPRAW_OFPT12_PACKET_IN) {
+        const struct ofp12_packet_in *opi;
+        struct cls_rule rule;
+        int error;
+
+        opi = ofpbuf_pull(&b, sizeof *opi);
+        error = oxm_pull_match_loose(&b, 0, &rule);
+        if (error) {
+            return error;
+        }
+
+        if (!ofpbuf_try_pull(&b, 2)) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+
+        pin->reason = opi->reason;
+        pin->table_id = opi->table_id;
+
+        pin->buffer_id = ntohl(opi->buffer_id);
+        pin->total_len = ntohs(opi->total_len);
+
+        ofputil_decode_packet_in_finish(pin, &rule, &b);
+    } else if (raw == OFPRAW_OFPT10_PACKET_IN) {
+        const struct ofp_packet_in *opi;
+
+        opi = ofpbuf_pull(&b, offsetof(struct ofp_packet_in, data));
 
         pin->packet = opi->data;
-        pin->packet_len = ntohs(opi->header.length)
-            - offsetof(struct ofp_packet_in, data);
+        pin->packet_len = b.size;
 
         pin->fmd.in_port = ntohs(opi->in_port);
         pin->reason = opi->reason;
         pin->buffer_id = ntohl(opi->buffer_id);
         pin->total_len = ntohs(opi->total_len);
-    } else if (code == OFPUTIL_NXT_PACKET_IN) {
+    } else if (raw == OFPRAW_NXT_PACKET_IN) {
         const struct nx_packet_in *npi;
         struct cls_rule rule;
-        struct ofpbuf b;
         int error;
 
-        ofpbuf_use_const(&b, oh, ntohs(oh->length));
-
         npi = ofpbuf_pull(&b, sizeof *npi);
         error = nx_pull_match_loose(&b, ntohs(npi->match_len), 0, &rule, NULL,
-                              NULL);
+                                    NULL);
         if (error) {
             return error;
         }
@@ -2083,23 +2133,14 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
             return OFPERR_OFPBRC_BAD_LEN;
         }
 
-        pin->packet = b.data;
-        pin->packet_len = b.size;
         pin->reason = npi->reason;
         pin->table_id = npi->table_id;
         pin->cookie = npi->cookie;
 
-        pin->fmd.in_port = rule.flow.in_port;
-
-        pin->fmd.tun_id = rule.flow.tun_id;
-        pin->fmd.tun_id_mask = rule.wc.tun_id_mask;
-
-        memcpy(pin->fmd.regs, rule.flow.regs, sizeof pin->fmd.regs);
-        memcpy(pin->fmd.reg_masks, rule.wc.reg_masks,
-               sizeof pin->fmd.reg_masks);
-
         pin->buffer_id = ntohl(npi->buffer_id);
         pin->total_len = ntohs(npi->total_len);
+
+        ofputil_decode_packet_in_finish(pin, &rule, &b);
     } else {
         NOT_REACHED();
     }
@@ -2107,24 +2148,66 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
     return 0;
 }
 
+static void
+ofputil_packet_in_to_rule(const struct ofputil_packet_in *pin,
+                          struct cls_rule *rule)
+{
+    int i;
+
+    cls_rule_init_catchall(rule, 0);
+    if (pin->fmd.tun_id != htonll(0)) {
+        cls_rule_set_tun_id(rule, pin->fmd.tun_id);
+    }
+    if (pin->fmd.metadata != htonll(0)) {
+        cls_rule_set_metadata(rule, pin->fmd.metadata);
+    }
+
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        if (pin->fmd.regs[i]) {
+            cls_rule_set_reg(rule, i, pin->fmd.regs[i]);
+        }
+    }
+
+    cls_rule_set_in_port(rule, pin->fmd.in_port);
+}
+
 /* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message
  * in the format specified by 'packet_in_format'.  */
 struct ofpbuf *
 ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
+                         enum ofputil_protocol protocol,
                          enum nx_packet_in_format packet_in_format)
 {
     size_t send_len = MIN(pin->send_len, pin->packet_len);
     struct ofpbuf *packet;
 
     /* Add OFPT_PACKET_IN. */
-    if (packet_in_format == NXPIF_OPENFLOW10) {
-        size_t header_len = offsetof(struct ofp_packet_in, data);
+    if (protocol == OFPUTIL_P_OF12) {
+        struct ofp12_packet_in *opi;
+        struct cls_rule rule;
+
+        ofputil_packet_in_to_rule(pin, &rule);
+
+        /* The final argument is just an estimate of the space required. */
+        packet = ofpraw_alloc_xid(OFPRAW_OFPT12_PACKET_IN, OFP12_VERSION,
+                                  htonl(0), (sizeof(struct flow_metadata) * 2
+                                             + 2 + send_len));
+        ofpbuf_put_zeros(packet, sizeof *opi);
+        oxm_put_match(packet, &rule);
+        ofpbuf_put_zeros(packet, 2);
+        ofpbuf_put(packet, pin->packet, send_len);
+
+        opi = packet->l3;
+        opi->buffer_id = htonl(pin->buffer_id);
+        opi->total_len = htons(pin->total_len);
+        opi->reason = pin->reason;
+        opi->table_id = pin->table_id;
+   } else if (packet_in_format == NXPIF_OPENFLOW10) {
         struct ofp_packet_in *opi;
 
-        packet = ofpbuf_new(send_len + header_len);
-        opi = ofpbuf_put_zeros(packet, header_len);
-        opi->header.version = OFP10_VERSION;
-        opi->header.type = OFPT_PACKET_IN;
+        packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION,
+                                  htonl(0), send_len);
+        opi = ofpbuf_put_zeros(packet, offsetof(struct ofp_packet_in, data));
         opi->total_len = htons(pin->total_len);
         opi->in_port = htons(pin->fmd.in_port);
         opi->reason = pin->reason;
@@ -2135,36 +2218,19 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
         struct nx_packet_in *npi;
         struct cls_rule rule;
         size_t match_len;
-        size_t i;
-
-        /* Estimate of required PACKET_IN length includes the NPI header, space
-         * for the match (2 times sizeof the metadata seems like enough), 2
-         * bytes for padding, and the packet length. */
-        packet = ofpbuf_new(sizeof *npi + sizeof(struct flow_metadata) * 2
-                            + 2 + send_len);
 
-        cls_rule_init_catchall(&rule, 0);
-        cls_rule_set_tun_id_masked(&rule, pin->fmd.tun_id,
-                                   pin->fmd.tun_id_mask);
-
-        for (i = 0; i < FLOW_N_REGS; i++) {
-            cls_rule_set_reg_masked(&rule, i, pin->fmd.regs[i],
-                                    pin->fmd.reg_masks[i]);
-        }
-
-        cls_rule_set_in_port(&rule, pin->fmd.in_port);
+        ofputil_packet_in_to_rule(pin, &rule);
 
+        /* The final argument is just an estimate of the space required. */
+        packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION,
+                                  htonl(0), (sizeof(struct flow_metadata) * 2
+                                             + 2 + send_len));
         ofpbuf_put_zeros(packet, sizeof *npi);
         match_len = nx_put_match(packet, &rule, 0, 0);
         ofpbuf_put_zeros(packet, 2);
         ofpbuf_put(packet, pin->packet, send_len);
 
-        npi = packet->data;
-        npi->nxh.header.version = OFP10_VERSION;
-        npi->nxh.header.type = OFPT_VENDOR;
-        npi->nxh.vendor = htonl(NX_VENDOR_ID);
-        npi->nxh.subtype = htonl(NXT_PACKET_IN);
-
+        npi = packet->l3;
         npi->buffer_id = htonl(pin->buffer_id);
         npi->total_len = htons(pin->total_len);
         npi->reason = pin->reason;
@@ -2174,7 +2240,7 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
     } else {
         NOT_REACHED();
     }
-    update_openflow_length(packet);
+    ofpmsg_update_length(packet);
 
     return packet;
 }
@@ -2214,30 +2280,69 @@ ofputil_packet_in_reason_from_string(const char *s,
     return false;
 }
 
+/* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in
+ * 'po'.
+ *
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out
+ * message's actions.  The caller must initialize 'ofpacts' and retains
+ * ownership of it.  'po->ofpacts' will point into the 'ofpacts' buffer.
+ *
+ * Returns 0 if successful, otherwise an OFPERR_* value. */
 enum ofperr
 ofputil_decode_packet_out(struct ofputil_packet_out *po,
-                          const struct ofp_packet_out *opo)
+                          const struct ofp_header *oh,
+                          struct ofpbuf *ofpacts)
 {
-    enum ofperr error;
+    enum ofperr bad_in_port_err;
+    enum ofpraw raw;
     struct ofpbuf b;
 
-    po->buffer_id = ntohl(opo->buffer_id);
-    po->in_port = ntohs(opo->in_port);
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    if (raw == OFPRAW_OFPT11_PACKET_OUT) {
+        enum ofperr error;
+        const struct ofp11_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+
+        po->buffer_id = ntohl(opo->buffer_id);
+        error = ofputil_port_from_ofp11(opo->in_port, &po->in_port);
+        if (error) {
+            return error;
+        }
+
+        error = ofpacts_pull_openflow11_actions(&b, ntohs(opo->actions_len),
+                                                ofpacts);
+        if (error) {
+            return error;
+        }
+
+        bad_in_port_err = OFPERR_OFPBMC_BAD_VALUE;
+    } else if (raw == OFPRAW_OFPT10_PACKET_OUT) {
+        enum ofperr error;
+        const struct ofp_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+
+        po->buffer_id = ntohl(opo->buffer_id);
+        po->in_port = ntohs(opo->in_port);
+
+        error = ofpacts_pull_openflow10(&b, ntohs(opo->actions_len), ofpacts);
+        if (error) {
+            return error;
+        }
+
+        bad_in_port_err = OFPERR_NXBRC_BAD_IN_PORT;
+    } else {
+        NOT_REACHED();
+    }
+
     if (po->in_port >= OFPP_MAX && po->in_port != OFPP_LOCAL
-        && po->in_port != OFPP_NONE) {
+        && 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);
-        return OFPERR_NXBRC_BAD_IN_PORT;
+        return bad_in_port_err;
     }
 
-    ofpbuf_use_const(&b, opo, ntohs(opo->header.length));
-    ofpbuf_pull(&b, sizeof *opo);
-
-    error = ofputil_pull_actions(&b, ntohs(opo->actions_len),
-                                 &po->actions, &po->n_actions);
-    if (error) {
-        return error;
-    }
+    po->ofpacts = ofpacts->data;
+    po->ofpacts_len = ofpacts->size;
 
     if (po->buffer_id == UINT32_MAX) {
         po->packet = b.data;
@@ -2363,16 +2468,17 @@ ofputil_decode_ofp11_port(struct ofputil_phy_port *pp,
     return 0;
 }
 
-static int
-ofputil_pull_phy_port(uint8_t ofp_version, struct ofpbuf *b,
-                      struct ofputil_phy_port *pp)
+static size_t
+ofputil_get_phy_port_size(enum ofp_version ofp_version)
 {
-    if (ofp_version == OFP10_VERSION) {
-        const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp);
-        return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF;
-    } else {
-        const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op);
-        return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
+    switch (ofp_version) {
+    case OFP10_VERSION:
+        return sizeof(struct ofp10_phy_port);
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        return sizeof(struct ofp11_port);
+    default:
+        NOT_REACHED();
     }
 }
 
@@ -2418,28 +2524,66 @@ ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp,
 }
 
 static void
-ofputil_put_phy_port(uint8_t ofp_version, const struct ofputil_phy_port *pp,
-                     struct ofpbuf *b)
+ofputil_put_phy_port(enum ofp_version ofp_version,
+                     const struct ofputil_phy_port *pp, struct ofpbuf *b)
 {
-    if (ofp_version == OFP10_VERSION) {
+    switch (ofp_version) {
+    case OFP10_VERSION: {
         struct ofp10_phy_port *opp;
         if (b->size + sizeof *opp <= UINT16_MAX) {
             opp = ofpbuf_put_uninit(b, sizeof *opp);
             ofputil_encode_ofp10_phy_port(pp, opp);
         }
-    } else {
+        break;
+    }
+
+    case OFP11_VERSION:
+    case OFP12_VERSION: {
         struct ofp11_port *op;
         if (b->size + sizeof *op <= UINT16_MAX) {
             op = ofpbuf_put_uninit(b, sizeof *op);
             ofputil_encode_ofp11_port(pp, op);
         }
+        break;
+    }
+
+    default:
+        NOT_REACHED();
+    }
+}
+
+void
+ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version,
+                                     const struct ofputil_phy_port *pp,
+                                     struct list *replies)
+{
+    switch (ofp_version) {
+    case OFP10_VERSION: {
+        struct ofp10_phy_port *opp;
+
+        opp = ofpmp_append(replies, sizeof *opp);
+        ofputil_encode_ofp10_phy_port(pp, opp);
+        break;
+    }
+
+    case OFP11_VERSION:
+    case OFP12_VERSION: {
+        struct ofp11_port *op;
+
+        op = ofpmp_append(replies, sizeof *op);
+        ofputil_encode_ofp11_port(pp, op);
+        break;
+    }
+
+    default:
+      NOT_REACHED();
     }
 }
 \f
 /* ofputil_switch_features */
 
 #define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \
-                     OFPC_IP_REASM | OFPC_QUEUE_STATS | OFPC_ARP_MATCH_IP)
+                     OFPC_IP_REASM | OFPC_QUEUE_STATS)
 BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_STATS == OFPC_FLOW_STATS);
 BUILD_ASSERT_DECL((int) OFPUTIL_C_TABLE_STATS == OFPC_TABLE_STATS);
 BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_STATS == OFPC_PORT_STATS);
@@ -2468,35 +2612,6 @@ static const struct ofputil_action_bit_translation of10_action_bits[] = {
     { 0, 0 },
 };
 
-static const struct ofputil_action_bit_translation of11_action_bits[] = {
-    { OFPUTIL_A_OUTPUT,         OFPAT11_OUTPUT },
-    { OFPUTIL_A_SET_VLAN_VID,   OFPAT11_SET_VLAN_VID },
-    { OFPUTIL_A_SET_VLAN_PCP,   OFPAT11_SET_VLAN_PCP },
-    { OFPUTIL_A_SET_DL_SRC,     OFPAT11_SET_DL_SRC },
-    { OFPUTIL_A_SET_DL_DST,     OFPAT11_SET_DL_DST },
-    { OFPUTIL_A_SET_NW_SRC,     OFPAT11_SET_NW_SRC },
-    { OFPUTIL_A_SET_NW_DST,     OFPAT11_SET_NW_DST },
-    { OFPUTIL_A_SET_NW_TOS,     OFPAT11_SET_NW_TOS },
-    { OFPUTIL_A_SET_NW_ECN,     OFPAT11_SET_NW_ECN },
-    { OFPUTIL_A_SET_TP_SRC,     OFPAT11_SET_TP_SRC },
-    { OFPUTIL_A_SET_TP_DST,     OFPAT11_SET_TP_DST },
-    { OFPUTIL_A_COPY_TTL_OUT,   OFPAT11_COPY_TTL_OUT },
-    { OFPUTIL_A_COPY_TTL_IN,    OFPAT11_COPY_TTL_IN },
-    { OFPUTIL_A_SET_MPLS_LABEL, OFPAT11_SET_MPLS_LABEL },
-    { OFPUTIL_A_SET_MPLS_TC,    OFPAT11_SET_MPLS_TC },
-    { OFPUTIL_A_SET_MPLS_TTL,   OFPAT11_SET_MPLS_TTL },
-    { OFPUTIL_A_DEC_MPLS_TTL,   OFPAT11_DEC_MPLS_TTL },
-    { OFPUTIL_A_PUSH_VLAN,      OFPAT11_PUSH_VLAN },
-    { OFPUTIL_A_POP_VLAN,       OFPAT11_POP_VLAN },
-    { OFPUTIL_A_PUSH_MPLS,      OFPAT11_PUSH_MPLS },
-    { OFPUTIL_A_POP_MPLS,       OFPAT11_POP_MPLS },
-    { OFPUTIL_A_SET_QUEUE,      OFPAT11_SET_QUEUE },
-    { OFPUTIL_A_GROUP,          OFPAT11_GROUP },
-    { OFPUTIL_A_SET_NW_TTL,     OFPAT11_SET_NW_TTL },
-    { OFPUTIL_A_DEC_NW_TTL,     OFPAT11_DEC_NW_TTL },
-    { 0, 0 },
-};
-
 static enum ofputil_action_bitmap
 decode_action_bits(ovs_be32 of_actions,
                    const struct ofputil_action_bit_translation *x)
@@ -2512,43 +2627,60 @@ decode_action_bits(ovs_be32 of_actions,
     return ofputil_actions;
 }
 
+static uint32_t
+ofputil_capabilities_mask(enum ofp_version ofp_version)
+{
+    /* Handle capabilities whose bit is unique for all Open Flow versions */
+    switch (ofp_version) {
+    case OFP10_VERSION:
+    case OFP11_VERSION:
+        return OFPC_COMMON | OFPC_ARP_MATCH_IP;
+    case OFP12_VERSION:
+        return OFPC_COMMON | OFPC12_PORT_BLOCKED;
+    default:
+        /* Caller needs to check osf->header.version itself */
+        return 0;
+    }
+}
+
 /* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an
  * abstract representation in '*features'.  Initializes '*b' to iterate over
  * the OpenFlow port structures following 'osf' with later calls to
- * ofputil_pull_switch_features_port().  Returns 0 if successful, otherwise an
+ * ofputil_pull_phy_port().  Returns 0 if successful, otherwise an
  * OFPERR_* value.  */
 enum ofperr
-ofputil_decode_switch_features(const struct ofp_switch_features *osf,
+ofputil_decode_switch_features(const struct ofp_header *oh,
                                struct ofputil_switch_features *features,
                                struct ofpbuf *b)
 {
-    ofpbuf_use_const(b, osf, ntohs(osf->header.length));
-    ofpbuf_pull(b, sizeof *osf);
-    b->l2 = (struct ofputil_switch_features *) osf;
+    const struct ofp_switch_features *osf;
+    enum ofpraw raw;
+
+    ofpbuf_use_const(b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(b);
 
+    osf = ofpbuf_pull(b, sizeof *osf);
     features->datapath_id = ntohll(osf->datapath_id);
     features->n_buffers = ntohl(osf->n_buffers);
     features->n_tables = osf->n_tables;
 
-    features->capabilities = ntohl(osf->capabilities) & OFPC_COMMON;
-    if (osf->header.version == OFP10_VERSION) {
-        if (b->size % sizeof(struct ofp10_phy_port)) {
-            return OFPERR_OFPBRC_BAD_LEN;
-        }
+    features->capabilities = ntohl(osf->capabilities) &
+        ofputil_capabilities_mask(oh->version);
+
+    if (b->size % ofputil_get_phy_port_size(oh->version)) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
 
+    if (raw == OFPRAW_OFPT10_FEATURES_REPLY) {
         if (osf->capabilities & htonl(OFPC10_STP)) {
             features->capabilities |= OFPUTIL_C_STP;
         }
         features->actions = decode_action_bits(osf->actions, of10_action_bits);
-    } else if (osf->header.version == OFP11_VERSION) {
-        if (b->size % sizeof(struct ofp11_port)) {
-            return OFPERR_OFPBRC_BAD_LEN;
-        }
-
+    } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY) {
         if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) {
             features->capabilities |= OFPUTIL_C_GROUP_STATS;
         }
-        features->actions = decode_action_bits(osf->actions, of11_action_bits);
+        features->actions = 0;
     } else {
         return OFPERR_OFPBRC_BAD_VERSION;
     }
@@ -2556,31 +2688,36 @@ ofputil_decode_switch_features(const struct ofp_switch_features *osf,
     return 0;
 }
 
-/* Given a buffer 'b' that was initialized by a previous successful call to
- * ofputil_decode_switch_features(), tries to decode an OpenFlow port structure
- * following the main switch features information.  If successful, initializes
- * '*pp' with an abstract representation of the port and returns 0.  If no
- * ports remained to be decoded, returns EOF.  On an error, returns a positive
- * OFPERR_* value.  */
-int
-ofputil_pull_switch_features_port(struct ofpbuf *b,
-                                  struct ofputil_phy_port *pp)
+/* Returns true if the maximum number of ports are in 'oh'. */
+static bool
+max_ports_in_features(const struct ofp_header *oh)
 {
-    const struct ofp_switch_features *osf = b->l2;
-    return ofputil_pull_phy_port(osf->header.version, b, pp);
+    size_t pp_size = ofputil_get_phy_port_size(oh->version);
+    return ntohs(oh->length) + pp_size > UINT16_MAX;
 }
 
-/* Returns the number of OpenFlow port structures that follow the main switch
- * features information in '*osf'.  The return value is only guaranteed to be
- * accurate if '*osf' is well-formed, that is, if
- * ofputil_decode_switch_features() can process '*osf' successfully. */
-size_t
-ofputil_count_phy_ports(const struct ofp_switch_features *osf)
+/* Given a buffer 'b' that contains a Features Reply message, checks if
+ * it contains the maximum number of ports that will fit.  If so, it
+ * returns true and removes the ports from the message.  The caller
+ * should then send an OFPST_PORT_DESC stats request to get the ports,
+ * since the switch may have more ports than could be represented in the
+ * Features Reply.  Otherwise, returns false.
+ */
+bool
+ofputil_switch_features_ports_trunc(struct ofpbuf *b)
 {
-    size_t ports_len = ntohs(osf->header.length) - sizeof *osf;
-    return (osf->header.version == OFP10_VERSION
-            ? ports_len / sizeof(struct ofp10_phy_port)
-            : ports_len / sizeof(struct ofp11_port));
+    struct ofp_header *oh = b->data;
+
+    if (max_ports_in_features(oh)) {
+        /* Remove all the ports. */
+        b->size = (sizeof(struct ofp_header)
+                   + sizeof(struct ofp_switch_features));
+        ofpmsg_update_length(b);
+
+        return true;
+    }
+
+    return false;
 }
 
 static ovs_be32
@@ -2608,24 +2745,45 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features,
 {
     struct ofp_switch_features *osf;
     struct ofpbuf *b;
+    enum ofp_version version;
+    enum ofpraw raw;
 
-    osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, xid, &b);
-    osf->header.version = ofputil_protocol_to_ofp_version(protocol);
+    version = ofputil_protocol_to_ofp_version(protocol);
+    switch (version) {
+    case OFP10_VERSION:
+        raw = OFPRAW_OFPT10_FEATURES_REPLY;
+        break;
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        raw = OFPRAW_OFPT11_FEATURES_REPLY;
+        break;
+    default:
+        NOT_REACHED();
+    }
+    b = ofpraw_alloc_xid(raw, version, xid, 0);
+    osf = ofpbuf_put_zeros(b, sizeof *osf);
     osf->datapath_id = htonll(features->datapath_id);
     osf->n_buffers = htonl(features->n_buffers);
     osf->n_tables = features->n_tables;
 
     osf->capabilities = htonl(features->capabilities & OFPC_COMMON);
-    if (osf->header.version == OFP10_VERSION) {
+    osf->capabilities = htonl(features->capabilities &
+                              ofputil_capabilities_mask(version));
+    switch (version) {
+    case OFP10_VERSION:
         if (features->capabilities & OFPUTIL_C_STP) {
             osf->capabilities |= htonl(OFPC10_STP);
         }
         osf->actions = encode_action_bits(features->actions, of10_action_bits);
-    } else {
+        break;
+    case OFP11_VERSION:
+    case OFP12_VERSION:
         if (features->capabilities & OFPUTIL_C_GROUP_STATS) {
             osf->capabilities |= htonl(OFPC11_GROUP_STATS);
         }
-        osf->actions = encode_action_bits(features->actions, of11_action_bits);
+        break;
+    default:
+        NOT_REACHED();
     }
 
     return b;
@@ -2638,9 +2796,9 @@ void
 ofputil_put_switch_features_port(const struct ofputil_phy_port *pp,
                                  struct ofpbuf *b)
 {
-    const struct ofp_switch_features *osf = b->data;
+    const struct ofp_header *oh = b->data;
 
-    ofputil_put_phy_port(osf->header.version, pp, b);
+    ofputil_put_phy_port(oh->version, pp, b);
 }
 \f
 /* ofputil_port_status */
@@ -2648,12 +2806,17 @@ ofputil_put_switch_features_port(const struct ofputil_phy_port *pp,
 /* Decodes the OpenFlow "port status" message in '*ops' into an abstract form
  * in '*ps'.  Returns 0 if successful, otherwise an OFPERR_* value. */
 enum ofperr
-ofputil_decode_port_status(const struct ofp_port_status *ops,
+ofputil_decode_port_status(const struct ofp_header *oh,
                            struct ofputil_port_status *ps)
 {
+    const struct ofp_port_status *ops;
     struct ofpbuf b;
     int retval;
 
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    ofpraw_pull_assert(&b);
+    ops = ofpbuf_pull(&b, sizeof *ops);
+
     if (ops->reason != OFPPR_ADD &&
         ops->reason != OFPPR_DELETE &&
         ops->reason != OFPPR_MODIFY) {
@@ -2661,9 +2824,7 @@ ofputil_decode_port_status(const struct ofp_port_status *ops,
     }
     ps->reason = ops->reason;
 
-    ofpbuf_use_const(&b, ops, ntohs(ops->header.length));
-    ofpbuf_pull(&b, sizeof *ops);
-    retval = ofputil_pull_phy_port(ops->header.version, &b, &ps->desc);
+    retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc);
     assert(retval != EOF);
     return retval;
 }
@@ -2677,13 +2838,29 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps,
 {
     struct ofp_port_status *ops;
     struct ofpbuf *b;
+    enum ofp_version version;
+    enum ofpraw raw;
+
+    version = ofputil_protocol_to_ofp_version(protocol);
+    switch (version) {
+    case OFP10_VERSION:
+        raw = OFPRAW_OFPT10_PORT_STATUS;
+        break;
 
-    b = ofpbuf_new(sizeof *ops + sizeof(struct ofp11_port));
-    ops = put_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, htonl(0), b);
-    ops->header.version = ofputil_protocol_to_ofp_version(protocol);
+    case OFP11_VERSION:
+    case OFP12_VERSION:
+        raw = OFPRAW_OFPT11_PORT_STATUS;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    b = ofpraw_alloc_xid(raw, version, htonl(0), 0);
+    ops = ofpbuf_put_zeros(b, sizeof *ops);
     ops->reason = ps->reason;
-    ofputil_put_phy_port(ops->header.version, &ps->desc, b);
-    update_openflow_length(b);
+    ofputil_put_phy_port(version, &ps->desc, b);
+    ofpmsg_update_length(b);
     return b;
 }
 \f
@@ -2695,26 +2872,24 @@ enum ofperr
 ofputil_decode_port_mod(const struct ofp_header *oh,
                         struct ofputil_port_mod *pm)
 {
-    if (oh->version == OFP10_VERSION) {
-        const struct ofp10_port_mod *opm = (const struct ofp10_port_mod *) oh;
+    enum ofpraw raw;
+    struct ofpbuf b;
 
-        if (oh->length != htons(sizeof *opm)) {
-            return OFPERR_OFPBRC_BAD_LEN;
-        }
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    if (raw == OFPRAW_OFPT10_PORT_MOD) {
+        const struct ofp10_port_mod *opm = b.data;
 
         pm->port_no = 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;
         pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
-    } else if (oh->version == OFP11_VERSION) {
-        const struct ofp11_port_mod *opm = (const struct ofp11_port_mod *) oh;
+    } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
+        const struct ofp11_port_mod *opm = b.data;
         enum ofperr error;
 
-        if (oh->length != htons(sizeof *opm)) {
-            return OFPERR_OFPBRC_BAD_LEN;
-        }
-
         error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
         if (error) {
             return error;
@@ -2725,7 +2900,7 @@ ofputil_decode_port_mod(const struct ofp_header *oh,
         pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
         pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
     } else {
-        return OFPERR_OFPBRC_BAD_VERSION;
+        return OFPERR_OFPBRC_BAD_TYPE;
     }
 
     pm->config &= pm->mask;
@@ -2739,452 +2914,368 @@ struct ofpbuf *
 ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
                         enum ofputil_protocol protocol)
 {
-    uint8_t ofp_version = ofputil_protocol_to_ofp_version(protocol);
+    enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
     struct ofpbuf *b;
 
-    if (ofp_version == OFP10_VERSION) {
+    switch (ofp_version) {
+    case OFP10_VERSION: {
         struct ofp10_port_mod *opm;
 
-        opm = make_openflow(sizeof *opm, OFPT10_PORT_MOD, &b);
+        b = ofpraw_alloc(OFPRAW_OFPT10_PORT_MOD, ofp_version, 0);
+        opm = ofpbuf_put_zeros(b, sizeof *opm);
         opm->port_no = htons(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);
         opm->advertise = netdev_port_features_to_ofp10(pm->advertise);
-    } else if (ofp_version == OFP11_VERSION) {
+        break;
+    }
+
+    case OFP11_VERSION:
+    case OFP12_VERSION: {
         struct ofp11_port_mod *opm;
 
-        opm = make_openflow(sizeof *opm, OFPT11_PORT_MOD, &b);
-        opm->port_no = htonl(pm->port_no);
+        b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0);
+        opm = ofpbuf_put_zeros(b, sizeof *opm);
+        opm->port_no = ofputil_port_to_ofp11(pm->port_no);
         memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
         opm->config = htonl(pm->config & OFPPC11_ALL);
         opm->mask = htonl(pm->mask & OFPPC11_ALL);
         opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
-    } else {
+        break;
+    }
+
+    default:
         NOT_REACHED();
     }
 
     return b;
 }
+\f
+/* ofputil_flow_monitor_request */
 
-struct ofpbuf *
-ofputil_encode_packet_out(const struct ofputil_packet_out *po)
+/* Converts an NXST_FLOW_MONITOR request in 'msg' into an abstract
+ * ofputil_flow_monitor_request in 'rq'.
+ *
+ * Multiple NXST_FLOW_MONITOR requests can be packed into a single OpenFlow
+ * message.  Calling this function multiple times for a single 'msg' iterates
+ * through the requests.  The caller must initially leave 'msg''s layer
+ * pointers null and not modify them between calls.
+ *
+ * Returns 0 if successful, EOF if no requests were left in this 'msg',
+ * otherwise an OFPERR_* value. */
+int
+ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq,
+                                    struct ofpbuf *msg)
 {
-    struct ofp_packet_out *opo;
-    size_t actions_len;
-    struct ofpbuf *msg;
-    size_t size;
+    struct nx_flow_monitor_request *nfmr;
+    uint16_t flags;
 
-    actions_len = po->n_actions * sizeof *po->actions;
-    size = sizeof *opo + actions_len;
-    if (po->buffer_id == UINT32_MAX) {
-        size += po->packet_len;
+    if (!msg->l2) {
+        msg->l2 = msg->data;
+        ofpraw_pull_assert(msg);
     }
 
-    msg = ofpbuf_new(size);
-    opo = put_openflow(sizeof *opo, OFPT10_PACKET_OUT, msg);
-    opo->buffer_id = htonl(po->buffer_id);
-    opo->in_port = htons(po->in_port);
-    opo->actions_len = htons(actions_len);
-    ofpbuf_put(msg, po->actions, actions_len);
-    if (po->buffer_id == UINT32_MAX) {
-        ofpbuf_put(msg, po->packet, po->packet_len);
+    if (!msg->size) {
+        return EOF;
     }
-    update_openflow_length(msg);
 
-    return msg;
-}
+    nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
+    if (!nfmr) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR request has %zu "
+                     "leftover bytes at end", msg->size);
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
 
-/* Returns a string representing the message type of 'type'.  The string is the
- * enumeration constant for the type, e.g. "OFPT_HELLO".  For statistics
- * messages, the constant is followed by "request" or "reply",
- * e.g. "OFPST_AGGREGATE reply". */
-const char *
-ofputil_msg_type_name(const struct ofputil_msg_type *type)
-{
-    return type->name;
-}
-\f
-/* Allocates and stores in '*bufferp' a new ofpbuf with a size of
- * 'openflow_len', starting with an OpenFlow header with the given 'type' and
- * an arbitrary transaction id.  Allocated bytes beyond the header, if any, are
- * zeroed.
- *
- * The caller is responsible for freeing '*bufferp' when it is no longer
- * needed.
- *
- * The OpenFlow header length is initially set to 'openflow_len'; if the
- * message is later extended, the length should be updated with
- * update_openflow_length() before sending.
- *
- * Returns the header. */
-void *
-make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **bufferp)
-{
-    *bufferp = ofpbuf_new(openflow_len);
-    return put_openflow_xid(openflow_len, type, alloc_xid(), *bufferp);
-}
+    flags = ntohs(nfmr->flags);
+    if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
+        || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
+                     | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
+        VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
+                     flags);
+        return OFPERR_NXBRC_FM_BAD_FLAGS;
+    }
 
-/* Similar to make_openflow() but creates a Nicira vendor extension message
- * with the specific 'subtype'.  'subtype' should be in host byte order. */
-void *
-make_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf **bufferp)
-{
-    return make_nxmsg_xid(openflow_len, subtype, alloc_xid(), bufferp);
-}
+    if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
 
-/* Allocates and stores in '*bufferp' a new ofpbuf with a size of
- * 'openflow_len', starting with an OpenFlow header with the given 'type' and
- * transaction id 'xid'.  Allocated bytes beyond the header, if any, are
- * zeroed.
- *
- * The caller is responsible for freeing '*bufferp' when it is no longer
- * needed.
- *
- * The OpenFlow header length is initially set to 'openflow_len'; if the
- * message is later extended, the length should be updated with
- * update_openflow_length() before sending.
- *
- * Returns the header. */
-void *
-make_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
-                  struct ofpbuf **bufferp)
-{
-    *bufferp = ofpbuf_new(openflow_len);
-    return put_openflow_xid(openflow_len, type, xid, *bufferp);
-}
+    rq->id = ntohl(nfmr->id);
+    rq->flags = flags;
+    rq->out_port = ntohs(nfmr->out_port);
+    rq->table_id = nfmr->table_id;
 
-/* Similar to make_openflow_xid() but creates a Nicira vendor extension message
- * with the specific 'subtype'.  'subtype' should be in host byte order. */
-void *
-make_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
-               struct ofpbuf **bufferp)
-{
-    *bufferp = ofpbuf_new(openflow_len);
-    return put_nxmsg_xid(openflow_len, subtype, xid, *bufferp);
+    return nx_pull_match(msg, ntohs(nfmr->match_len), OFP_DEFAULT_PRIORITY,
+                         &rq->match, NULL, NULL);
 }
 
-/* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
- * with the given 'type' and an arbitrary transaction id.  Allocated bytes
- * beyond the header, if any, are zeroed.
- *
- * The OpenFlow header length is initially set to 'openflow_len'; if the
- * message is later extended, the length should be updated with
- * update_openflow_length() before sending.
- *
- * Returns the header. */
-void *
-put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *buffer)
+void
+ofputil_append_flow_monitor_request(
+    const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg)
 {
-    return put_openflow_xid(openflow_len, type, alloc_xid(), buffer);
+    struct nx_flow_monitor_request *nfmr;
+    size_t start_ofs;
+    int match_len;
+
+    if (!msg->size) {
+        ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, OFP10_VERSION, msg);
+    }
+
+    start_ofs = msg->size;
+    ofpbuf_put_zeros(msg, sizeof *nfmr);
+    match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0));
+
+    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->match_len = htons(match_len);
+    nfmr->table_id = rq->table_id;
 }
 
-/* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
- * with the given 'type' and an transaction id 'xid'.  Allocated bytes beyond
- * the header, if any, are zeroed.
+/* Converts an NXST_FLOW_MONITOR reply (also known as a flow update) in 'msg'
+ * into an abstract ofputil_flow_update in 'update'.  The caller must have
+ * initialized update->match to point to space allocated for a cls_rule.
  *
- * The OpenFlow header length is initially set to 'openflow_len'; if the
- * message is later extended, the length should be updated with
- * update_openflow_length() before sending.
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the update's
+ * actions (except for NXFME_ABBREV, which never includes actions).  The caller
+ * must initialize 'ofpacts' and retains ownership of it.  'update->ofpacts'
+ * will point into the 'ofpacts' buffer.
  *
- * Returns the header. */
-void *
-put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
-                 struct ofpbuf *buffer)
+ * Multiple flow updates can be packed into a single OpenFlow message.  Calling
+ * this function multiple times for a single 'msg' iterates through the
+ * updates.  The caller must initially leave 'msg''s layer pointers null and
+ * not modify them between calls.
+ *
+ * Returns 0 if successful, EOF if no updates were left in this 'msg',
+ * otherwise an OFPERR_* value. */
+int
+ofputil_decode_flow_update(struct ofputil_flow_update *update,
+                           struct ofpbuf *msg, struct ofpbuf *ofpacts)
 {
-    struct ofp_header *oh;
+    struct nx_flow_update_header *nfuh;
+    unsigned int length;
 
-    assert(openflow_len >= sizeof *oh);
-    assert(openflow_len <= UINT16_MAX);
+    if (!msg->l2) {
+        msg->l2 = msg->data;
+        ofpraw_pull_assert(msg);
+    }
 
-    oh = ofpbuf_put_uninit(buffer, openflow_len);
-    oh->version = OFP10_VERSION;
-    oh->type = type;
-    oh->length = htons(openflow_len);
-    oh->xid = xid;
-    memset(oh + 1, 0, openflow_len - sizeof *oh);
-    return oh;
-}
+    if (!msg->size) {
+        return EOF;
+    }
 
-/* Similar to put_openflow() but append a Nicira vendor extension message with
- * the specific 'subtype'.  'subtype' should be in host byte order. */
-void *
-put_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf *buffer)
-{
-    return put_nxmsg_xid(openflow_len, subtype, alloc_xid(), buffer);
-}
+    if (msg->size < sizeof(struct nx_flow_update_header)) {
+        goto bad_len;
+    }
 
-/* Similar to put_openflow_xid() but append a Nicira vendor extension message
- * with the specific 'subtype'.  'subtype' should be in host byte order. */
-void *
-put_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
-              struct ofpbuf *buffer)
-{
-    struct nicira_header *nxh;
+    nfuh = msg->data;
+    update->event = ntohs(nfuh->event);
+    length = ntohs(nfuh->length);
+    if (length > msg->size || length % 8) {
+        goto bad_len;
+    }
 
-    nxh = put_openflow_xid(openflow_len, OFPT_VENDOR, xid, buffer);
-    nxh->vendor = htonl(NX_VENDOR_ID);
-    nxh->subtype = htonl(subtype);
-    return nxh;
-}
+    if (update->event == NXFME_ABBREV) {
+        struct nx_flow_update_abbrev *nfua;
 
-/* Updates the 'length' field of the OpenFlow message in 'buffer' to
- * 'buffer->size'. */
-void
-update_openflow_length(struct ofpbuf *buffer)
-{
-    struct ofp_header *oh = ofpbuf_at_assert(buffer, 0, sizeof *oh);
-    oh->length = htons(buffer->size);
-}
+        if (length != sizeof *nfua) {
+            goto bad_len;
+        }
 
-static void
-put_stats__(ovs_be32 xid, uint8_t ofp_type,
-            ovs_be16 ofpst_type, ovs_be32 nxst_subtype,
-            struct ofpbuf *msg)
-{
-    if (ofpst_type == htons(OFPST_VENDOR)) {
-        struct nicira_stats_msg *nsm;
+        nfua = ofpbuf_pull(msg, sizeof *nfua);
+        update->xid = nfua->xid;
+        return 0;
+    } else if (update->event == NXFME_ADDED
+               || update->event == NXFME_DELETED
+               || update->event == NXFME_MODIFIED) {
+        struct nx_flow_update_full *nfuf;
+        unsigned int actions_len;
+        unsigned int match_len;
+        enum ofperr error;
 
-        nsm = put_openflow_xid(sizeof *nsm, ofp_type, xid, msg);
-        nsm->vsm.osm.type = ofpst_type;
-        nsm->vsm.vendor = htonl(NX_VENDOR_ID);
-        nsm->subtype = nxst_subtype;
-    } else {
-        struct ofp_stats_msg *osm;
+        if (length < sizeof *nfuf) {
+            goto bad_len;
+        }
 
-        osm = put_openflow_xid(sizeof *osm, ofp_type, xid, msg);
-        osm->type = ofpst_type;
-    }
-}
+        nfuf = ofpbuf_pull(msg, sizeof *nfuf);
+        match_len = ntohs(nfuf->match_len);
+        if (sizeof *nfuf + match_len > length) {
+            goto bad_len;
+        }
 
-/* Creates a statistics request message with total length 'openflow_len'
- * (including all headers) and the given 'ofpst_type', and stores the buffer
- * containing the new message in '*bufferp'.  If 'ofpst_type' is OFPST_VENDOR
- * then 'nxst_subtype' is used as the Nicira vendor extension statistics
- * subtype (otherwise 'nxst_subtype' is ignored).
- *
- * Initializes bytes following the headers to all-bits-zero.
- *
- * Returns the first byte of the new message. */
-void *
-ofputil_make_stats_request(size_t openflow_len, uint16_t ofpst_type,
-                           uint32_t nxst_subtype, struct ofpbuf **bufferp)
-{
-    struct ofpbuf *msg;
+        update->reason = ntohs(nfuf->reason);
+        update->idle_timeout = ntohs(nfuf->idle_timeout);
+        update->hard_timeout = ntohs(nfuf->hard_timeout);
+        update->table_id = nfuf->table_id;
+        update->cookie = nfuf->cookie;
+
+        error = nx_pull_match(msg, match_len, ntohs(nfuf->priority),
+                              update->match, NULL, NULL);
+        if (error) {
+            return error;
+        }
+
+        actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
+        error = ofpacts_pull_openflow10(msg, actions_len, ofpacts);
+        if (error) {
+            return error;
+        }
 
-    msg = *bufferp = ofpbuf_new(openflow_len);
-    put_stats__(alloc_xid(), OFPT10_STATS_REQUEST,
-                htons(ofpst_type), htonl(nxst_subtype), msg);
-    ofpbuf_padto(msg, openflow_len);
+        update->ofpacts = ofpacts->data;
+        update->ofpacts_len = ofpacts->size;
+        return 0;
+    } else {
+        VLOG_WARN_RL(&bad_ofmsg_rl,
+                     "NXST_FLOW_MONITOR reply has bad event %"PRIu16,
+                     ntohs(nfuh->event));
+        return OFPERR_OFPET_BAD_REQUEST;
+    }
 
-    return msg->data;
+bad_len:
+    VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR reply has %zu "
+                 "leftover bytes at end", msg->size);
+    return OFPERR_OFPBRC_BAD_LEN;
 }
 
-static void
-put_stats_reply__(const struct ofp_stats_msg *request, struct ofpbuf *msg)
+uint32_t
+ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh)
 {
-    assert(request->header.type == OFPT10_STATS_REQUEST ||
-           request->header.type == OFPT10_STATS_REPLY);
-    put_stats__(request->header.xid, OFPT10_STATS_REPLY, request->type,
-                (request->type != htons(OFPST_VENDOR)
-                 ? htonl(0)
-                 : ((const struct nicira_stats_msg *) request)->subtype),
-                msg);
+    const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
+
+    return ntohl(cancel->id);
 }
 
-/* Creates a statistics reply message with total length 'openflow_len'
- * (including all headers) and the same type (either a standard OpenFlow
- * statistics type or a Nicira extension type and subtype) as 'request', and
- * stores the buffer containing the new message in '*bufferp'.
- *
- * Initializes bytes following the headers to all-bits-zero.
- *
- * Returns the first byte of the new message. */
-void *
-ofputil_make_stats_reply(size_t openflow_len,
-                         const struct ofp_stats_msg *request,
-                         struct ofpbuf **bufferp)
+struct ofpbuf *
+ofputil_encode_flow_monitor_cancel(uint32_t id)
 {
+    struct nx_flow_monitor_cancel *nfmc;
     struct ofpbuf *msg;
 
-    msg = *bufferp = ofpbuf_new(openflow_len);
-    put_stats_reply__(request, msg);
-    ofpbuf_padto(msg, openflow_len);
-
-    return msg->data;
+    msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, OFP10_VERSION, 0);
+    nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
+    nfmc->id = htonl(id);
+    return msg;
 }
 
-/* Initializes 'replies' as a list of ofpbufs that will contain a series of
- * replies to 'request', which should be an OpenFlow or Nicira extension
- * statistics request.  Initially 'replies' will have a single reply message
- * that has only a header.  The functions ofputil_reserve_stats_reply() and
- * ofputil_append_stats_reply() may be used to add to the reply. */
 void
-ofputil_start_stats_reply(const struct ofp_stats_msg *request,
-                          struct list *replies)
+ofputil_start_flow_update(struct list *replies)
 {
     struct ofpbuf *msg;
 
-    msg = ofpbuf_new(1024);
-    put_stats_reply__(request, msg);
+    msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, OFP10_VERSION,
+                           htonl(0), 1024);
 
     list_init(replies);
     list_push_back(replies, &msg->list_node);
 }
 
-/* Prepares to append up to 'len' bytes to the series of statistics replies in
- * 'replies', which should have been initialized with
- * ofputil_start_stats_reply().  Returns an ofpbuf with at least 'len' bytes of
- * tailroom.  (The 'len' bytes have not actually be allocated; the caller must
- * do so with e.g. ofpbuf_put_uninit().) */
-struct ofpbuf *
-ofputil_reserve_stats_reply(size_t len, struct list *replies)
+void
+ofputil_append_flow_update(const struct ofputil_flow_update *update,
+                           struct list *replies)
 {
-    struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
-    struct ofp_stats_msg *osm = msg->data;
+    struct nx_flow_update_header *nfuh;
+    struct ofpbuf *msg;
+    size_t start_ofs;
+
+    msg = ofpbuf_from_list(list_back(replies));
+    start_ofs = msg->size;
 
-    if (msg->size + len <= UINT16_MAX) {
-        ofpbuf_prealloc_tailroom(msg, len);
+    if (update->event == NXFME_ABBREV) {
+        struct nx_flow_update_abbrev *nfua;
+
+        nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
+        nfua->xid = update->xid;
     } else {
-        osm->flags |= htons(OFPSF_REPLY_MORE);
+        struct nx_flow_update_full *nfuf;
+        int match_len;
+
+        ofpbuf_put_zeros(msg, sizeof *nfuf);
+        match_len = nx_put_match(msg, update->match, htonll(0), htonll(0));
+        ofpacts_put_openflow10(update->ofpacts, update->ofpacts_len, msg);
 
-        msg = ofpbuf_new(MAX(1024, sizeof(struct nicira_stats_msg) + len));
-        put_stats_reply__(osm, msg);
-        list_push_back(replies, &msg->list_node);
+        nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
+        nfuf->reason = htons(update->reason);
+        nfuf->priority = htons(update->match->priority);
+        nfuf->idle_timeout = htons(update->idle_timeout);
+        nfuf->hard_timeout = htons(update->hard_timeout);
+        nfuf->match_len = htons(match_len);
+        nfuf->table_id = update->table_id;
+        nfuf->cookie = update->cookie;
     }
-    return msg;
-}
 
-/* Appends 'len' bytes to the series of statistics replies in 'replies', and
- * returns the first byte. */
-void *
-ofputil_append_stats_reply(size_t len, struct list *replies)
-{
-    return ofpbuf_put_uninit(ofputil_reserve_stats_reply(len, replies), len);
-}
+    nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
+    nfuh->length = htons(msg->size - start_ofs);
+    nfuh->event = htons(update->event);
 
-/* Returns the first byte past the ofp_stats_msg header in 'oh'. */
-const void *
-ofputil_stats_body(const struct ofp_header *oh)
-{
-    assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
-    return (const struct ofp_stats_msg *) oh + 1;
+    ofpmp_postappend(replies, start_ofs);
 }
-
-/* Returns the number of bytes past the ofp_stats_msg header in 'oh'. */
-size_t
-ofputil_stats_body_len(const struct ofp_header *oh)
+\f
+struct ofpbuf *
+ofputil_encode_packet_out(const struct ofputil_packet_out *po,
+                          enum ofputil_protocol protocol)
 {
-    assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
-    return ntohs(oh->length) - sizeof(struct ofp_stats_msg);
-}
+    enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
+    struct ofpbuf *msg;
+    size_t size;
 
-/* Returns the first byte past the nicira_stats_msg header in 'oh'. */
-const void *
-ofputil_nxstats_body(const struct ofp_header *oh)
-{
-    assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
-    return ((const struct nicira_stats_msg *) oh) + 1;
-}
+    size = po->ofpacts_len;
+    if (po->buffer_id == UINT32_MAX) {
+        size += po->packet_len;
+    }
 
-/* Returns the number of bytes past the nicira_stats_msg header in 'oh'. */
-size_t
-ofputil_nxstats_body_len(const struct ofp_header *oh)
-{
-    assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
-    return ntohs(oh->length) - sizeof(struct nicira_stats_msg);
-}
+    switch (ofp_version) {
+    case OFP10_VERSION: {
+        struct ofp_packet_out *opo;
+        size_t actions_ofs;
 
-struct ofpbuf *
-make_flow_mod(uint16_t command, const struct cls_rule *rule,
-              size_t actions_len)
-{
-    struct ofp_flow_mod *ofm;
-    size_t size = sizeof *ofm + actions_len;
-    struct ofpbuf *out = ofpbuf_new(size);
-    ofm = ofpbuf_put_zeros(out, sizeof *ofm);
-    ofm->header.version = OFP10_VERSION;
-    ofm->header.type = OFPT10_FLOW_MOD;
-    ofm->header.length = htons(size);
-    ofm->cookie = 0;
-    ofm->priority = htons(MIN(rule->priority, UINT16_MAX));
-    ofputil_cls_rule_to_match(rule, &ofm->match);
-    ofm->command = htons(command);
-    return out;
-}
+        msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size);
+        ofpbuf_put_zeros(msg, sizeof *opo);
+        actions_ofs = msg->size;
+        ofpacts_put_openflow10(po->ofpacts, po->ofpacts_len, msg);
 
-struct ofpbuf *
-make_add_flow(const struct cls_rule *rule, uint32_t buffer_id,
-              uint16_t idle_timeout, size_t actions_len)
-{
-    struct ofpbuf *out = make_flow_mod(OFPFC_ADD, rule, actions_len);
-    struct ofp_flow_mod *ofm = out->data;
-    ofm->idle_timeout = htons(idle_timeout);
-    ofm->hard_timeout = htons(OFP_FLOW_PERMANENT);
-    ofm->buffer_id = htonl(buffer_id);
-    return out;
-}
+        opo = msg->l3;
+        opo->buffer_id = htonl(po->buffer_id);
+        opo->in_port = htons(po->in_port);
+        opo->actions_len = htons(msg->size - actions_ofs);
+        break;
+    }
 
-struct ofpbuf *
-make_del_flow(const struct cls_rule *rule)
-{
-    struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, rule, 0);
-    struct ofp_flow_mod *ofm = out->data;
-    ofm->out_port = htons(OFPP_NONE);
-    return out;
-}
+    case OFP11_VERSION:
+    case OFP12_VERSION: {
+        struct ofp11_packet_out *opo;
+        size_t len;
 
-struct ofpbuf *
-make_add_simple_flow(const struct cls_rule *rule,
-                     uint32_t buffer_id, uint16_t out_port,
-                     uint16_t idle_timeout)
-{
-    if (out_port != OFPP_NONE) {
-        struct ofp_action_output *oao;
-        struct ofpbuf *buffer;
+        msg = ofpraw_alloc(OFPRAW_OFPT11_PACKET_OUT, ofp_version, size);
+        ofpbuf_put_zeros(msg, sizeof *opo);
+        len = ofpacts_put_openflow11_actions(po->ofpacts, po->ofpacts_len, msg);
 
-        buffer = make_add_flow(rule, buffer_id, idle_timeout, sizeof *oao);
-        ofputil_put_OFPAT10_OUTPUT(buffer)->port = htons(out_port);
-        return buffer;
-    } else {
-        return make_add_flow(rule, buffer_id, idle_timeout, 0);
+        opo = msg->l3;
+        opo->buffer_id = htonl(po->buffer_id);
+        opo->in_port = ofputil_port_to_ofp11(po->in_port);
+        opo->actions_len = htons(len);
+        break;
     }
-}
 
-struct ofpbuf *
-make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason,
-               const struct ofpbuf *payload, int max_send_len)
-{
-    struct ofp_packet_in *opi;
-    struct ofpbuf *buf;
-    int send_len;
+    default:
+        NOT_REACHED();
+    }
+
+    if (po->buffer_id == UINT32_MAX) {
+        ofpbuf_put(msg, po->packet, po->packet_len);
+    }
 
-    send_len = MIN(max_send_len, payload->size);
-    buf = ofpbuf_new(sizeof *opi + send_len);
-    opi = put_openflow_xid(offsetof(struct ofp_packet_in, data),
-                           OFPT_PACKET_IN, 0, buf);
-    opi->buffer_id = htonl(buffer_id);
-    opi->total_len = htons(payload->size);
-    opi->in_port = htons(in_port);
-    opi->reason = reason;
-    ofpbuf_put(buf, payload->data, send_len);
-    update_openflow_length(buf);
+    ofpmsg_update_length(msg);
 
-    return buf;
+    return msg;
 }
-
+\f
 /* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
 struct ofpbuf *
-make_echo_request(void)
+make_echo_request(enum ofp_version ofp_version)
 {
-    struct ofp_header *rq;
-    struct ofpbuf *out = ofpbuf_new(sizeof *rq);
-    rq = ofpbuf_put_uninit(out, sizeof *rq);
-    rq->version = OFP10_VERSION;
-    rq->type = OFPT_ECHO_REQUEST;
-    rq->length = htons(sizeof *rq);
-    rq->xid = htonl(0);
-    return out;
+    return ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, ofp_version,
+                            htonl(0), 0);
 }
 
 /* Creates and returns an OFPT_ECHO_REPLY message matching the
@@ -3192,20 +3283,37 @@ make_echo_request(void)
 struct ofpbuf *
 make_echo_reply(const struct ofp_header *rq)
 {
-    size_t size = ntohs(rq->length);
-    struct ofpbuf *out = ofpbuf_new(size);
-    struct ofp_header *reply = ofpbuf_put(out, rq, size);
-    reply->type = OFPT_ECHO_REPLY;
-    return out;
+    struct ofpbuf rq_buf;
+    struct ofpbuf *reply;
+
+    ofpbuf_use_const(&rq_buf, rq, ntohs(rq->length));
+    ofpraw_pull_assert(&rq_buf);
+
+    reply = ofpraw_alloc_reply(OFPRAW_OFPT_ECHO_REPLY, rq, rq_buf.size);
+    ofpbuf_put(reply, rq_buf.data, rq_buf.size);
+    return reply;
 }
 
 struct ofpbuf *
-ofputil_encode_barrier_request(void)
+ofputil_encode_barrier_request(enum ofp_version ofp_version)
 {
-    struct ofpbuf *msg;
+    enum ofpraw type;
 
-    make_openflow(sizeof(struct ofp_header), OFPT10_BARRIER_REQUEST, &msg);
-    return msg;
+    switch (ofp_version) {
+    case OFP12_VERSION:
+    case OFP11_VERSION:
+        type = OFPRAW_OFPT11_BARRIER_REQUEST;
+        break;
+
+    case OFP10_VERSION:
+        type = OFPRAW_OFPT10_BARRIER_REQUEST;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    return ofpraw_alloc(type, ofp_version, 0);
 }
 
 const char *
@@ -3289,6 +3397,7 @@ ofputil_check_output_port(uint16_t port, int max_ports)
     case OFPP_FLOOD:
     case OFPP_ALL:
     case OFPP_CONTROLLER:
+    case OFPP_NONE:
     case OFPP_LOCAL:
         return 0;
 
@@ -3362,275 +3471,35 @@ ofputil_format_port(uint16_t port, struct ds *s)
     ds_put_cstr(s, name);
 }
 
-static enum ofperr
-check_resubmit_table(const struct nx_action_resubmit *nar)
-{
-    if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
-        return OFPERR_OFPBAC_BAD_ARGUMENT;
-    }
-    return 0;
-}
-
-static enum ofperr
-check_output_reg(const struct nx_action_output_reg *naor,
-                 const struct flow *flow)
-{
-    struct mf_subfield src;
-    size_t i;
-
-    for (i = 0; i < sizeof naor->zero; i++) {
-        if (naor->zero[i]) {
-            return OFPERR_OFPBAC_BAD_ARGUMENT;
-        }
-    }
-
-    nxm_decode(&src, naor->src, naor->ofs_nbits);
-    return mf_check_src(&src, flow);
-}
-
-enum ofperr
-validate_actions(const union ofp_action *actions, size_t n_actions,
-                 const struct flow *flow, int max_ports)
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', tries to pull the first element from the array.  If
+ * successful, initializes '*pp' with an abstract representation of the
+ * port and returns 0.  If no ports remain to be decoded, returns EOF.
+ * On an error, returns a positive OFPERR_* value. */
+int
+ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
+                      struct ofputil_phy_port *pp)
 {
-    const union ofp_action *a;
-    size_t left;
-
-    OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
-        enum ofperr error;
-        uint16_t port;
-        int code;
-
-        code = ofputil_decode_action(a);
-        if (code < 0) {
-            error = -code;
-            VLOG_WARN_RL(&bad_ofmsg_rl,
-                         "action decoding error at offset %td (%s)",
-                         (a - actions) * sizeof *a, ofperr_get_name(error));
-
-            return error;
-        }
-
-        error = 0;
-        switch ((enum ofputil_action_code) code) {
-        case OFPUTIL_OFPAT10_OUTPUT:
-            error = ofputil_check_output_port(ntohs(a->output.port),
-                                              max_ports);
-            break;
-
-        case OFPUTIL_OFPAT10_SET_VLAN_VID:
-            if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
-                error = OFPERR_OFPBAC_BAD_ARGUMENT;
-            }
-            break;
-
-        case OFPUTIL_OFPAT10_SET_VLAN_PCP:
-            if (a->vlan_pcp.vlan_pcp & ~7) {
-                error = OFPERR_OFPBAC_BAD_ARGUMENT;
-            }
-            break;
-
-        case OFPUTIL_OFPAT10_ENQUEUE:
-            port = ntohs(((const struct ofp_action_enqueue *) a)->port);
-            if (port >= max_ports && port != OFPP_IN_PORT
-                && port != OFPP_LOCAL) {
-                error = OFPERR_OFPBAC_BAD_OUT_PORT;
-            }
-            break;
-
-        case OFPUTIL_NXAST_REG_MOVE:
-            error = nxm_check_reg_move((const struct nx_action_reg_move *) a,
-                                       flow);
-            break;
-
-        case OFPUTIL_NXAST_REG_LOAD:
-            error = nxm_check_reg_load((const struct nx_action_reg_load *) a,
-                                       flow);
-            break;
-
-        case OFPUTIL_NXAST_MULTIPATH:
-            error = multipath_check((const struct nx_action_multipath *) a,
-                                    flow);
-            break;
-
-        case OFPUTIL_NXAST_AUTOPATH:
-            error = autopath_check((const struct nx_action_autopath *) a,
-                                   flow);
-            break;
-
-        case OFPUTIL_NXAST_BUNDLE:
-        case OFPUTIL_NXAST_BUNDLE_LOAD:
-            error = bundle_check((const struct nx_action_bundle *) a,
-                                 max_ports, flow);
-            break;
-
-        case OFPUTIL_NXAST_OUTPUT_REG:
-            error = check_output_reg((const struct nx_action_output_reg *) a,
-                                     flow);
-            break;
-
-        case OFPUTIL_NXAST_RESUBMIT_TABLE:
-            error = check_resubmit_table(
-                (const struct nx_action_resubmit *) a);
-            break;
-
-        case OFPUTIL_NXAST_LEARN:
-            error = learn_check((const struct nx_action_learn *) a, flow);
-            break;
-
-        case OFPUTIL_NXAST_CONTROLLER:
-            if (((const struct nx_action_controller *) a)->zero) {
-                error = OFPERR_NXBAC_MUST_BE_ZERO;
-            }
-            break;
-
-        case OFPUTIL_OFPAT10_STRIP_VLAN:
-        case OFPUTIL_OFPAT10_SET_NW_SRC:
-        case OFPUTIL_OFPAT10_SET_NW_DST:
-        case OFPUTIL_OFPAT10_SET_NW_TOS:
-        case OFPUTIL_OFPAT10_SET_TP_SRC:
-        case OFPUTIL_OFPAT10_SET_TP_DST:
-        case OFPUTIL_OFPAT10_SET_DL_SRC:
-        case OFPUTIL_OFPAT10_SET_DL_DST:
-        case OFPUTIL_NXAST_RESUBMIT:
-        case OFPUTIL_NXAST_SET_TUNNEL:
-        case OFPUTIL_NXAST_SET_QUEUE:
-        case OFPUTIL_NXAST_POP_QUEUE:
-        case OFPUTIL_NXAST_NOTE:
-        case OFPUTIL_NXAST_SET_TUNNEL64:
-        case OFPUTIL_NXAST_EXIT:
-        case OFPUTIL_NXAST_DEC_TTL:
-        case OFPUTIL_NXAST_FIN_TIMEOUT:
-            break;
-        }
-
-        if (error) {
-            VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)",
-                         (a - actions) * sizeof *a, ofperr_get_name(error));
-            return error;
-        }
-    }
-    if (left) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu",
-                     (n_actions - left) * sizeof *a);
-        return OFPERR_OFPBAC_BAD_LEN;
+    switch (ofp_version) {
+    case OFP10_VERSION: {
+        const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp);
+        return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF;
     }
-    return 0;
-}
-
-struct ofputil_action {
-    int code;
-    unsigned int min_len;
-    unsigned int max_len;
-};
-
-static const struct ofputil_action action_bad_type
-    = { -OFPERR_OFPBAC_BAD_TYPE,   0, UINT_MAX };
-static const struct ofputil_action action_bad_len
-    = { -OFPERR_OFPBAC_BAD_LEN,    0, UINT_MAX };
-static const struct ofputil_action action_bad_vendor
-    = { -OFPERR_OFPBAC_BAD_VENDOR, 0, UINT_MAX };
-
-static const struct ofputil_action *
-ofputil_decode_ofpat_action(const union ofp_action *a)
-{
-    enum ofp10_action_type type = ntohs(a->type);
-
-    switch (type) {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME)                    \
-        case ENUM: {                                        \
-            static const struct ofputil_action action = {   \
-                OFPUTIL_##ENUM,                             \
-                sizeof(struct STRUCT),                      \
-                sizeof(struct STRUCT)                       \
-            };                                              \
-            return &action;                                 \
-        }
-#include "ofp-util.def"
-
-    case OFPAT10_VENDOR:
-    default:
-        return &action_bad_type;
+    case OFP11_VERSION:
+    case OFP12_VERSION: {
+        const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op);
+        return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
     }
-}
-
-static const struct ofputil_action *
-ofputil_decode_nxast_action(const union ofp_action *a)
-{
-    const struct nx_action_header *nah = (const struct nx_action_header *) a;
-    enum nx_action_subtype subtype = ntohs(nah->subtype);
-
-    switch (subtype) {
-#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)            \
-        case ENUM: {                                            \
-            static const struct ofputil_action action = {       \
-                OFPUTIL_##ENUM,                                 \
-                sizeof(struct STRUCT),                          \
-                EXTENSIBLE ? UINT_MAX : sizeof(struct STRUCT)   \
-            };                                                  \
-            return &action;                                     \
-        }
-#include "ofp-util.def"
-
-    case NXAST_SNAT__OBSOLETE:
-    case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
     default:
-        return &action_bad_type;
-    }
-}
-
-/* Parses 'a' to determine its type.  Returns a nonnegative OFPUTIL_OFPAT10_* or
- * OFPUTIL_NXAST_* constant if successful, otherwise a negative OFPERR_* error
- * code.
- *
- * The caller must have already verified that 'a''s length is correct (that is,
- * a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no
- * longer than the amount of space allocated to 'a').
- *
- * This function verifies that 'a''s length is correct for the type of action
- * that it represents. */
-int
-ofputil_decode_action(const union ofp_action *a)
-{
-    const struct ofputil_action *action;
-    uint16_t len = ntohs(a->header.len);
-
-    if (a->type != htons(OFPAT10_VENDOR)) {
-        action = ofputil_decode_ofpat_action(a);
-    } else {
-        switch (ntohl(a->vendor.vendor)) {
-        case NX_VENDOR_ID:
-            if (len < sizeof(struct nx_action_header)) {
-                return -OFPERR_OFPBAC_BAD_LEN;
-            }
-            action = ofputil_decode_nxast_action(a);
-            break;
-        default:
-            action = &action_bad_vendor;
-            break;
-        }
+        NOT_REACHED();
     }
-
-    return (len >= action->min_len && len <= action->max_len
-            ? action->code
-            : -OFPERR_OFPBAC_BAD_LEN);
 }
 
-/* Parses 'a' and returns its type as an OFPUTIL_OFPAT10_* or OFPUTIL_NXAST_*
- * constant.  The caller must have already validated that 'a' is a valid action
- * understood by Open vSwitch (e.g. by a previous successful call to
- * ofputil_decode_action()). */
-enum ofputil_action_code
-ofputil_decode_action_unsafe(const union ofp_action *a)
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', returns the number of elements. */
+size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b)
 {
-    const struct ofputil_action *action;
-
-    if (a->type != htons(OFPAT10_VENDOR)) {
-        action = ofputil_decode_ofpat_action(a);
-    } else {
-        action = ofputil_decode_nxast_action(a);
-    }
-
-    return action->code;
+    return b->size / ofputil_get_phy_port_size(ofp_version);
 }
 
 /* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if
@@ -3642,7 +3511,9 @@ int
 ofputil_action_code_from_name(const char *name)
 {
     static const char *names[OFPUTIL_N_ACTIONS] = {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME)             NAME,
+        NULL,
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME)           NAME,
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME)           NAME,
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
 #include "ofp-util.def"
     };
@@ -3666,8 +3537,12 @@ void *
 ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
 {
     switch (code) {
+    case OFPUTIL_ACTION_INVALID:
+        NOT_REACHED();
+
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)                    \
     case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#define OFPAT11_ACTION OFPAT10_ACTION
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)        \
     case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
 #include "ofp-util.def"
@@ -3691,6 +3566,7 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
         ofputil_init_##ENUM(s);                                 \
         return s;                                               \
     }
+#define OFPAT11_ACTION OFPAT10_ACTION
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)            \
     void                                                        \
     ofputil_init_##ENUM(struct STRUCT *s)                       \
@@ -3711,37 +3587,8 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
     }
 #include "ofp-util.def"
 
-/* Returns true if 'action' outputs to 'port', false otherwise. */
-bool
-action_outputs_to_port(const union ofp_action *action, ovs_be16 port)
-{
-    switch (ntohs(action->type)) {
-    case OFPAT10_OUTPUT:
-        return action->output.port == port;
-    case OFPAT10_ENQUEUE:
-        return ((const struct ofp_action_enqueue *) action)->port == port;
-    default:
-        return false;
-    }
-}
-
-/* "Normalizes" the wildcards in 'rule'.  That means:
- *
- *    1. If the type of level N is known, then only the valid fields for that
- *       level may be specified.  For example, ARP does not have a TOS field,
- *       so nw_tos must be wildcarded if 'rule' specifies an ARP flow.
- *       Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
- *       ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an
- *       IPv4 flow.
- *
- *    2. If the type of level N is not known (or not understood by Open
- *       vSwitch), then no fields at all for that level may be specified.  For
- *       example, Open vSwitch does not understand SCTP, an L4 protocol, so the
- *       L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
- *       SCTP flow.
- */
-void
-ofputil_normalize_rule(struct cls_rule *rule)
+static void
+ofputil_normalize_rule__(struct cls_rule *rule, bool may_log)
 {
     enum {
         MAY_NW_ADDR     = 1 << 0, /* nw_src, nw_dst */
@@ -3800,14 +3647,14 @@ ofputil_normalize_rule(struct cls_rule *rule)
         wc.wildcards |= FWW_NW_TTL;
     }
     if (!(may_match & MAY_ARP_SHA)) {
-        wc.wildcards |= FWW_ARP_SHA;
+        memset(wc.arp_sha_mask, 0, ETH_ADDR_LEN);
     }
     if (!(may_match & MAY_ARP_THA)) {
-        wc.wildcards |= FWW_ARP_THA;
+        memset(wc.arp_tha_mask, 0, ETH_ADDR_LEN);
     }
     if (!(may_match & MAY_IPV6)) {
         wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any;
-        wc.wildcards |= FWW_IPV6_LABEL;
+        wc.ipv6_label_mask = htonl(0);
     }
     if (!(may_match & MAY_ND_TARGET)) {
         wc.nd_target_mask = in6addr_any;
@@ -3815,7 +3662,7 @@ ofputil_normalize_rule(struct cls_rule *rule)
 
     /* Log any changes. */
     if (!flow_wildcards_equal(&wc, &rule->wc)) {
-        bool log = !VLOG_DROP_INFO(&bad_ofmsg_rl);
+        bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl);
         char *pre = log ? cls_rule_to_string(rule) : NULL;
 
         rule->wc = wc;
@@ -3832,55 +3679,37 @@ ofputil_normalize_rule(struct cls_rule *rule)
     }
 }
 
-/* Attempts to pull 'actions_len' bytes from the front of 'b'.  Returns 0 if
- * successful, otherwise an OpenFlow error.
+/* "Normalizes" the wildcards in 'rule'.  That means:
+ *
+ *    1. If the type of level N is known, then only the valid fields for that
+ *       level may be specified.  For example, ARP does not have a TOS field,
+ *       so nw_tos must be wildcarded if 'rule' specifies an ARP flow.
+ *       Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
+ *       ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an
+ *       IPv4 flow.
  *
- * If successful, the first action is stored in '*actionsp' and the number of
- * "union ofp_action" size elements into '*n_actionsp'.  Otherwise NULL and 0
- * are stored, respectively.
+ *    2. If the type of level N is not known (or not understood by Open
+ *       vSwitch), then no fields at all for that level may be specified.  For
+ *       example, Open vSwitch does not understand SCTP, an L4 protocol, so the
+ *       L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
+ *       SCTP flow.
  *
- * This function does not check that the actions are valid (the caller should
- * do so, with validate_actions()).  The caller is also responsible for making
- * sure that 'b->data' is initially aligned appropriately for "union
- * ofp_action". */
-enum ofperr
-ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len,
-                     union ofp_action **actionsp, size_t *n_actionsp)
-{
-    if (actions_len % OFP_ACTION_ALIGN != 0) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
-                     "is not a multiple of %d", actions_len, OFP_ACTION_ALIGN);
-        goto error;
-    }
-
-    *actionsp = ofpbuf_try_pull(b, actions_len);
-    if (*actionsp == NULL) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
-                     "exceeds remaining message length (%zu)",
-                     actions_len, b->size);
-        goto error;
-    }
-
-    *n_actionsp = actions_len / OFP_ACTION_ALIGN;
-    return 0;
-
-error:
-    *actionsp = NULL;
-    *n_actionsp = 0;
-    return OFPERR_OFPBRC_BAD_LEN;
-}
-
-bool
-ofputil_actions_equal(const union ofp_action *a, size_t n_a,
-                      const union ofp_action *b, size_t n_b)
+ * If this function changes 'rule', it logs a rate-limited informational
+ * message. */
+void
+ofputil_normalize_rule(struct cls_rule *rule)
 {
-    return n_a == n_b && (!n_a || !memcmp(a, b, n_a * sizeof *a));
+    ofputil_normalize_rule__(rule, true);
 }
 
-union ofp_action *
-ofputil_actions_clone(const union ofp_action *actions, size_t n)
+/* Same as ofputil_normalize_rule() without the logging.  Thus, this function
+ * is suitable for a program's internal use, whereas ofputil_normalize_rule()
+ * sense for use on flows received from elsewhere (so that a bug in the program
+ * that sent them can be reported and corrected). */
+void
+ofputil_normalize_rule_quiet(struct cls_rule *rule)
 {
-    return n ? xmemdup(actions, n * sizeof *actions) : NULL;
+    ofputil_normalize_rule__(rule, false);
 }
 
 /* Parses a key or a key-value pair from '*stringp'.
@@ -3926,7 +3755,8 @@ ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
         for (value_len = 0; level > 0; value_len++) {
             switch (value[value_len]) {
             case '\0':
-                ovs_fatal(0, "unbalanced parentheses in argument to %s", key);
+                level = 0;
+                break;
 
             case '(':
                 level++;
index 8739ac0..391c14b 100644 (file)
@@ -3,7 +3,7 @@
 #ifndef OFPAT10_ACTION
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)
 #endif
-OFPAT10_ACTION(OFPAT10_OUTPUT,       ofp_action_output,   "output")
+OFPAT10_ACTION(OFPAT10_OUTPUT,       ofp10_action_output, "output")
 OFPAT10_ACTION(OFPAT10_SET_VLAN_VID, ofp_action_vlan_vid, "mod_vlan_vid")
 OFPAT10_ACTION(OFPAT10_SET_VLAN_PCP, ofp_action_vlan_pcp, "mod_vlan_pcp")
 OFPAT10_ACTION(OFPAT10_STRIP_VLAN,   ofp_action_header,   "strip_vlan")
@@ -15,28 +15,51 @@ OFPAT10_ACTION(OFPAT10_SET_NW_TOS,   ofp_action_nw_tos,   "mod_nw_tos")
 OFPAT10_ACTION(OFPAT10_SET_TP_SRC,   ofp_action_tp_port,  "mod_tp_src")
 OFPAT10_ACTION(OFPAT10_SET_TP_DST,   ofp_action_tp_port,  "mod_tp_dst")
 OFPAT10_ACTION(OFPAT10_ENQUEUE,      ofp_action_enqueue,  "enqueue")
-#undef OFPAT10_ACTION
+
+#ifndef OFPAT11_ACTION
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME)
+#endif
+OFPAT11_ACTION(OFPAT11_OUTPUT,       ofp11_action_output, "output")
+OFPAT11_ACTION(OFPAT11_SET_VLAN_VID, ofp_action_vlan_vid, "mod_vlan_vid")
+OFPAT11_ACTION(OFPAT11_SET_VLAN_PCP, ofp_action_vlan_pcp, "mod_vlan_pcp")
+OFPAT11_ACTION(OFPAT11_SET_DL_SRC,   ofp_action_dl_addr,  "mod_dl_src")
+OFPAT11_ACTION(OFPAT11_SET_DL_DST,   ofp_action_dl_addr,  "mod_dl_dst")
+OFPAT11_ACTION(OFPAT11_SET_NW_SRC,   ofp_action_nw_addr,  "mod_nw_src")
+OFPAT11_ACTION(OFPAT11_SET_NW_DST,   ofp_action_nw_addr,  "mod_nw_dst")
+OFPAT11_ACTION(OFPAT11_SET_NW_TOS,   ofp_action_nw_tos,   "mod_nw_tos")
+//OFPAT11_ACTION(OFPAT11_SET_NW_ECN,   ofp11_action_nw_ecn, "mod_nw_ecn")
+OFPAT11_ACTION(OFPAT11_SET_TP_SRC,   ofp_action_tp_port,  "mod_tp_src")
+OFPAT11_ACTION(OFPAT11_SET_TP_DST,   ofp_action_tp_port,  "mod_tp_dst")
+//OFPAT11_ACTION(OFPAT11_PUSH_VLAN,    ofp11_action_push,   "push_vlan")
+//OFPAT11_ACTION(OFPAT11_POP_VLAN,     ofp_action_header,   "pop_vlan")
+//OFPAT11_ACTION(OFPAT11_SET_QUEUE,    ofp11_action_set_queue, "set_queue")
+//OFPAT11_ACTION(OFPAT11_SET_NW_TTL,   ofp11_action_nw_ttl, "set_nw_ttl")
+//OFPAT11_ACTION(OFPAT11_DEC_NW_TTL,   ofp_action_header,   "dec_ttl")
 
 #ifndef NXAST_ACTION
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)
 #endif
-NXAST_ACTION(NXAST_RESUBMIT,       nx_action_resubmit,     0, "resubmit")
-NXAST_ACTION(NXAST_SET_TUNNEL,     nx_action_set_tunnel,   0, "set_tunnel")
-NXAST_ACTION(NXAST_SET_QUEUE,      nx_action_set_queue,    0, "set_queue")
-NXAST_ACTION(NXAST_POP_QUEUE,      nx_action_pop_queue,    0, "pop_queue")
-NXAST_ACTION(NXAST_REG_MOVE,       nx_action_reg_move,     0, "move")
-NXAST_ACTION(NXAST_REG_LOAD,       nx_action_reg_load,     0, "load")
-NXAST_ACTION(NXAST_NOTE,           nx_action_note,         1, "note")
-NXAST_ACTION(NXAST_SET_TUNNEL64,   nx_action_set_tunnel64, 0, "set_tunnel64")
-NXAST_ACTION(NXAST_MULTIPATH,      nx_action_multipath,    0, "multipath")
-NXAST_ACTION(NXAST_AUTOPATH,       nx_action_autopath,     0, "autopath")
-NXAST_ACTION(NXAST_BUNDLE,         nx_action_bundle,       1, "bundle")
-NXAST_ACTION(NXAST_BUNDLE_LOAD,    nx_action_bundle,       1, "bundle_load")
-NXAST_ACTION(NXAST_RESUBMIT_TABLE, nx_action_resubmit,     0, NULL)
-NXAST_ACTION(NXAST_OUTPUT_REG,     nx_action_output_reg,   0, NULL)
-NXAST_ACTION(NXAST_LEARN,          nx_action_learn,        1, "learn")
-NXAST_ACTION(NXAST_EXIT,           nx_action_header,       0, "exit")
-NXAST_ACTION(NXAST_DEC_TTL,        nx_action_header,       0, "dec_ttl")
-NXAST_ACTION(NXAST_FIN_TIMEOUT,    nx_action_fin_timeout,  0, "fin_timeout")
-NXAST_ACTION(NXAST_CONTROLLER,     nx_action_controller,   0, "controller")
+NXAST_ACTION(NXAST_RESUBMIT,        nx_action_resubmit,     0, "resubmit")
+NXAST_ACTION(NXAST_SET_TUNNEL,      nx_action_set_tunnel,   0, "set_tunnel")
+NXAST_ACTION(NXAST_SET_QUEUE,       nx_action_set_queue,    0, "set_queue")
+NXAST_ACTION(NXAST_POP_QUEUE,       nx_action_pop_queue,    0, "pop_queue")
+NXAST_ACTION(NXAST_REG_MOVE,        nx_action_reg_move,     0, "move")
+NXAST_ACTION(NXAST_REG_LOAD,        nx_action_reg_load,     0, "load")
+NXAST_ACTION(NXAST_NOTE,            nx_action_note,         1, "note")
+NXAST_ACTION(NXAST_SET_TUNNEL64,    nx_action_set_tunnel64, 0, "set_tunnel64")
+NXAST_ACTION(NXAST_MULTIPATH,       nx_action_multipath,    0, "multipath")
+NXAST_ACTION(NXAST_AUTOPATH__DEPRECATED,nx_action_autopath, 0, "autopath")
+NXAST_ACTION(NXAST_BUNDLE,          nx_action_bundle,       1, "bundle")
+NXAST_ACTION(NXAST_BUNDLE_LOAD,     nx_action_bundle,       1, "bundle_load")
+NXAST_ACTION(NXAST_RESUBMIT_TABLE,  nx_action_resubmit,     0, NULL)
+NXAST_ACTION(NXAST_OUTPUT_REG,      nx_action_output_reg,   0, NULL)
+NXAST_ACTION(NXAST_LEARN,           nx_action_learn,        1, "learn")
+NXAST_ACTION(NXAST_EXIT,            nx_action_header,       0, "exit")
+NXAST_ACTION(NXAST_DEC_TTL,         nx_action_header,       0, "dec_ttl")
+NXAST_ACTION(NXAST_FIN_TIMEOUT,     nx_action_fin_timeout,  0, "fin_timeout")
+NXAST_ACTION(NXAST_CONTROLLER,      nx_action_controller,   0, "controller")
+NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids,      1, NULL)
+
+#undef OFPAT10_ACTION
+#undef OFPAT11_ACTION
 #undef NXAST_ACTION
index fd76eac..9cc3028 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 #include <stddef.h>
 #include <stdint.h>
 #include "classifier.h"
+#include "compiler.h"
 #include "flow.h"
 #include "netdev.h"
 #include "openflow/nicira-ext.h"
 struct cls_rule;
 struct ofpbuf;
 
-/* Basic decoding and length validation of OpenFlow messages. */
-enum ofputil_msg_code {
-    OFPUTIL_MSG_INVALID,
-
-    /* OFPT_* messages. */
-    OFPUTIL_OFPT_HELLO,
-    OFPUTIL_OFPT_ERROR,
-    OFPUTIL_OFPT_ECHO_REQUEST,
-    OFPUTIL_OFPT_ECHO_REPLY,
-    OFPUTIL_OFPT_FEATURES_REQUEST,
-    OFPUTIL_OFPT_FEATURES_REPLY,
-    OFPUTIL_OFPT_GET_CONFIG_REQUEST,
-    OFPUTIL_OFPT_GET_CONFIG_REPLY,
-    OFPUTIL_OFPT_SET_CONFIG,
-    OFPUTIL_OFPT_PACKET_IN,
-    OFPUTIL_OFPT_FLOW_REMOVED,
-    OFPUTIL_OFPT_PORT_STATUS,
-    OFPUTIL_OFPT_PACKET_OUT,
-    OFPUTIL_OFPT_FLOW_MOD,
-    OFPUTIL_OFPT_PORT_MOD,
-    OFPUTIL_OFPT_BARRIER_REQUEST,
-    OFPUTIL_OFPT_BARRIER_REPLY,
-    OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST,
-    OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY,
-
-    /* OFPST_* stat requests. */
-    OFPUTIL_OFPST_DESC_REQUEST,
-    OFPUTIL_OFPST_FLOW_REQUEST,
-    OFPUTIL_OFPST_AGGREGATE_REQUEST,
-    OFPUTIL_OFPST_TABLE_REQUEST,
-    OFPUTIL_OFPST_PORT_REQUEST,
-    OFPUTIL_OFPST_QUEUE_REQUEST,
-
-    /* OFPST_* stat replies. */
-    OFPUTIL_OFPST_DESC_REPLY,
-    OFPUTIL_OFPST_FLOW_REPLY,
-    OFPUTIL_OFPST_QUEUE_REPLY,
-    OFPUTIL_OFPST_PORT_REPLY,
-    OFPUTIL_OFPST_TABLE_REPLY,
-    OFPUTIL_OFPST_AGGREGATE_REPLY,
-
-    /* NXT_* messages. */
-    OFPUTIL_NXT_ROLE_REQUEST,
-    OFPUTIL_NXT_ROLE_REPLY,
-    OFPUTIL_NXT_SET_FLOW_FORMAT,
-    OFPUTIL_NXT_FLOW_MOD_TABLE_ID,
-    OFPUTIL_NXT_FLOW_MOD,
-    OFPUTIL_NXT_FLOW_REMOVED,
-    OFPUTIL_NXT_SET_PACKET_IN_FORMAT,
-    OFPUTIL_NXT_PACKET_IN,
-    OFPUTIL_NXT_FLOW_AGE,
-    OFPUTIL_NXT_SET_ASYNC_CONFIG,
-    OFPUTIL_NXT_SET_CONTROLLER_ID,
-
-    /* NXST_* stat requests. */
-    OFPUTIL_NXST_FLOW_REQUEST,
-    OFPUTIL_NXST_AGGREGATE_REQUEST,
-
-    /* NXST_* stat replies. */
-    OFPUTIL_NXST_FLOW_REPLY,
-    OFPUTIL_NXST_AGGREGATE_REPLY
-};
-
-struct ofputil_msg_type;
-enum ofperr ofputil_decode_msg_type(const struct ofp_header *,
-                                    const struct ofputil_msg_type **);
-enum ofperr ofputil_decode_msg_type_partial(const struct ofp_header *,
-                                            size_t length,
-                                            const struct ofputil_msg_type **);
-enum ofputil_msg_code ofputil_msg_type_code(const struct ofputil_msg_type *);
-const char *ofputil_msg_type_name(const struct ofputil_msg_type *);
-
 /* 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);
@@ -110,8 +39,8 @@ enum ofperr ofputil_check_output_port(uint16_t ofp_port, int max_ports);
 bool ofputil_port_from_string(const char *, uint16_t *port);
 void ofputil_format_port(uint16_t port, struct ds *);
 
-/* Converting OFPFW_NW_SRC_MASK and OFPFW_NW_DST_MASK wildcard bit counts to
- * and from IP bitmasks. */
+/* Converting OFPFW10_NW_SRC_MASK and OFPFW10_NW_DST_MASK wildcard bit counts
+ * to and from IP bitmasks. */
 ovs_be32 ofputil_wcbits_to_netmask(int wcbits);
 int ofputil_netmask_to_wcbits(ovs_be32 netmask);
 
@@ -139,6 +68,9 @@ enum ofputil_protocol {
     OFPUTIL_P_NXM_TID  = 1 << 3, /* NXM + flow_mod_table_id extension. */
 #define OFPUTIL_P_NXM_ANY (OFPUTIL_P_NXM | OFPUTIL_P_NXM_TID)
 
+    /* OpenFlow 1.2 */
+    OFPUTIL_P_OF12      = 1 << 4, /* OpenFlow 1.2 flow format. */
+
     /* All protocols. */
 #define OFPUTIL_P_ANY (OFPUTIL_P_OF10_ANY | OFPUTIL_P_NXM_ANY)
 
@@ -150,8 +82,9 @@ enum ofputil_protocol {
 extern enum ofputil_protocol ofputil_flow_dump_protocols[];
 extern size_t ofputil_n_flow_dump_protocols;
 
-enum ofputil_protocol ofputil_protocol_from_ofp_version(int version);
-uint8_t ofputil_protocol_to_ofp_version(enum ofputil_protocol);
+enum ofputil_protocol
+ofputil_protocol_from_ofp_version(enum ofp_version version);
+enum ofp_version  ofputil_protocol_to_ofp_version(enum ofputil_protocol);
 
 bool ofputil_protocol_is_valid(enum ofputil_protocol);
 enum ofputil_protocol ofputil_protocol_set_tid(enum ofputil_protocol,
@@ -175,12 +108,25 @@ enum ofputil_protocol ofputil_nx_flow_format_to_protocol(enum nx_flow_format);
 bool ofputil_nx_flow_format_is_valid(enum nx_flow_format);
 const char *ofputil_nx_flow_format_to_string(enum nx_flow_format);
 
-/* Work with OpenFlow 1.0 ofp_match. */
-void ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *);
-void ofputil_cls_rule_from_match(const struct ofp_match *,
-                                 unsigned int priority, struct cls_rule *);
+/* Work with ofp10_match. */
+void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *);
+void ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *,
+                                       unsigned int priority,
+                                       struct cls_rule *);
 void ofputil_normalize_rule(struct cls_rule *);
-void ofputil_cls_rule_to_match(const struct cls_rule *, struct ofp_match *);
+void ofputil_normalize_rule_quiet(struct cls_rule *);
+void ofputil_cls_rule_to_ofp10_match(const struct cls_rule *,
+                                     struct ofp10_match *);
+
+/* Work with ofp11_match. */
+enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, unsigned int priority,
+                                     struct cls_rule *,
+                                     uint16_t *padded_match_len);
+enum ofperr ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *,
+                                              unsigned int priority,
+                                              struct cls_rule *);
+void ofputil_cls_rule_to_ofp11_match(const struct cls_rule *,
+                                     struct ofp11_match *);
 
 /* dl_type translation between OpenFlow and 'struct flow' format. */
 ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type);
@@ -195,11 +141,29 @@ struct ofpbuf *ofputil_make_set_packet_in_format(enum nx_packet_in_format);
 /* NXT_FLOW_MOD_TABLE_ID extension. */
 struct ofpbuf *ofputil_make_flow_mod_table_id(bool flow_mod_table_id);
 
-/* Protocol-independent flow_mod. */
+/* Protocol-independent flow_mod.
+ *
+ * The handling of cookies across multiple versions of OpenFlow is a bit
+ * confusing.  A full description of Open vSwitch's cookie handling is
+ * in the DESIGN file.  The following table shows the expected values of
+ * the cookie-related fields for the different flow_mod commands in
+ * OpenFlow 1.0 ("OF10") and NXM.  "<used>" and "-" indicate a value
+ * that may be populated and an ignored field, respectively.
+ *
+ *               cookie  cookie_mask  new_cookie
+ *               ======  ===========  ==========
+ * OF10 Add        -          0         <used>
+ * OF10 Modify     -          0         <used>
+ * OF10 Delete     -          0           -
+ * NXM Add         -          0         <used>
+ * NXM Modify    <used>     <used>      <used>
+ * NXM Delete    <used>     <used>        -
+ */
 struct ofputil_flow_mod {
     struct cls_rule cr;
-    ovs_be64 cookie;
-    ovs_be64 cookie_mask;
+    ovs_be64 cookie;         /* Cookie bits to match. */
+    ovs_be64 cookie_mask;    /* 1-bit in each 'cookie' bit to match. */
+    ovs_be64 new_cookie;     /* New cookie to install or -1. */
     uint8_t table_id;
     uint16_t command;
     uint16_t idle_timeout;
@@ -207,13 +171,14 @@ struct ofputil_flow_mod {
     uint32_t buffer_id;
     uint16_t out_port;
     uint16_t flags;
-    union ofp_action *actions;
-    size_t n_actions;
+    struct ofpact *ofpacts;     /* Series of "struct ofpact"s. */
+    size_t ofpacts_len;         /* Length of ofpacts, in bytes. */
 };
 
 enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
                                     const struct ofp_header *,
-                                    enum ofputil_protocol);
+                                    enum ofputil_protocol,
+                                    struct ofpbuf *ofpacts);
 struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
                                        enum ofputil_protocol);
 
@@ -250,13 +215,14 @@ struct ofputil_flow_stats {
     int hard_age;               /* Seconds since last change, -1 if unknown. */
     uint64_t packet_count;      /* Packet count, UINT64_MAX if unknown. */
     uint64_t byte_count;        /* Byte count, UINT64_MAX if unknown. */
-    union ofp_action *actions;
-    size_t n_actions;
+    struct ofpact *ofpacts;
+    size_t ofpacts_len;
 };
 
 int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *,
                                     struct ofpbuf *msg,
-                                    bool flow_age_extension);
+                                    bool flow_age_extension,
+                                    struct ofpbuf *ofpacts);
 void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *,
                                      struct list *replies);
 
@@ -269,7 +235,10 @@ struct ofputil_aggregate_stats {
 
 struct ofpbuf *ofputil_encode_aggregate_stats_reply(
     const struct ofputil_aggregate_stats *stats,
-    const struct ofp_stats_msg *request);
+    const struct ofp_header *request);
+enum ofperr ofputil_decode_aggregate_stats_reply(
+    struct ofputil_aggregate_stats *,
+    const struct ofp_header *reply);
 
 /* Flow removed message, independent of protocol. */
 struct ofputil_flow_removed {
@@ -279,6 +248,7 @@ struct ofputil_flow_removed {
     uint32_t duration_sec;
     uint32_t duration_nsec;
     uint16_t idle_timeout;
+    uint16_t hard_timeout;
     uint64_t packet_count;      /* Packet count, UINT64_MAX if unknown. */
     uint64_t byte_count;        /* Byte count, UINT64_MAX if unknown. */
 };
@@ -305,28 +275,34 @@ struct ofputil_packet_in {
     struct flow_metadata fmd;   /* Metadata at creation time. */
 };
 
-int ofputil_decode_packet_in(struct ofputil_packet_in *,
-                             const struct ofp_header *);
+enum ofperr ofputil_decode_packet_in(struct ofputil_packet_in *,
+                                     const struct ofp_header *);
 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);
 bool ofputil_packet_in_reason_from_string(const char *,
                                           enum ofp_packet_in_reason *);
 
-/* Abstract packet-out message. */
+/* Abstract packet-out message.
+ *
+ * ofputil_decode_packet_out() will ensure that 'in_port' is a physical port
+ * (OFPP_MAX or less) or one of OFPP_LOCAL, OFPP_NONE, or OFPP_CONTROLLER. */
 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 or OFPP_NONE. */
-    union ofp_action *actions;  /* Actions. */
-    size_t n_actions;           /* Number of elements in 'actions' array. */
+    uint16_t in_port;           /* Packet's input port. */
+    struct ofpact *ofpacts;     /* Actions. */
+    size_t ofpacts_len;         /* Size of ofpacts in bytes. */
 };
 
 enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *,
-                                      const struct ofp_packet_out *);
-struct ofpbuf *ofputil_encode_packet_out(const struct ofputil_packet_out *);
+                                      const struct ofp_header *,
+                                      struct ofpbuf *ofpacts);
+struct ofpbuf *ofputil_encode_packet_out(const struct ofputil_packet_out *,
+                                         enum ofputil_protocol protocol);
 
 enum ofputil_port_config {
     /* OpenFlow 1.0 and 1.1 share these values for these port config bits. */
@@ -375,19 +351,24 @@ struct ofputil_phy_port {
 };
 
 enum ofputil_capabilities {
-    /* OpenFlow 1.0 and 1.1 share these values for these capabilities. */
+    /* OpenFlow 1.0, 1.1 and 1.2 share these values for these capabilities. */
     OFPUTIL_C_FLOW_STATS     = 1 << 0,  /* Flow statistics. */
     OFPUTIL_C_TABLE_STATS    = 1 << 1,  /* Table statistics. */
     OFPUTIL_C_PORT_STATS     = 1 << 2,  /* Port statistics. */
     OFPUTIL_C_IP_REASM       = 1 << 5,  /* Can reassemble IP fragments. */
     OFPUTIL_C_QUEUE_STATS    = 1 << 6,  /* Queue statistics. */
+
+    /* OpenFlow 1.0 and 1.1 share this capability. */
     OFPUTIL_C_ARP_MATCH_IP   = 1 << 7,  /* Match IP addresses in ARP pkts. */
 
     /* OpenFlow 1.0 only. */
     OFPUTIL_C_STP            = 1 << 3,  /* 802.1d spanning tree. */
 
-    /* OpenFlow 1.1 only. */
+    /* OpenFlow 1.1 and 1.2 share this capability. */
     OFPUTIL_C_GROUP_STATS    = 1 << 4,  /* Group statistics. */
+
+    /* OpenFlow 1.2 only */
+    OFPUTIL_C_PORT_BLOCKED   = 1 << 8,  /* Switch will block looping ports */
 };
 
 enum ofputil_action_bitmap {
@@ -429,18 +410,21 @@ struct ofputil_switch_features {
     enum ofputil_action_bitmap actions;
 };
 
-enum ofperr ofputil_decode_switch_features(const struct ofp_switch_features *,
+enum ofperr ofputil_decode_switch_features(const struct ofp_header *,
                                            struct ofputil_switch_features *,
                                            struct ofpbuf *);
-int ofputil_pull_switch_features_port(struct ofpbuf *,
-                                      struct ofputil_phy_port *);
-size_t ofputil_count_phy_ports(const struct ofp_switch_features *);
 
 struct ofpbuf *ofputil_encode_switch_features(
     const struct ofputil_switch_features *, enum ofputil_protocol,
     ovs_be32 xid);
 void ofputil_put_switch_features_port(const struct ofputil_phy_port *,
                                       struct ofpbuf *);
+bool ofputil_switch_features_ports_trunc(struct ofpbuf *b);
+
+/* phy_port helper functions. */
+int ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *,
+                          struct ofputil_phy_port *);
+size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *);
 
 /* Abstract ofp_port_status. */
 struct ofputil_port_status {
@@ -448,7 +432,7 @@ struct ofputil_port_status {
     struct ofputil_phy_port desc;
 };
 
-enum ofperr ofputil_decode_port_status(const struct ofp_port_status *,
+enum ofperr ofputil_decode_port_status(const struct ofp_header *,
                                        struct ofputil_port_status *);
 struct ofpbuf *ofputil_encode_port_status(const struct ofputil_port_status *,
                                           enum ofputil_protocol);
@@ -467,60 +451,62 @@ enum ofperr ofputil_decode_port_mod(const struct ofp_header *,
 struct ofpbuf *ofputil_encode_port_mod(const struct ofputil_port_mod *,
                                        enum ofputil_protocol);
 
-/* OpenFlow protocol utility functions. */
-void *make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **);
-void *make_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf **);
-
-void *make_openflow_xid(size_t openflow_len, uint8_t type,
-                        ovs_be32 xid, struct ofpbuf **);
-void *make_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
-                     struct ofpbuf **);
-
-void *put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *);
-void *put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
-                       struct ofpbuf *);
-
-void *put_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf *);
-void *put_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
-                    struct ofpbuf *);
-
-void update_openflow_length(struct ofpbuf *);
-
-void *ofputil_make_stats_request(size_t openflow_len, uint16_t type,
-                                 uint32_t subtype, struct ofpbuf **);
-void *ofputil_make_stats_reply(size_t openflow_len,
-                               const struct ofp_stats_msg *request,
-                               struct ofpbuf **);
-
-void ofputil_start_stats_reply(const struct ofp_stats_msg *request,
-                               struct list *);
-struct ofpbuf *ofputil_reserve_stats_reply(size_t len, struct list *);
-void *ofputil_append_stats_reply(size_t len, struct list *);
-
-const void *ofputil_stats_body(const struct ofp_header *);
-size_t ofputil_stats_body_len(const struct ofp_header *);
-
-const void *ofputil_nxstats_body(const struct ofp_header *);
-size_t ofputil_nxstats_body_len(const struct ofp_header *);
-
-struct ofpbuf *make_flow_mod(uint16_t command, const struct cls_rule *,
-                             size_t actions_len);
-struct ofpbuf *make_add_flow(const struct cls_rule *, uint32_t buffer_id,
-                             uint16_t max_idle, size_t actions_len);
-struct ofpbuf *make_del_flow(const struct cls_rule *);
-struct ofpbuf *make_add_simple_flow(const struct cls_rule *,
-                                    uint32_t buffer_id, uint16_t out_port,
-                                    uint16_t max_idle);
-struct ofpbuf *make_packet_in(uint32_t buffer_id, uint16_t in_port,
-                              uint8_t reason,
-                              const struct ofpbuf *payload, int max_send_len);
-struct ofpbuf *make_echo_request(void);
+/* Abstract nx_flow_monitor_request. */
+struct ofputil_flow_monitor_request {
+    uint32_t id;
+    enum nx_flow_monitor_flags flags;
+    uint16_t out_port;
+    uint8_t table_id;
+    struct cls_rule match;
+};
+
+int ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *,
+                                        struct ofpbuf *msg);
+void ofputil_append_flow_monitor_request(
+    const struct ofputil_flow_monitor_request *, struct ofpbuf *msg);
+
+/* Abstract nx_flow_update. */
+struct ofputil_flow_update {
+    enum nx_flow_update_event event;
+
+    /* Used only for NXFME_ADDED, NXFME_DELETED, NXFME_MODIFIED. */
+    enum ofp_flow_removed_reason reason;
+    uint16_t idle_timeout;
+    uint16_t hard_timeout;
+    uint8_t table_id;
+    ovs_be64 cookie;
+    struct cls_rule *match;
+    struct ofpact *ofpacts;
+    size_t ofpacts_len;
+
+    /* Used only for NXFME_ABBREV. */
+    ovs_be32 xid;
+};
+
+int ofputil_decode_flow_update(struct ofputil_flow_update *,
+                               struct ofpbuf *msg, struct ofpbuf *ofpacts);
+void ofputil_start_flow_update(struct list *replies);
+void ofputil_append_flow_update(const struct ofputil_flow_update *,
+                                struct list *replies);
+
+/* Abstract nx_flow_monitor_cancel. */
+uint32_t ofputil_decode_flow_monitor_cancel(const struct ofp_header *);
+struct ofpbuf *ofputil_encode_flow_monitor_cancel(uint32_t id);
+
+/* Encoding OpenFlow stats messages. */
+void ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version,
+                                          const struct ofputil_phy_port *pp,
+                                          struct list *replies);
+
+/* Encoding simple OpenFlow messages. */
+struct ofpbuf *make_echo_request(enum ofp_version);
 struct ofpbuf *make_echo_reply(const struct ofp_header *rq);
 
-struct ofpbuf *ofputil_encode_barrier_request(void);
+struct ofpbuf *ofputil_encode_barrier_request(enum ofp_version);
 
 const char *ofputil_frag_handling_to_string(enum ofp_config_flags);
 bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *);
+
 \f
 /* Actions. */
 
@@ -561,24 +547,23 @@ bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *);
  *
  * (The above list helps developers who want to "grep" for these definitions.)
  */
-enum ofputil_action_code {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME)             OFPUTIL_##ENUM,
+enum OVS_PACKED_ENUM ofputil_action_code {
+    OFPUTIL_ACTION_INVALID,
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME)           OFPUTIL_##ENUM,
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME)           OFPUTIL_##ENUM,
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM,
 #include "ofp-util.def"
 };
 
 /* The number of values of "enum ofputil_action_code". */
 enum {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME)             + 1
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME)           + 1
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME)           + 1
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) + 1
-    OFPUTIL_N_ACTIONS = 0
+    OFPUTIL_N_ACTIONS = 1
 #include "ofp-util.def"
 };
 
-int ofputil_decode_action(const union ofp_action *);
-enum ofputil_action_code ofputil_decode_action_unsafe(
-    const union ofp_action *);
-
 int ofputil_action_code_from_name(const char *);
 
 void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
@@ -600,6 +585,9 @@ void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)              \
     void ofputil_init_##ENUM(struct STRUCT *);          \
     struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME)              \
+    void ofputil_init_##ENUM(struct STRUCT *);          \
+    struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)    \
     void ofputil_init_##ENUM(struct STRUCT *);          \
     struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
@@ -607,38 +595,6 @@ void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
 
 #define OFP_ACTION_ALIGN 8      /* Alignment of ofp_actions. */
 
-static inline union ofp_action *
-ofputil_action_next(const union ofp_action *a)
-{
-    return ((union ofp_action *) (void *)
-            ((uint8_t *) a + ntohs(a->header.len)));
-}
-
-static inline bool
-ofputil_action_is_valid(const union ofp_action *a, size_t n_actions)
-{
-    uint16_t len = ntohs(a->header.len);
-    return (!(len % OFP_ACTION_ALIGN)
-            && len >= sizeof *a
-            && len / sizeof *a <= n_actions);
-}
-
-/* This macro is careful to check for actions with bad lengths. */
-#define OFPUTIL_ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS)         \
-    for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS);                      \
-         (LEFT) > 0 && ofputil_action_is_valid(ITER, LEFT);             \
-         ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
-          (ITER) = ofputil_action_next(ITER)))
-
-/* This macro does not check for actions with bad lengths.  It should only be
- * used with actions from trusted sources or with actions that have already
- * been validated (e.g. with OFPUTIL_ACTION_FOR_EACH).  */
-#define OFPUTIL_ACTION_FOR_EACH_UNSAFE(ITER, LEFT, ACTIONS, N_ACTIONS)  \
-    for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS);                      \
-         (LEFT) > 0;                                                    \
-         ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
-          (ITER) = ofputil_action_next(ITER)))
-
 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);
index a982420..a7d4c73 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -95,7 +95,7 @@ ofpbuf_use_stub(struct ofpbuf *b, void *base, size_t allocated)
 void
 ofpbuf_use_const(struct ofpbuf *b, const void *data, size_t size)
 {
-    ofpbuf_use__(b, (void *) data, size, OFPBUF_STACK);
+    ofpbuf_use__(b, CONST_CAST(void *, data), size, OFPBUF_STACK);
     b->size = size;
 }
 
@@ -408,7 +408,7 @@ ofpbuf_put_hex(struct ofpbuf *b, const char *s, size_t *n)
             if (n) {
                 *n = b->size - initial_size;
             }
-            return (char *) s;
+            return CONST_CAST(char *, s);
         }
 
         ofpbuf_put(b, &byte, 1);
index 37f4453..520455d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index d978718..e357233 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
 #include "ovsdb-parser.h"
 #include "json.h"
 #include "shash.h"
+#include "smap.h"
 #include "sort.h"
 #include "unicode.h"
 
@@ -883,9 +884,10 @@ ovsdb_datum_default(const struct ovsdb_type *type)
         d = &default_data[kt][vt];
         if (!d->n) {
             d->n = 1;
-            d->keys = (union ovsdb_atom *) ovsdb_atom_default(kt);
+            d->keys = CONST_CAST(union ovsdb_atom *, ovsdb_atom_default(kt));
             if (vt != OVSDB_TYPE_VOID) {
-                d->values = (union ovsdb_atom *) ovsdb_atom_default(vt);
+                d->values = CONST_CAST(union ovsdb_atom *,
+                                       ovsdb_atom_default(vt));
             }
         }
         return d;
@@ -1524,27 +1526,26 @@ ovsdb_datum_to_bare(const struct ovsdb_datum *datum,
 }
 
 /* Initializes 'datum' as a string-to-string map whose contents are taken from
- * 'sh'.  Destroys 'sh'. */
+ * 'smap'.  Destroys 'smap'. */
 void
-ovsdb_datum_from_shash(struct ovsdb_datum *datum, struct shash *sh)
+ovsdb_datum_from_smap(struct ovsdb_datum *datum, struct smap *smap)
 {
-    struct shash_node *node, *next;
+    struct smap_node *node, *next;
     size_t i;
 
-    datum->n = shash_count(sh);
+    datum->n = smap_count(smap);
     datum->keys = xmalloc(datum->n * sizeof *datum->keys);
     datum->values = xmalloc(datum->n * sizeof *datum->values);
 
     i = 0;
-    SHASH_FOR_EACH_SAFE (node, next, sh) {
-        datum->keys[i].string = node->name;
-        datum->values[i].string = node->data;
-        shash_steal(sh, node);
+    SMAP_FOR_EACH_SAFE (node, next, smap) {
+        smap_steal(smap, node,
+                   &datum->keys[i].string, &datum->values[i].string);
         i++;
     }
     assert(i == datum->n);
 
-    shash_destroy(sh);
+    smap_destroy(smap);
     ovsdb_datum_sort_unique(datum, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
 }
 
index 181df3b..2e31cc5 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
 
 struct ds;
 struct ovsdb_symbol_table;
+struct smap;
 
 /* One value of an atomic type (given by enum ovs_atomic_type). */
 union ovsdb_atom {
@@ -172,7 +173,7 @@ void ovsdb_datum_to_string(const struct ovsdb_datum *,
 void ovsdb_datum_to_bare(const struct ovsdb_datum *,
                          const struct ovsdb_type *, struct ds *);
 
-void ovsdb_datum_from_shash(struct ovsdb_datum *, struct shash *);
+void ovsdb_datum_from_smap(struct ovsdb_datum *, struct smap *);
 
 /* Comparison. */
 uint32_t ovsdb_datum_hash(const struct ovsdb_datum *,
index c5a4ad4..eb9f1ee 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 89b0c19..5f39c3e 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index ef37d92..546acbb 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks.
+/* Copyright (c) 2009, 2010, 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.
@@ -51,6 +51,7 @@ struct ovsdb_idl_table_class {
     const struct ovsdb_idl_column *columns;
     size_t n_columns;
     size_t allocation_size;
+    void (*row_init)(struct ovsdb_idl_row *);
 };
 
 struct ovsdb_idl_table {
index bb7da66..6118852 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -230,6 +230,7 @@ ovsdb_idl_destroy(struct ovsdb_idl *idl)
         json_destroy(idl->monitor_request_id);
         free(idl->lock_name);
         json_destroy(idl->lock_request_id);
+        hmap_destroy(&idl->outstanding_txns);
         free(idl);
     }
 }
@@ -906,6 +907,7 @@ static struct ovsdb_idl_row *
 ovsdb_idl_row_create__(const struct ovsdb_idl_table_class *class)
 {
     struct ovsdb_idl_row *row = xzalloc(class->allocation_size);
+    class->row_init(row);
     list_init(&row->src_arcs);
     list_init(&row->dst_arcs);
     hmap_node_nullify(&row->txn_node);
@@ -1819,7 +1821,7 @@ ovsdb_idl_txn_write(const struct ovsdb_idl_row *row_,
                     const struct ovsdb_idl_column *column,
                     struct ovsdb_datum *datum)
 {
-    struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_;
+    struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
     const struct ovsdb_idl_table_class *class;
     size_t column_idx;
 
@@ -1906,7 +1908,7 @@ void
 ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_,
                      const struct ovsdb_idl_column *column)
 {
-    struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_;
+    struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
     const struct ovsdb_idl_table_class *class;
     size_t column_idx;
 
@@ -1945,7 +1947,7 @@ ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_,
 void
 ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_)
 {
-    struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_;
+    struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
 
     if (ovsdb_idl_row_is_synthetic(row)) {
         return;
index 33cd340..c48ad1b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index c46c237..77460a8 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2011 Nicira Networks
+/* Copyright (c) 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.
index 44586aa..75bf6e4 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 959f087..b47bfad 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index a7b4e20..efd83a7 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index cd9227b..16f4fe6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
 #include "byte-order.h"
 #include "csum.h"
 #include "flow.h"
+#include "hmap.h"
 #include "dynamic-string.h"
 #include "ofpbuf.h"
 
@@ -43,6 +44,83 @@ dpid_from_string(const char *s, uint64_t *dpidp)
     return *dpidp != 0;
 }
 
+/* Returns true if 'ea' is a reserved address, that a bridge must never
+ * forward, false otherwise.
+ *
+ * If you change this function's behavior, please update corresponding
+ * documentation in vswitch.xml at the same time. */
+bool
+eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN])
+{
+    struct eth_addr_node {
+        struct hmap_node hmap_node;
+        uint64_t ea64;
+    };
+
+    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 },
+
+        /* Extreme protocols. */
+        { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000000ULL }, /* EDP. */
+        { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000004ULL }, /* EAPS. */
+        { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000006ULL }, /* EAPS. */
+
+        /* Cisco protocols. */
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000c000000ULL }, /* ISL. */
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccccULL }, /* PAgP, UDLD, CDP,
+                                                            * DTP, VTP. */
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000ccccccdULL }, /* PVST+. */
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000ccdcdcdULL }, /* STP Uplink Fast,
+                                                            * FlexLink. */
+
+        /* Cisco CFM. */
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc0ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc1ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc2ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc3ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc4ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc5ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc6ULL },
+        { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc7ULL },
+    };
+
+    static struct hmap addrs = HMAP_INITIALIZER(&addrs);
+    struct eth_addr_node *node;
+    uint64_t ea64;
+
+    if (hmap_is_empty(&addrs)) {
+        for (node = nodes; node < &nodes[ARRAY_SIZE(nodes)]; node++) {
+            hmap_insert(&addrs, &node->hmap_node,
+                        hash_2words(node->ea64, node->ea64 >> 32));
+        }
+    }
+
+    ea64 = eth_addr_to_uint64(ea);
+    HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash_2words(ea64, ea64 >> 32),
+                             &addrs) {
+        if (node->ea64 == ea64) {
+            return true;
+        }
+    }
+    return false;
+}
+
 bool
 eth_addr_from_string(const char *s, uint8_t ea[ETH_ADDR_LEN])
 {
@@ -55,27 +133,37 @@ eth_addr_from_string(const char *s, uint8_t ea[ETH_ADDR_LEN])
     }
 }
 
-/* Fills 'b' with an 802.2 SNAP packet with Ethernet source address 'eth_src',
- * the Nicira OUI as SNAP organization and 'snap_type' as SNAP type.  The text
- * string in 'tag' is enclosed as the packet payload.
- *
+/* Fills 'b' with a Reverse ARP packet with Ethernet source address 'eth_src'.
  * This function is used by Open vSwitch to compose packets in cases where
- * context is important but content doesn't (or shouldn't) matter.  For this
- * purpose, 'snap_type' should be a random number and 'tag' should be an
- * English phrase that explains the purpose of the packet.  (The English phrase
- * gives hapless admins running Wireshark the opportunity to figure out what's
- * going on.) */
+ * context is important but content doesn't (or shouldn't) matter.
+ *
+ * The returned packet has enough headroom to insert an 802.1Q VLAN header if
+ * desired. */
 void
-compose_benign_packet(struct ofpbuf *b, const char *tag, uint16_t snap_type,
-                      const uint8_t eth_src[ETH_ADDR_LEN])
+compose_rarp(struct ofpbuf *b, const uint8_t eth_src[ETH_ADDR_LEN])
 {
-    size_t tag_size = strlen(tag) + 1;
-    char *payload;
+    struct eth_header *eth;
+    struct arp_eth_header *arp;
 
-    payload = snap_compose(b, eth_addr_broadcast, eth_src, 0x002320, snap_type,
-                           tag_size + ETH_ADDR_LEN);
-    memcpy(payload, tag, tag_size);
-    memcpy(payload + tag_size, eth_src, ETH_ADDR_LEN);
+    ofpbuf_clear(b);
+    ofpbuf_prealloc_tailroom(b, ETH_HEADER_LEN + VLAN_HEADER_LEN
+                             + ARP_ETH_HEADER_LEN);
+    ofpbuf_reserve(b, VLAN_HEADER_LEN);
+    eth = ofpbuf_put_uninit(b, sizeof *eth);
+    memcpy(eth->eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
+    memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
+    eth->eth_type = htons(ETH_TYPE_RARP);
+
+    arp = ofpbuf_put_uninit(b, sizeof *arp);
+    arp->ar_hrd = htons(ARP_HRD_ETHERNET);
+    arp->ar_pro = htons(ARP_PRO_IP);
+    arp->ar_hln = sizeof arp->ar_sha;
+    arp->ar_pln = sizeof arp->ar_spa;
+    arp->ar_op = htons(ARP_OP_RARP);
+    memcpy(arp->ar_sha, eth_src, ETH_ADDR_LEN);
+    arp->ar_spa = htonl(0);
+    memcpy(arp->ar_tha, eth_src, ETH_ADDR_LEN);
+    arp->ar_tpa = htonl(0);
 }
 
 /* Insert VLAN header according to given TCI. Packet passed must be Ethernet
@@ -148,13 +236,36 @@ eth_from_hex(const char *hex, struct ofpbuf **packetp)
     return NULL;
 }
 
+void
+eth_format_masked(const uint8_t eth[ETH_ADDR_LEN],
+                  const uint8_t mask[ETH_ADDR_LEN], struct ds *s)
+{
+    ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth));
+    if (mask && !eth_mask_is_exact(mask)) {
+        ds_put_format(s, "/"ETH_ADDR_FMT, ETH_ADDR_ARGS(mask));
+    }
+}
+
+void
+eth_addr_bitand(const uint8_t src[ETH_ADDR_LEN],
+                const uint8_t mask[ETH_ADDR_LEN],
+                uint8_t dst[ETH_ADDR_LEN])
+{
+    int i;
+
+    for (i = 0; i < ETH_ADDR_LEN; i++) {
+        dst[i] = src[i] & mask[i];
+    }
+}
+
 /* Given the IP netmask 'netmask', returns the number of bits of the IP address
- * that it specifies, that is, the number of 1-bits in 'netmask'.  'netmask'
- * must be a CIDR netmask (see ip_is_cidr()). */
+ * that it specifies, that is, the number of 1-bits in 'netmask'.
+ *
+ * If 'netmask' is not a CIDR netmask (see ip_is_cidr()), the return value will
+ * still be in the valid range but isn't otherwise meaningful. */
 int
 ip_count_cidr_bits(ovs_be32 netmask)
 {
-    assert(ip_is_cidr(netmask));
     return 32 - ctz(ntohl(netmask));
 }
 
@@ -252,7 +363,10 @@ ipv6_create_mask(int mask)
 
 /* Given the IPv6 netmask 'netmask', returns the number of bits of the IPv6
  * address that it specifies, that is, the number of 1-bits in 'netmask'.
- * 'netmask' must be a CIDR netmask (see ipv6_is_cidr()). */
+ * 'netmask' must be a CIDR netmask (see ipv6_is_cidr()).
+ *
+ * If 'netmask' is not a CIDR netmask (see ipv6_is_cidr()), the return value
+ * will still be in the valid range but isn't otherwise meaningful. */
 int
 ipv6_count_cidr_bits(const struct in6_addr *netmask)
 {
@@ -260,8 +374,6 @@ ipv6_count_cidr_bits(const struct in6_addr *netmask)
     int count = 0;
     const uint8_t *netmaskp = &netmask->s6_addr[0];
 
-    assert(ipv6_is_cidr(netmask));
-
     for (i=0; i<16; i++) {
         if (netmaskp[i] == 0xff) {
             count += 8;
@@ -337,49 +449,6 @@ eth_compose(struct ofpbuf *b, const uint8_t eth_dst[ETH_ADDR_LEN],
     return data;
 }
 
-/* Populates 'b' with an Ethernet LLC+SNAP packet headed with the given
- * 'eth_dst', 'eth_src', 'snap_org', and 'snap_type'.  A payload of 'size'
- * bytes is allocated in 'b' and returned.  This payload may be populated with
- * appropriate information by the caller.
- *
- * The returned packet has enough headroom to insert an 802.1Q VLAN header if
- * desired. */
-void *
-snap_compose(struct ofpbuf *b, const uint8_t eth_dst[ETH_ADDR_LEN],
-             const uint8_t eth_src[ETH_ADDR_LEN],
-             unsigned int oui, uint16_t snap_type, size_t size)
-{
-    struct eth_header *eth;
-    struct llc_snap_header *llc_snap;
-    void *payload;
-
-    /* Compose basic packet structure.  (We need the payload size to stick into
-     * the 802.2 header.) */
-    ofpbuf_clear(b);
-    ofpbuf_prealloc_tailroom(b, ETH_HEADER_LEN + VLAN_HEADER_LEN
-                             + LLC_SNAP_HEADER_LEN + size);
-    ofpbuf_reserve(b, VLAN_HEADER_LEN);
-    eth = ofpbuf_put_zeros(b, ETH_HEADER_LEN);
-    llc_snap = ofpbuf_put_zeros(b, LLC_SNAP_HEADER_LEN);
-    payload = ofpbuf_put_uninit(b, size);
-
-    /* Compose 802.2 header. */
-    memcpy(eth->eth_dst, eth_dst, ETH_ADDR_LEN);
-    memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
-    eth->eth_type = htons(b->size - ETH_HEADER_LEN);
-
-    /* Compose LLC, SNAP headers. */
-    llc_snap->llc.llc_dsap = LLC_DSAP_SNAP;
-    llc_snap->llc.llc_ssap = LLC_SSAP_SNAP;
-    llc_snap->llc.llc_cntl = LLC_CNTL_SNAP;
-    llc_snap->snap.snap_org[0] = oui >> 16;
-    llc_snap->snap.snap_org[1] = oui >> 8;
-    llc_snap->snap.snap_org[2] = oui;
-    llc_snap->snap.snap_type = htons(snap_type);
-
-    return payload;
-}
-
 static void
 packet_set_ipv4_addr(struct ofpbuf *packet, ovs_be32 *addr, ovs_be32 new_addr)
 {
index dc71b05..e5be1cb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -64,6 +64,12 @@ static inline bool eth_addr_is_zero(const uint8_t ea[6])
 {
     return !(ea[0] | ea[1] | ea[2] | ea[3] | ea[4] | ea[5]);
 }
+
+static inline int eth_mask_is_exact(const uint8_t ea[ETH_ADDR_LEN])
+{
+    return (ea[0] & ea[1] & ea[2] & ea[3] & ea[4] & ea[5]) == 0xff;
+}
+
 static inline int eth_addr_compare_3way(const uint8_t a[ETH_ADDR_LEN],
                                         const uint8_t b[ETH_ADDR_LEN])
 {
@@ -74,6 +80,17 @@ static inline bool eth_addr_equals(const uint8_t a[ETH_ADDR_LEN],
 {
     return !eth_addr_compare_3way(a, b);
 }
+static inline bool eth_addr_equal_except(const uint8_t a[ETH_ADDR_LEN],
+                                    const uint8_t b[ETH_ADDR_LEN],
+                                    const uint8_t mask[ETH_ADDR_LEN])
+{
+    return !(((a[0] ^ b[0]) & mask[0])
+             || ((a[1] ^ b[1]) & mask[1])
+             || ((a[2] ^ b[2]) & mask[2])
+             || ((a[3] ^ b[3]) & mask[3])
+             || ((a[4] ^ b[4]) & mask[4])
+             || ((a[5] ^ b[5]) & mask[5]));
+}
 static inline uint64_t eth_addr_to_uint64(const uint8_t ea[ETH_ADDR_LEN])
 {
     return (((uint64_t) ea[0] << 40)
@@ -114,28 +131,21 @@ static inline void eth_addr_nicira_random(uint8_t ea[ETH_ADDR_LEN])
     /* Set the top bit to indicate random Nicira address. */
     ea[3] |= 0x80;
 }
-/* Returns true if 'ea' is a reserved multicast address, that a bridge must
- * never forward, false otherwise. */
-static inline bool eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN])
-{
-    return (ea[0] == 0x01
-            && ea[1] == 0x80
-            && ea[2] == 0xc2
-            && ea[3] == 0x00
-            && ea[4] == 0x00
-            && (ea[5] & 0xf0) == 0x00);
-}
 
+bool eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN]);
 bool eth_addr_from_string(const char *, uint8_t ea[ETH_ADDR_LEN]);
 
-void compose_benign_packet(struct ofpbuf *, const char *tag,
-                           uint16_t snap_type,
-                           const uint8_t eth_src[ETH_ADDR_LEN]);
+void compose_rarp(struct ofpbuf *, const uint8_t eth_src[ETH_ADDR_LEN]);
 
 void eth_push_vlan(struct ofpbuf *, ovs_be16 tci);
 void eth_pop_vlan(struct ofpbuf *);
 
 const char *eth_from_hex(const char *hex, struct ofpbuf **packetp);
+void eth_format_masked(const uint8_t eth[ETH_ADDR_LEN],
+                       const uint8_t mask[ETH_ADDR_LEN], struct ds *s);
+void eth_addr_bitand(const uint8_t src[ETH_ADDR_LEN],
+                     const uint8_t mask[ETH_ADDR_LEN],
+                     uint8_t dst[ETH_ADDR_LEN]);
 
 /* Example:
  *
@@ -170,6 +180,9 @@ const char *eth_from_hex(const char *hex, struct ofpbuf **packetp);
 #define ETH_TYPE_VLAN          0x8100
 #define ETH_TYPE_IPV6          0x86dd
 #define ETH_TYPE_LACP          0x8809
+#define ETH_TYPE_RARP          0x8035
+#define ETH_TYPE_MPLS          0x8847
+#define ETH_TYPE_MPLS_MCAST    0x8848
 
 /* Minimum value for an Ethernet type.  Values below this are IEEE 802.2 frame
  * lengths. */
@@ -307,6 +320,10 @@ void ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *);
 #define IP_IHL(ip_ihl_ver) ((ip_ihl_ver) & 15)
 #define IP_IHL_VER(ihl, ver) (((ver) << 4) | (ihl))
 
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+
 /* TOS fields. */
 #define IP_ECN_MASK 0x03
 #define IP_DSCP_MASK 0xfc
@@ -391,6 +408,7 @@ BUILD_ASSERT_DECL(TCP_HEADER_LEN == sizeof(struct tcp_header));
 #define ARP_PRO_IP 0x0800
 #define ARP_OP_REQUEST 1
 #define ARP_OP_REPLY 2
+#define ARP_OP_RARP 3
 
 #define ARP_ETH_HEADER_LEN 28
 struct arp_eth_header {
index 9c95c40..aa63be3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
index 7ea0f23..43a4306 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 76fbc1d..516cf13 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -157,7 +157,7 @@ poll_immediate_wake(const char *where)
 static void
 log_wakeup(const char *where, const struct pollfd *pollfd, int timeout)
 {
-    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(120, 120);
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
     enum vlog_level level;
     int cpu_usage;
     struct ds s;
index da8f6e2..e77d027 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 3d6c11a..91dfc06 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -83,8 +83,8 @@ process_init(void)
 
     /* Create notification pipe. */
     xpipe(fds);
-    set_nonblocking(fds[0]);
-    set_nonblocking(fds[1]);
+    xset_nonblocking(fds[0]);
+    xset_nonblocking(fds[1]);
 
     /* Set up child termination signal handler. */
     memset(&sa, 0, sizeof sa);
index 9c4556b..f677725 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * 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.
index 911ebf3..3ea65d5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 5bb041e..eaac143 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 700de11..ddf578c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "coverage.h"
+#include "ofp-msgs.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
@@ -79,7 +80,6 @@ struct rconn {
     int backoff;
     int max_backoff;
     time_t backoff_deadline;
-    time_t last_received;
     time_t last_connected;
     time_t last_disconnected;
     unsigned int packets_sent;
@@ -104,11 +104,15 @@ struct rconn {
     time_t creation_time;
     unsigned long int total_time_connected;
 
-    /* Throughout this file, "probe" is shorthand for "inactivity probe".
-     * When nothing has been received from the peer for a while, we send out
-     * an echo request as an inactivity probe packet.  We should receive back
-     * a response. */
+    /* Throughout this file, "probe" is shorthand for "inactivity probe".  When
+     * no activity has been observed from the peer for a while, we send out an
+     * echo request as an inactivity probe packet.  We should receive back a
+     * response.
+     *
+     * "Activity" is defined as either receiving an OpenFlow message from the
+     * peer or successfully sending a message that had been in 'txq'. */
     int probe_interval;         /* Secs of inactivity before sending probe. */
+    time_t last_activity;       /* Last time we saw some activity. */
 
     /* When we create a vconn we obtain these values, to save them past the end
      * of the vconn's lifetime.  Otherwise, in-band control will only allow
@@ -178,7 +182,6 @@ rconn_create(int probe_interval, int max_backoff, uint8_t dscp)
     rc->backoff = 0;
     rc->max_backoff = max_backoff ? max_backoff : 8;
     rc->backoff_deadline = TIME_MIN;
-    rc->last_received = time_now();
     rc->last_connected = TIME_MIN;
     rc->last_disconnected = TIME_MIN;
     rc->seqno = 0;
@@ -194,6 +197,8 @@ rconn_create(int probe_interval, int max_backoff, uint8_t dscp)
     rc->creation_time = time_now();
     rc->total_time_connected = 0;
 
+    rc->last_activity = time_now();
+
     rconn_set_probe_interval(rc, probe_interval);
     rconn_set_dscp(rc, dscp);
 
@@ -226,6 +231,12 @@ rconn_set_dscp(struct rconn *rc, uint8_t dscp)
     rc->dscp = dscp;
 }
 
+uint8_t
+rconn_get_dscp(const struct rconn *rc)
+{
+    return rc->dscp;
+}
+
 void
 rconn_set_probe_interval(struct rconn *rc, int probe_interval)
 {
@@ -412,6 +423,7 @@ do_tx_work(struct rconn *rc)
         if (error) {
             break;
         }
+        rc->last_activity = time_now();
     }
     if (list_is_empty(&rc->txq)) {
         poll_immediate_wake();
@@ -422,7 +434,7 @@ static unsigned int
 timeout_ACTIVE(const struct rconn *rc)
 {
     if (rc->probe_interval) {
-        unsigned int base = MAX(rc->last_received, rc->state_entered);
+        unsigned int base = MAX(rc->last_activity, rc->state_entered);
         unsigned int arg = base + rc->probe_interval - rc->state_entered;
         return arg;
     }
@@ -433,15 +445,20 @@ static void
 run_ACTIVE(struct rconn *rc)
 {
     if (timed_out(rc)) {
-        unsigned int base = MAX(rc->last_received, rc->state_entered);
+        unsigned int base = MAX(rc->last_activity, rc->state_entered);
+        int version;
+
         VLOG_DBG("%s: idle %u seconds, sending inactivity probe",
                  rc->name, (unsigned int) (time_now() - base));
 
+        version = rconn_get_version(rc);
+        assert(version >= 0 && version <= 0xff);
+
         /* Ordering is important here: rconn_send() can transition to BACKOFF,
          * and we don't want to transition back to IDLE if so, because then we
          * can end up queuing a packet with vconn == NULL and then *boom*. */
         state_transition(rc, S_IDLE);
-        rconn_send(rc, make_echo_request(), NULL);
+        rconn_send(rc, make_echo_request(version), NULL);
         return;
     }
 
@@ -536,7 +553,7 @@ rconn_recv(struct rconn *rc)
                 rc->probably_admitted = true;
                 rc->last_admitted = time_now();
             }
-            rc->last_received = time_now();
+            rc->last_activity = time_now();
             rc->packets_received++;
             if (rc->state == S_IDLE) {
                 state_transition(rc, S_ACTIVE);
@@ -581,7 +598,7 @@ rconn_send(struct rconn *rc, struct ofpbuf *b,
         copy_to_monitor(rc, b);
         b->private_p = counter;
         if (counter) {
-            rconn_packet_counter_inc(counter);
+            rconn_packet_counter_inc(counter, b->size);
         }
         list_push_back(&rc->txq, &b->list_node);
 
@@ -616,7 +633,9 @@ rconn_send_with_limit(struct rconn *rc, struct ofpbuf *b,
                       struct rconn_packet_counter *counter, int queue_limit)
 {
     int retval;
-    retval = counter->n >= queue_limit ? EAGAIN : rconn_send(rc, b, counter);
+    retval = (counter->n_packets >= queue_limit
+              ? EAGAIN
+              : rconn_send(rc, b, counter));
     if (retval) {
         COVERAGE_INC(rconn_overflow);
     }
@@ -762,21 +781,6 @@ rconn_get_state(const struct rconn *rc)
     return state_name(rc->state);
 }
 
-/* Returns the number of connection attempts made by 'rc', including any
- * ongoing attempt that has not yet succeeded or failed. */
-unsigned int
-rconn_get_attempted_connections(const struct rconn *rc)
-{
-    return rc->n_attempted_connections;
-}
-
-/* Returns the number of successful connection attempts made by 'rc'. */
-unsigned int
-rconn_get_successful_connections(const struct rconn *rc)
-{
-    return rc->n_successful_connections;
-}
-
 /* Returns the time at which the last successful connection was made by
  * 'rc'. Returns TIME_MIN if never connected. */
 time_t
@@ -793,45 +797,6 @@ rconn_get_last_disconnect(const struct rconn *rc)
     return rc->last_disconnected;
 }
 
-/* Returns the time at which the last OpenFlow message was received by 'rc'.
- * If no packets have been received on 'rc', returns the time at which 'rc'
- * was created. */
-time_t
-rconn_get_last_received(const struct rconn *rc)
-{
-    return rc->last_received;
-}
-
-/* Returns the time at which 'rc' was created. */
-time_t
-rconn_get_creation_time(const struct rconn *rc)
-{
-    return rc->creation_time;
-}
-
-/* Returns the approximate number of seconds that 'rc' has been connected. */
-unsigned long int
-rconn_get_total_time_connected(const struct rconn *rc)
-{
-    return (rc->total_time_connected
-            + (rconn_is_connected(rc) ? elapsed_in_this_state(rc) : 0));
-}
-
-/* Returns the current amount of backoff, in seconds.  This is the amount of
- * time after which the rconn will transition from BACKOFF to CONNECTING. */
-int
-rconn_get_backoff(const struct rconn *rc)
-{
-    return rc->backoff;
-}
-
-/* Returns the number of seconds spent in this state so far. */
-unsigned int
-rconn_get_state_elapsed(const struct rconn *rc)
-{
-    return elapsed_in_this_state(rc);
-}
-
 /* Returns 'rc''s current connection sequence number, a number that changes
  * every time that 'rconn' connects or disconnects. */
 unsigned int
@@ -855,12 +820,18 @@ rconn_get_last_error(const struct rconn *rc)
 {
     return rc->last_error;
 }
+
+/* Returns the number of messages queued for transmission on 'rc'. */
+unsigned int
+rconn_count_txqlen(const struct rconn *rc)
+{
+    return list_size(&rc->txq);
+}
 \f
 struct rconn_packet_counter *
 rconn_packet_counter_create(void)
 {
-    struct rconn_packet_counter *c = xmalloc(sizeof *c);
-    c->n = 0;
+    struct rconn_packet_counter *c = xzalloc(sizeof *c);
     c->ref_cnt = 1;
     return c;
 }
@@ -870,24 +841,32 @@ rconn_packet_counter_destroy(struct rconn_packet_counter *c)
 {
     if (c) {
         assert(c->ref_cnt > 0);
-        if (!--c->ref_cnt && !c->n) {
+        if (!--c->ref_cnt && !c->n_packets) {
             free(c);
         }
     }
 }
 
 void
-rconn_packet_counter_inc(struct rconn_packet_counter *c)
+rconn_packet_counter_inc(struct rconn_packet_counter *c, unsigned int n_bytes)
 {
-    c->n++;
+    c->n_packets++;
+    c->n_bytes += n_bytes;
 }
 
 void
-rconn_packet_counter_dec(struct rconn_packet_counter *c)
+rconn_packet_counter_dec(struct rconn_packet_counter *c, unsigned int n_bytes)
 {
-    assert(c->n > 0);
-    if (!--c->n && !c->ref_cnt) {
-        free(c);
+    assert(c->n_packets > 0);
+    assert(c->n_bytes >= n_bytes);
+
+    c->n_bytes -= n_bytes;
+    c->n_packets--;
+    if (!c->n_packets) {
+        assert(!c->n_bytes);
+        if (!c->ref_cnt) {
+            free(c);
+        }
     }
 }
 \f
@@ -914,6 +893,7 @@ static int
 try_send(struct rconn *rc)
 {
     struct ofpbuf *msg = ofpbuf_from_list(rc->txq.next);
+    unsigned int n_bytes = msg->size;
     struct rconn_packet_counter *counter = msg->private_p;
     int retval;
 
@@ -934,7 +914,7 @@ try_send(struct rconn *rc)
     COVERAGE_INC(rconn_sent);
     rc->packets_sent++;
     if (counter) {
-        rconn_packet_counter_dec(counter);
+        rconn_packet_counter_dec(counter, n_bytes);
     }
     return 0;
 }
@@ -1014,7 +994,7 @@ flush_queue(struct rconn *rc)
         struct ofpbuf *b = ofpbuf_from_list(list_pop_front(&rc->txq));
         struct rconn_packet_counter *counter = b->private_p;
         if (counter) {
-            rconn_packet_counter_dec(counter);
+            rconn_packet_counter_dec(counter, b->size);
         }
         COVERAGE_INC(rconn_discarded);
         ofpbuf_delete(b);
@@ -1098,19 +1078,64 @@ is_connected_state(enum state state)
 static bool
 is_admitted_msg(const struct ofpbuf *b)
 {
-    struct ofp_header *oh = b->data;
-    uint8_t type = oh->type;
-    return !(type < 32
-             && (1u << type) & ((1u << OFPT_HELLO) |
-                                (1u << OFPT_ERROR) |
-                                (1u << OFPT_ECHO_REQUEST) |
-                                (1u << OFPT_ECHO_REPLY) |
-                                (1u << OFPT_VENDOR) |
-                                (1u << OFPT_FEATURES_REQUEST) |
-                                (1u << OFPT_FEATURES_REPLY) |
-                                (1u << OFPT_GET_CONFIG_REQUEST) |
-                                (1u << OFPT_GET_CONFIG_REPLY) |
-                                (1u << OFPT_SET_CONFIG)));
+    enum ofptype type;
+    enum ofperr error;
+
+    error = ofptype_decode(&type, b->data);
+    if (error) {
+        return false;
+    }
+
+    switch (type) {
+    case OFPTYPE_HELLO:
+    case OFPTYPE_ERROR:
+    case OFPTYPE_ECHO_REQUEST:
+    case OFPTYPE_ECHO_REPLY:
+    case OFPTYPE_FEATURES_REQUEST:
+    case OFPTYPE_FEATURES_REPLY:
+    case OFPTYPE_GET_CONFIG_REQUEST:
+    case OFPTYPE_GET_CONFIG_REPLY:
+    case OFPTYPE_SET_CONFIG:
+        return false;
+
+    case OFPTYPE_PACKET_IN:
+    case OFPTYPE_FLOW_REMOVED:
+    case OFPTYPE_PORT_STATUS:
+    case OFPTYPE_PACKET_OUT:
+    case OFPTYPE_FLOW_MOD:
+    case OFPTYPE_PORT_MOD:
+    case OFPTYPE_BARRIER_REQUEST:
+    case OFPTYPE_BARRIER_REPLY:
+    case OFPTYPE_DESC_STATS_REQUEST:
+    case OFPTYPE_DESC_STATS_REPLY:
+    case OFPTYPE_FLOW_STATS_REQUEST:
+    case OFPTYPE_FLOW_STATS_REPLY:
+    case OFPTYPE_AGGREGATE_STATS_REQUEST:
+    case OFPTYPE_AGGREGATE_STATS_REPLY:
+    case OFPTYPE_TABLE_STATS_REQUEST:
+    case OFPTYPE_TABLE_STATS_REPLY:
+    case OFPTYPE_PORT_STATS_REQUEST:
+    case OFPTYPE_PORT_STATS_REPLY:
+    case OFPTYPE_QUEUE_STATS_REQUEST:
+    case OFPTYPE_QUEUE_STATS_REPLY:
+    case OFPTYPE_PORT_DESC_STATS_REQUEST:
+    case OFPTYPE_PORT_DESC_STATS_REPLY:
+    case OFPTYPE_ROLE_REQUEST:
+    case OFPTYPE_ROLE_REPLY:
+    case OFPTYPE_SET_FLOW_FORMAT:
+    case OFPTYPE_FLOW_MOD_TABLE_ID:
+    case OFPTYPE_SET_PACKET_IN_FORMAT:
+    case OFPTYPE_FLOW_AGE:
+    case OFPTYPE_SET_ASYNC_CONFIG:
+    case OFPTYPE_SET_CONTROLLER_ID:
+    case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+    case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
+    case OFPTYPE_FLOW_MONITOR_CANCEL:
+    case OFPTYPE_FLOW_MONITOR_PAUSED:
+    case OFPTYPE_FLOW_MONITOR_RESUMED:
+    default:
+        return true;
+    }
 }
 
 /* Returns true if 'rc' is currently logging information about connection
index eea054f..d10cef7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@ struct rconn_packet_counter;
 struct rconn *rconn_create(int inactivity_probe_interval,
                           int max_backoff, uint8_t dscp);
 void rconn_set_dscp(struct rconn *rc, uint8_t dscp);
+uint8_t rconn_get_dscp(const struct rconn *rc);
 void rconn_set_max_backoff(struct rconn *, int max_backoff);
 int rconn_get_max_backoff(const struct rconn *);
 void rconn_set_probe_interval(struct rconn *, int inactivity_probe_interval);
@@ -80,33 +81,22 @@ ovs_be16 rconn_get_local_port(const struct rconn *);
 int rconn_get_version(const struct rconn *);
 
 const char *rconn_get_state(const struct rconn *);
-unsigned int rconn_get_attempted_connections(const struct rconn *);
-unsigned int rconn_get_successful_connections(const struct rconn *);
 time_t rconn_get_last_connection(const struct rconn *);
 time_t rconn_get_last_disconnect(const struct rconn *);
-time_t rconn_get_last_received(const struct rconn *);
-time_t rconn_get_creation_time(const struct rconn *);
-unsigned long int rconn_get_total_time_connected(const struct rconn *);
-int rconn_get_backoff(const struct rconn *);
-unsigned int rconn_get_state_elapsed(const struct rconn *);
 unsigned int rconn_get_connection_seqno(const struct rconn *);
 int rconn_get_last_error(const struct rconn *);
+unsigned int rconn_count_txqlen(const struct rconn *);
 
-/* Counts the number of packets queued into an rconn by a given source. */
+/* Counts packets and bytes queued into an rconn by a given source. */
 struct rconn_packet_counter {
-    int n;                      /* Number of packets queued. */
+    unsigned int n_packets;     /* Number of packets queued. */
+    unsigned int n_bytes;       /* Number of bytes queued. */
     int ref_cnt;                /* Number of owners. */
 };
 
 struct rconn_packet_counter *rconn_packet_counter_create(void);
 void rconn_packet_counter_destroy(struct rconn_packet_counter *);
-void rconn_packet_counter_inc(struct rconn_packet_counter *);
-void rconn_packet_counter_dec(struct rconn_packet_counter *);
-
-static inline int
-rconn_packet_counter_read(const struct rconn_packet_counter *counter)
-{
-    return counter->n;
-}
+void rconn_packet_counter_inc(struct rconn_packet_counter *, unsigned n_bytes);
+void rconn_packet_counter_dec(struct rconn_packet_counter *, unsigned n_bytes);
 
 #endif /* rconn.h */
index 78ebda5..0333d96 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * 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.
index 997a03f..e935d0a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
diff --git a/lib/route-table-bsd.c b/lib/route-table-bsd.c
new file mode 100644 (file)
index 0000000..eb4a168
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2012 Ed Maste. All rights reserved.
+ *
+ * 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 "route-table.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(route_table);
+
+static int pid;
+static unsigned int register_count = 0;
+
+bool
+route_table_get_name(ovs_be32 ip, char name[IFNAMSIZ])
+{
+    struct {
+        struct rt_msghdr rtm;
+        char space[512];
+    } rtmsg;
+
+    struct rt_msghdr *rtm = &rtmsg.rtm;
+    struct sockaddr_dl *ifp = NULL;
+    struct sockaddr_in *sin;
+    struct sockaddr *sa;
+    static int seq;
+    int i, len, namelen, rtsock;
+
+    rtsock = socket(PF_ROUTE, SOCK_RAW, 0);
+    if (rtsock < 0)
+        return false;
+
+    memset(&rtmsg, 0, sizeof(rtmsg));
+
+    rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
+    rtm->rtm_version = RTM_VERSION;
+    rtm->rtm_type = RTM_GET;
+    rtm->rtm_addrs = RTA_DST | RTA_IFP;
+    rtm->rtm_seq = ++seq;
+
+    sin = (struct sockaddr_in *)(rtm + 1);
+    sin->sin_len = len = sizeof(struct sockaddr_in);
+    sin->sin_family = AF_INET;
+    sin->sin_addr.s_addr = ip;
+
+    if ((write(rtsock, (char *)&rtmsg, rtm->rtm_msglen)) < 0) {
+        close(rtsock);
+        return false;
+    }
+
+    do {
+        len = read(rtsock, (char *)&rtmsg, sizeof(rtmsg));
+    } while (len > 0 && (rtmsg.rtm.rtm_seq != seq ||
+        rtmsg.rtm.rtm_pid != pid));
+
+    close(rtsock);
+
+    if (len < 0) {
+        return false;
+    }
+
+    sa = (struct sockaddr *)(rtm + 1);
+    for (i = 1; i; i <<= 1) {
+        if (rtm->rtm_addrs & i) {
+            if (i == RTA_IFP && sa->sa_family == AF_LINK &&
+              ((struct sockaddr_dl *)sa)->sdl_nlen) {
+                ifp = (struct sockaddr_dl *)sa;
+                namelen = ifp->sdl_nlen;
+                if (namelen > IFNAMSIZ - 1)
+                    namelen = IFNAMSIZ - 1;
+                memcpy(name, ifp->sdl_data, namelen);
+                name[namelen] = '\0';
+                return true;
+            }
+            sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa));
+        }
+    }
+    return false;
+}
+
+void
+route_table_register(void)
+{
+    if (!register_count)
+    {
+        pid = getpid();
+    }
+
+    register_count++;
+}
+
+void
+route_table_unregister(void)
+{
+    register_count--;
+}
+
+void
+route_table_run(void)
+{
+}
+
+void
+route_table_wait(void)
+{
+}
index 8e54b28..63a09f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 534350c..804cb3f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
@@ -17,6 +17,7 @@
 #ifndef ROUTE_TABLE_H
 #define ROUTE_TABLE_H 1
 
+#include <sys/socket.h>
 #include <net/if.h>
 #include <stdbool.h>
 #include <stdint.h>
diff --git a/lib/rtbsd.c b/lib/rtbsd.c
new file mode 100644 (file)
index 0000000..c5fe03a
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2011 Gaetano Catalli.
+ *
+ * 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 <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <poll.h>
+
+#include "coverage.h"
+#include "socket-util.h"
+#include "poll-loop.h"
+#include "vlog.h"
+#include "rtbsd.h"
+
+VLOG_DEFINE_THIS_MODULE(rtbsd);
+COVERAGE_DEFINE(rtbsd_changed);
+
+/* PF_ROUTE socket. */
+static int notify_sock = -1;
+
+/* All registered notifiers. */
+static struct list all_notifiers = LIST_INITIALIZER(&all_notifiers);
+
+static void rtbsd_report_change(const struct if_msghdr *);
+static void rtbsd_report_notify_error(void);
+
+/* Registers 'cb' to be called with auxiliary data 'aux' with network device
+ * change notifications.  The notifier is stored in 'notifier', which the
+ * caller must not modify or free.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+rtbsd_notifier_register(struct rtbsd_notifier *notifier,
+                            rtbsd_notify_func *cb, void *aux)
+{
+    if (notify_sock < 0) {
+        int error;
+        notify_sock = socket(PF_ROUTE, SOCK_RAW, 0);
+        if (notify_sock < 0) {
+            VLOG_WARN("could not create PF_ROUTE socket: %s",
+                      strerror(errno));
+            return errno;
+        }
+        error = set_nonblocking(notify_sock);
+        if (error) {
+            VLOG_WARN("error set_nonblocking PF_ROUTE socket: %s",
+                    strerror(error));
+            return error;
+        }
+    } else {
+        /* Catch up on notification work so that the new notifier won't
+         * receive any stale notifications. XXX*/
+        rtbsd_notifier_run();
+    }
+
+    list_push_back(&all_notifiers, &notifier->node);
+    notifier->cb = cb;
+    notifier->aux = aux;
+    return 0;
+}
+
+/* Cancels notification on 'notifier', which must have previously been
+ * registered with rtbsd_notifier_register(). */
+void
+rtbsd_notifier_unregister(struct rtbsd_notifier *notifier)
+{
+    list_remove(&notifier->node);
+    if (list_is_empty(&all_notifiers)) {
+        close(notify_sock);
+        notify_sock = -1;
+    }
+}
+
+/* Calls all of the registered notifiers, passing along any as-yet-unreported
+ * netdev change events. */
+void
+rtbsd_notifier_run(void)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    struct if_msghdr msg;
+    if (notify_sock < 0) {
+        return;
+    }
+
+    for (;;) {
+        int retval;
+
+        msg.ifm_type = RTM_IFINFO;
+        msg.ifm_version = RTM_VERSION; //XXX check if necessary
+
+        /* read from PF_ROUTE socket */
+        retval = read(notify_sock, (char *)&msg, sizeof(msg));
+        if (retval >= 0) {
+            /* received packet from PF_ROUTE socket
+             * XXX check for bad packets */
+            if (msg.ifm_type == RTM_IFINFO) {
+                rtbsd_report_change(&msg);
+            }
+        } else if (errno == EAGAIN) {
+            return;
+        } else {
+            if (errno == ENOBUFS) {
+                VLOG_WARN_RL(&rl, "PF_ROUTE receive buffer overflowed");
+            } else {
+                VLOG_WARN_RL(&rl, "error reading PF_ROUTE socket: %s",
+                             strerror(errno));
+            }
+            rtbsd_report_notify_error();
+        }
+    }
+}
+
+/* Causes poll_block() to wake up when network device change notifications are
+ * ready. */
+void
+rtbsd_notifier_wait(void)
+{
+    if (notify_sock >= 0) {
+        poll_fd_wait(notify_sock, POLLIN);
+    }
+}
+
+static void
+rtbsd_report_change(const struct if_msghdr *msg)
+{
+    struct rtbsd_notifier *notifier;
+    struct rtbsd_change change;
+
+    COVERAGE_INC(rtbsd_changed);
+
+    change.msg_type = msg->ifm_type; //XXX
+    change.if_index = msg->ifm_index;
+    if_indextoname(msg->ifm_index, change.if_name);
+    change.master_ifindex = 0; //XXX
+
+    LIST_FOR_EACH (notifier, node, &all_notifiers) {
+        notifier->cb(&change, notifier->aux);
+    }
+}
+
+/* If an error occurs the notifiers' callbacks are called with NULL changes */
+static void
+rtbsd_report_notify_error(void)
+{
+    struct rtbsd_notifier *notifier;
+
+    LIST_FOR_EACH (notifier, node, &all_notifiers) {
+        notifier->cb(NULL, notifier->aux);
+    }
+}
diff --git a/lib/rtbsd.h b/lib/rtbsd.h
new file mode 100644 (file)
index 0000000..60bfae9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Gaetano Catalli.
+ *
+ * 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 RTBSD_H
+#define RTBSD_H 1
+
+#include "list.h"
+
+/*
+ * A digested version of a message received from a PF_ROUTE socket which
+ * indicates that a network device has been created or destroyed or changed.
+ */
+struct rtbsd_change {
+    /* Copied from struct if_msghdr. */
+    int msg_type;             /* e.g. XXX. */
+
+    /* Copied from struct if_msghdr. */
+    int if_index;              /* Index of network device. */
+
+    char if_name[IF_NAMESIZE];         /* Name of network device. */
+    int master_ifindex;         /* Ifindex of datapath master (0 if none). */
+};
+
+/*
+ * Function called to report that a netdev has changed.  'change' describes the
+ * specific change.  It may be null if the buffer of change information
+ * overflowed, in which case the function must assume that every device may
+ * have changed.  'aux' is as specified in the call to
+ * rtbsd_notifier_register().
+ */
+typedef void rtbsd_notify_func(const struct rtbsd_change *, void *aux);
+
+struct rtbsd_notifier {
+    struct list node;
+    rtbsd_notify_func *cb;
+    void *aux;
+};
+
+int rtbsd_notifier_register(struct rtbsd_notifier *,
+                                rtbsd_notify_func *, void *aux);
+void rtbsd_notifier_unregister(struct rtbsd_notifier *);
+void rtbsd_notifier_run(void);
+void rtbsd_notifier_wait(void);
+
+#endif /* rtbsd.h */
index 07350a6..0b3710f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
index 7c64c94..d0d9a75 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index cae956b..f4287fc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Nicira Networks.
+ * Copyright (c) 2008, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,13 +34,15 @@ sat_sub(unsigned int x, unsigned int y)
     return x >= y ? x - y : 0;
 }
 
-/* Saturating multiplication: overflow yields UINT_MAX. */
+/* Saturating multiplication of "unsigned int"s: overflow yields UINT_MAX. */
+#define SAT_MUL(X, Y)                                                   \
+    ((Y) == 0 ? 0                                                       \
+     : (X) <= UINT_MAX / (Y) ? (unsigned int) (X) * (unsigned int) (Y)  \
+     : UINT_MAX)
 static inline unsigned int
 sat_mul(unsigned int x, unsigned int y)
 {
-    return (!y ? 0
-            : x <= UINT_MAX / y ? x * y
-            : UINT_MAX);
+    return SAT_MUL(x, y);
 }
 
 #endif /* sat-math.h */
index 6205e50..4f48ef2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is from the Apache Portable Runtime Library.
  * The full upstream copyright and license statement is included below.
- * Modifications copyright (c) 2009, 2010 Nicira Networks.
+ * Modifications copyright (c) 2009, 2010 Nicira, Inc.
  */
 
 /* Licensed to the Apache Software Foundation (ASF) under one or more
index 9015104..eda265d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is from the Apache Portable Runtime Library.
  * The full upstream copyright and license statement is included below.
- * Modifications copyright (c) 2009 Nicira Networks.
+ * Modifications copyright (c) 2009 Nicira, Inc.
  */
 
 /* Licensed to the Apache Software Foundation (ASF) under one or more
index a325dec..1cf7d6e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -109,7 +109,7 @@ shash_add_nocopy__(struct shash *sh, char *name, const void *data, size_t hash)
 {
     struct shash_node *node = xmalloc(sizeof *node);
     node->name = name;
-    node->data = (void *) data;
+    node->data = CONST_CAST(void *, data);
     hmap_insert(&sh->map, &node->node, hash);
     return node;
 }
@@ -163,7 +163,7 @@ shash_replace(struct shash *sh, const char *name, const void *data)
         return NULL;
     } else {
         void *old_data = node->data;
-        node->data = (void *) data;
+        node->data = CONST_CAST(void *, data);
         return old_data;
     }
 }
@@ -313,58 +313,3 @@ shash_random_node(struct shash *sh)
 {
     return CONTAINER_OF(hmap_random_node(&sh->map), struct shash_node, node);
 }
-\f
-/* String-to-string maps (smaps). */
-
-/* Frees 'smap', including its keys and string values. */
-void
-smap_destroy(struct shash *smap)
-{
-    shash_destroy_free_data(smap);
-}
-
-/* Returns true if string-to-string maps 'a' and 'b' contain the same keys and
- * values, false otherwise. */
-bool
-smap_equal(const struct shash *a, const struct shash *b)
-{
-    struct shash_node *a_node;
-
-    if (shash_count(a) != shash_count(b)) {
-        return false;
-    }
-
-    SHASH_FOR_EACH (a_node, a) {
-        uint32_t hash = a_node->node.hash;
-        size_t len = strlen(a_node->name);
-        struct shash_node *b_node = shash_find__(b, a_node->name, len, hash);
-        if (!b_node || strcmp(a_node->data, b_node->data)) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-/* Initializes 'dst' as a clone of 'src'. */
-void
-smap_clone(struct shash *dst, const struct shash *src)
-{
-    struct shash_node *node;
-
-    shash_init(dst);
-    SHASH_FOR_EACH (node, src) {
-        shash_add_nocopy__(dst, xstrdup(node->name), xstrdup(node->data),
-                           node->node.hash);
-    }
-}
-
-/* Adds 'key' with string 'value' to 'smap', making a copy of each.
- *
- * It is the caller's responsibility to avoid duplicate names, if that is
- * desirable. */
-void
-smap_add(struct shash *smap, const char *key, const char *value)
-{
-    shash_add(smap, key, xstrdup(value));
-}
index c7e3d14..97d36ba 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 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.
@@ -67,12 +67,6 @@ const struct shash_node **shash_sort(const struct shash *);
 bool shash_equal_keys(const struct shash *, const struct shash *);
 struct shash_node *shash_random_node(struct shash *);
 
-/* Working with "smaps": shashes used as string-to-string maps. */
-void smap_destroy(struct shash *);
-bool smap_equal(const struct shash *, const struct shash *);
-void smap_clone(struct shash *, const struct shash *);
-void smap_add(struct shash *, const char *key, const char *value);
-
 #ifdef  __cplusplus
 }
 #endif
index 7650cb9..b712f7e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -64,8 +64,8 @@ signal_init(void)
     if (!inited) {
         inited = true;
         xpipe(fds);
-        set_nonblocking(fds[0]);
-        set_nonblocking(fds[1]);
+        xset_nonblocking(fds[0]);
+        xset_nonblocking(fds[1]);
     }
 }
 
index e09b04a..ac96b0f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 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.
diff --git a/lib/simap.c b/lib/simap.c
new file mode 100644 (file)
index 0000000..f6804aa
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "simap.h"
+#include <assert.h>
+#include "hash.h"
+
+static size_t hash_name(const char *, size_t length);
+static struct simap_node *simap_find__(const struct simap *,
+                                       const char *name, size_t name_len,
+                                       size_t hash);
+static struct simap_node *simap_add_nocopy__(struct simap *,
+                                             char *name, unsigned int data,
+                                             size_t hash);
+static int compare_nodes_by_name(const void *a_, const void *b_);
+
+/* Initializes 'simap' as an empty string-to-integer map. */
+void
+simap_init(struct simap *simap)
+{
+    hmap_init(&simap->map);
+}
+
+/* Frees all the data that 'simap' contains. */
+void
+simap_destroy(struct simap *simap)
+{
+    if (simap) {
+        simap_clear(simap);
+        hmap_destroy(&simap->map);
+    }
+}
+
+/* Exchanges the contents of 'a' and 'b'. */
+void
+simap_swap(struct simap *a, struct simap *b)
+{
+    hmap_swap(&a->map, &b->map);
+}
+
+/* Adjusts 'simap' so that it is still valid after it has been moved around in
+ * memory (e.g. due to realloc()). */
+void
+simap_moved(struct simap *simap)
+{
+    hmap_moved(&simap->map);
+}
+
+/* Removes all of the mappings from 'simap' and frees them. */
+void
+simap_clear(struct simap *simap)
+{
+    struct simap_node *node, *next;
+
+    SIMAP_FOR_EACH_SAFE (node, next, simap) {
+        hmap_remove(&simap->map, &node->node);
+        free(node->name);
+        free(node);
+    }
+}
+
+/* Returns true if 'simap' contains no mappings, false if it contains at least
+ * one. */
+bool
+simap_is_empty(const struct simap *simap)
+{
+    return hmap_is_empty(&simap->map);
+}
+
+/* Returns the number of mappings in 'simap'. */
+size_t
+simap_count(const struct simap *simap)
+{
+    return hmap_count(&simap->map);
+}
+
+/* Inserts a mapping from 'name' to 'data' into 'simap', replacing any
+ * existing mapping for 'name'.  Returns true if a new mapping was added,
+ * false if an existing mapping's value was replaced.
+ *
+ * The caller retains ownership of 'name'. */
+bool
+simap_put(struct simap *simap, const char *name, unsigned int data)
+{
+    size_t length = strlen(name);
+    size_t hash = hash_name(name, length);
+    struct simap_node *node;
+
+    node = simap_find__(simap, name, length, hash);
+    if (node) {
+        node->data = data;
+        return false;
+    } else {
+        simap_add_nocopy__(simap, xmemdup0(name, length), data, hash);
+        return true;
+    }
+}
+
+/* Increases the data value in the mapping for 'name' by 'amt', or inserts a
+ * mapping from 'name' to 'amt' if no such mapping exists.  Returns the
+ * new total data value for the mapping.
+ *
+ * If 'amt' is zero, this function does nothing and returns 0.  That is, this
+ * function won't create a mapping with a initial value of 0.
+ *
+ * The caller retains ownership of 'name'. */
+unsigned int
+simap_increase(struct simap *simap, const char *name, unsigned int amt)
+{
+    if (amt) {
+        size_t length = strlen(name);
+        size_t hash = hash_name(name, length);
+        struct simap_node *node;
+
+        node = simap_find__(simap, name, length, hash);
+        if (node) {
+            node->data += amt;
+        } else {
+            node = simap_add_nocopy__(simap, xmemdup0(name, length),
+                                      amt, hash);
+        }
+        return node->data;
+    } else {
+        return 0;
+    }
+}
+
+/* Deletes 'node' from 'simap' and frees its associated memory. */
+void
+simap_delete(struct simap *simap, struct simap_node *node)
+{
+    hmap_remove(&simap->map, &node->node);
+    free(node->name);
+    free(node);
+}
+
+/* Searches 'simap' for a mapping with the given 'name'.  Returns it, if found,
+ * or a null pointer if not. */
+struct simap_node *
+simap_find(const struct simap *simap, const char *name)
+{
+    return simap_find_len(simap, name, strlen(name));
+}
+
+/* Searches 'simap' for a mapping whose name is the first 'name_len' bytes
+ * starting at 'name'.  Returns it, if found, or a null pointer if not. */
+struct simap_node *
+simap_find_len(const struct simap *simap, const char *name, size_t len)
+{
+    return simap_find__(simap, name, len, hash_name(name, len));
+}
+
+/* Searches 'simap' for a mapping with the given 'name'.  Returns the
+ * associated data value, if found, otherwise zero. */
+unsigned int
+simap_get(const struct simap *simap, const char *name)
+{
+    struct simap_node *node = simap_find(simap, name);
+    return node ? node->data : 0;
+}
+
+/* Returns an array that contains a pointer to each mapping in 'simap',
+ * ordered alphabetically by name.  The returned array has simap_count(simap)
+ * elements.
+ *
+ * The caller is responsible for freeing the returned array (with free()).  It
+ * should not free the individual "simap_node"s in the array, because they are
+ * still part of 'simap'. */
+const struct simap_node **
+simap_sort(const struct simap *simap)
+{
+    if (simap_is_empty(simap)) {
+        return NULL;
+    } else {
+        const struct simap_node **nodes;
+        struct simap_node *node;
+        size_t i, n;
+
+        n = simap_count(simap);
+        nodes = xmalloc(n * sizeof *nodes);
+        i = 0;
+        SIMAP_FOR_EACH (node, simap) {
+            nodes[i++] = node;
+        }
+        assert(i == n);
+
+        qsort(nodes, n, sizeof *nodes, compare_nodes_by_name);
+
+        return nodes;
+    }
+}
+\f
+static size_t
+hash_name(const char *name, size_t length)
+{
+    return hash_bytes(name, length, 0);
+}
+
+static struct simap_node *
+simap_find__(const struct simap *simap, const char *name, size_t name_len,
+             size_t hash)
+{
+    struct simap_node *node;
+
+    HMAP_FOR_EACH_WITH_HASH (node, node, hash, &simap->map) {
+        if (!strncmp(node->name, name, name_len) && !node->name[name_len]) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+static struct simap_node *
+simap_add_nocopy__(struct simap *simap, char *name, unsigned int data,
+                   size_t hash)
+{
+    struct simap_node *node = xmalloc(sizeof *node);
+    node->name = name;
+    node->data = data;
+    hmap_insert(&simap->map, &node->node, hash);
+    return node;
+}
+
+static int
+compare_nodes_by_name(const void *a_, const void *b_)
+{
+    const struct simap_node *const *a = a_;
+    const struct simap_node *const *b = b_;
+    return strcmp((*a)->name, (*b)->name);
+}
diff --git a/lib/simap.h b/lib/simap.h
new file mode 100644 (file)
index 0000000..e7bf80b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMAP_H
+#define SIMAP_H 1
+
+#include "hmap.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/* A map from strings to unsigned integers. */
+struct simap {
+    struct hmap map;            /* Contains "struct simap_node"s. */
+};
+
+struct simap_node {
+    struct hmap_node node;      /* In struct simap's 'map' hmap. */
+    char *name;
+    unsigned int data;
+};
+
+#define SIMAP_INITIALIZER(SIMAP) { HMAP_INITIALIZER(&(SIMAP)->map) }
+
+#define SIMAP_FOR_EACH(SIMAP_NODE, SIMAP) \
+    HMAP_FOR_EACH (SIMAP_NODE, node, &(SIMAP)->map)
+
+#define SIMAP_FOR_EACH_SAFE(SIMAP_NODE, NEXT, SIMAP) \
+    HMAP_FOR_EACH_SAFE (SIMAP_NODE, NEXT, node, &(SIMAP)->map)
+
+void simap_init(struct simap *);
+void simap_destroy(struct simap *);
+void simap_swap(struct simap *, struct simap *);
+void simap_moved(struct simap *);
+void simap_clear(struct simap *);
+
+bool simap_is_empty(const struct simap *);
+size_t simap_count(const struct simap *);
+
+bool simap_put(struct simap *, const char *, unsigned int);
+unsigned int simap_increase(struct simap *, const char *, unsigned int);
+
+unsigned int simap_get(const struct simap *, const char *);
+struct simap_node *simap_find(const struct simap *, const char *);
+struct simap_node *simap_find_len(const struct simap *,
+                                  const char *, size_t len);
+
+void simap_delete(struct simap *, struct simap_node *);
+
+const struct simap_node **simap_sort(const struct simap *);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* simap.h */
diff --git a/lib/smap.c b/lib/smap.c
new file mode 100644 (file)
index 0000000..b81ac09
--- /dev/null
@@ -0,0 +1,330 @@
+/* Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#include <config.h>
+#include "smap.h"
+
+#include <assert.h>
+
+#include "hash.h"
+#include "json.h"
+
+static struct smap_node *smap_add__(struct smap *, char *, void *,
+                                    size_t hash);
+static struct smap_node *smap_find__(const struct smap *, const char *key,
+                                     size_t key_len, size_t hash);
+static int compare_nodes_by_key(const void *, const void *);
+\f
+/* Public Functions. */
+
+void
+smap_init(struct smap *smap)
+{
+    hmap_init(&smap->map);
+}
+
+void
+smap_destroy(struct smap *smap)
+{
+    if (smap) {
+        smap_clear(smap);
+        hmap_destroy(&smap->map);
+    }
+}
+
+/* Adds 'key' paired with 'value' to 'smap'.  It is the caller's responsibility
+ * to avoid duplicate keys if desirable. */
+struct smap_node *
+smap_add(struct smap *smap, const char *key, const char *value)
+{
+    size_t key_len = strlen(key);
+    return smap_add__(smap, xmemdup0(key, key_len), xstrdup(value),
+                      hash_bytes(key, key_len, 0));
+}
+
+/* Attempts to add 'key' to 'smap' associated with 'value'.  If 'key' already
+ * exists in 'smap', does nothing and returns false.  Otherwise, performs the
+ * addition and returns true. */
+bool
+smap_add_once(struct smap *smap, const char *key, const char *value)
+{
+    if (!smap_get(smap, key)) {
+        smap_add(smap, key, value);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/* Adds 'key' paired with a value derived from 'format' (similar to printf).
+ * It is the caller's responsibility to avoid duplicate keys if desirable. */
+void
+smap_add_format(struct smap *smap, const char *key, const char *format, ...)
+{
+    size_t key_len;
+    va_list args;
+    char *value;
+
+    va_start(args, format);
+    value = xvasprintf(format, args);
+    va_end(args);
+
+    key_len = strlen(key);
+    smap_add__(smap, xmemdup0(key, key_len), value,
+               hash_bytes(key, key_len, 0));
+}
+
+/* Searches for 'key' in 'smap'.  If it does not already exists, adds it.
+ * Otherwise, changes its value to 'value'. */
+void
+smap_replace(struct smap *smap, const char *key, const char *value)
+{
+    size_t  key_len = strlen(key);
+    size_t hash = hash_bytes(key, key_len, 0);
+
+    struct smap_node *node;
+
+    node = smap_find__(smap, key, key_len, hash);
+    if (node) {
+        free(node->value);
+        node->value = xstrdup(value);
+    } else {
+        smap_add__(smap, xmemdup0(key, key_len), xstrdup(value), hash);
+    }
+}
+
+/* If 'key' is in 'smap', removes it.  Otherwise does nothing. */
+void
+smap_remove(struct smap *smap, const char *key)
+{
+    struct smap_node *node = smap_get_node(smap, key);
+
+    if (node) {
+        smap_remove_node(smap, node);
+    }
+}
+
+/* Removes 'node' from 'smap'. */
+void
+smap_remove_node(struct smap *smap, struct smap_node *node)
+{
+    hmap_remove(&smap->map, &node->node);
+    free(node->key);
+    free(node->value);
+    free(node);
+}
+
+/* Deletes 'node' from 'smap'.
+ *
+ * If 'keyp' is nonnull, stores the node's key in '*keyp' and transfers
+ * ownership to the caller.  Otherwise, frees the node's key.  Similarly for
+ * 'valuep' and the node's value. */
+void
+smap_steal(struct smap *smap, struct smap_node *node,
+           char **keyp, char **valuep)
+{
+    if (keyp) {
+        *keyp = node->key;
+    } else {
+        free(node->key);
+    }
+
+    if (valuep) {
+        *valuep = node->value;
+    } else {
+        free(node->value);
+    }
+
+    hmap_remove(&smap->map, &node->node);
+    free(node);
+}
+
+/* Removes all key-value pairs from 'smap'. */
+void
+smap_clear(struct smap *smap)
+{
+    struct smap_node *node, *next;
+
+    SMAP_FOR_EACH_SAFE (node, next, smap) {
+        smap_remove_node(smap, node);
+    }
+}
+
+/* Returns the value associated with 'key' in 'smap', or NULL. */
+const char *
+smap_get(const struct smap *smap, const char *key)
+{
+    struct smap_node *node = smap_get_node(smap, key);
+    return node ? node->value : NULL;
+}
+
+/* Returns the node associated with 'key' in 'smap', or NULL. */
+struct smap_node *
+smap_get_node(const struct smap *smap, const char *key)
+{
+    size_t key_len = strlen(key);
+    return smap_find__(smap, key, key_len, hash_bytes(key, key_len, 0));
+}
+
+/* Gets the value associated with 'key' in 'smap' and converts it to a boolean.
+ * If 'key' is not in 'smap', or its value is neither "true" nor "false",
+ * returns 'def'. */
+bool
+smap_get_bool(const struct smap *smap, const char *key, bool def)
+{
+    const char *value = smap_get(smap, key);
+
+    if (!value) {
+        return def;
+    }
+
+    if (def) {
+        return strcasecmp("false", value) != 0;
+    } else {
+        return !strcasecmp("true", value);
+    }
+}
+
+/* Gets the value associated with 'key' in 'smap' and converts it to an int
+ * using atoi().  If 'key' is not in 'smap', returns 'def'. */
+int
+smap_get_int(const struct smap *smap, const char *key, int def)
+{
+    const char *value = smap_get(smap, key);
+
+    return value ? atoi(value) : def;
+}
+
+/* Returns true of there are no elements in 'smap'. */
+bool
+smap_is_empty(const struct smap *smap)
+{
+    return hmap_is_empty(&smap->map);
+}
+
+/* Returns the number of elements in 'smap'. */
+size_t
+smap_count(const struct smap *smap)
+{
+    return hmap_count(&smap->map);
+}
+
+/* Initializes 'dst' as a clone of 'src. */
+void
+smap_clone(struct smap *dst, const struct smap *src)
+{
+    const struct smap_node *node;
+
+    smap_init(dst);
+    SMAP_FOR_EACH (node, src) {
+        smap_add__(dst, xstrdup(node->key), xstrdup(node->value),
+                   node->node.hash);
+    }
+}
+
+/* Returns an array of nodes sorted on key or NULL if 'smap' is empty.  The
+ * caller is responsible for freeing this array. */
+const struct smap_node **
+smap_sort(const struct smap *smap)
+{
+    if (smap_is_empty(smap)) {
+        return NULL;
+    } else {
+        const struct smap_node **nodes;
+        struct smap_node *node;
+        size_t i, n;
+
+        n = smap_count(smap);
+        nodes = xmalloc(n * sizeof *nodes);
+        i = 0;
+        SMAP_FOR_EACH (node, smap) {
+            nodes[i++] = node;
+        }
+        assert(i == n);
+
+        qsort(nodes, n, sizeof *nodes, compare_nodes_by_key);
+
+        return nodes;
+    }
+}
+
+/* Adds each of the key-value pairs from 'json' (which must be a JSON object
+ * whose values are strings) to 'smap'.
+ *
+ * The caller must have initialized 'smap'.
+ *
+ * The caller retains ownership of 'json' and everything in it. */
+void
+smap_from_json(struct smap *smap, const struct json *json)
+{
+    const struct shash_node *node;
+
+    SHASH_FOR_EACH (node, json_object(json)) {
+        const struct json *value = node->data;
+        smap_add(smap, node->name, json_string(value));
+    }
+}
+
+/* Returns a JSON object that maps from the keys in 'smap' to their values.
+ *
+ * The caller owns the returned value and must eventually json_destroy() it.
+ *
+ * The caller retains ownership of 'smap' and everything in it. */
+struct json *
+smap_to_json(const struct smap *smap)
+{
+    const struct smap_node *node;
+    struct json *json;
+
+    json = json_object_create();
+    SMAP_FOR_EACH (node, smap) {
+        json_object_put_string(json, node->key, node->value);
+    }
+    return json;
+}
+\f
+/* Private Helpers. */
+
+static struct smap_node *
+smap_add__(struct smap *smap, char *key, void *value, size_t hash)
+{
+    struct smap_node *node = xmalloc(sizeof *node);
+    node->key = key;
+    node->value = value;
+    hmap_insert(&smap->map, &node->node, hash);
+    return node;
+}
+
+static struct smap_node *
+smap_find__(const struct smap *smap, const char *key, size_t key_len,
+            size_t hash)
+{
+    struct smap_node *node;
+
+    HMAP_FOR_EACH_WITH_HASH (node, node, hash, &smap->map) {
+        if (!strncmp(node->key, key, key_len) && !node->key[key_len]) {
+            return node;
+        }
+    }
+
+    return NULL;
+}
+
+static int
+compare_nodes_by_key(const void *a_, const void *b_)
+{
+    const struct smap_node *const *a = a_;
+    const struct smap_node *const *b = b_;
+    return strcmp((*a)->key, (*b)->key);
+}
diff --git a/lib/smap.h b/lib/smap.h
new file mode 100644 (file)
index 0000000..640c2a8
--- /dev/null
@@ -0,0 +1,69 @@
+/* Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.  */
+
+#ifndef SMAP_H
+#define SMAP_H 1
+
+#include "hmap.h"
+
+struct json;
+
+/* A map from string to string. */
+struct smap {
+    struct hmap map;           /* Contains "struct smap_node"s. */
+};
+
+struct smap_node {
+    struct hmap_node node;     /* In struct smap's 'map' hmap. */
+    char *key;
+    char *value;
+};
+
+#define SMAP_INITIALIZER(SMAP) { HMAP_INITIALIZER(&(SMAP)->map) }
+
+#define SMAP_FOR_EACH(SMAP_NODE, SMAP) \
+    HMAP_FOR_EACH (SMAP_NODE, node, &(SMAP)->map)
+
+#define SMAP_FOR_EACH_SAFE(SMAP_NODE, NEXT, SMAP) \
+    HMAP_FOR_EACH_SAFE (SMAP_NODE, NEXT, node, &(SMAP)->map)
+
+void smap_init(struct smap *);
+void smap_destroy(struct smap *);
+
+struct smap_node *smap_add(struct smap *, const char *, const char *);
+bool smap_add_once(struct smap *, const char *, const char *);
+void smap_add_format(struct smap *, const char *key, const char *, ...)
+    PRINTF_FORMAT(3, 4);
+void smap_replace(struct smap *, const char *, const char *);
+
+void smap_remove(struct smap *, const char *);
+void smap_remove_node(struct smap *, struct smap_node *);
+void smap_steal(struct smap *, struct smap_node *, char **keyp, char **valuep);
+void smap_clear(struct smap *);
+
+const char *smap_get(const struct smap *, const char *);
+struct smap_node *smap_get_node(const struct smap *, const char *);
+bool smap_get_bool(const struct smap *smap, const char *key, bool def);
+int smap_get_int(const struct smap *smap, const char *key, int def);
+
+bool smap_is_empty(const struct smap *);
+size_t smap_count(const struct smap *);
+
+void smap_clone(struct smap *dst, const struct smap *src);
+const struct smap_node **smap_sort(const struct smap *);
+
+void smap_from_json(struct smap *, const struct json *);
+struct json *smap_to_json(const struct smap *);
+
+#endif /* smap.h */
index a9b3b1a..7c40ab8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <sys/resource.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/uio.h>
 #include <sys/un.h>
 #include <unistd.h>
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "packets.h"
+#include "poll-loop.h"
 #include "util.h"
 #include "vlog.h"
 #if AF_PACKET && __linux__
@@ -82,15 +84,25 @@ set_nonblocking(int fd)
     }
 }
 
+void
+xset_nonblocking(int fd)
+{
+    if (set_nonblocking(fd)) {
+        exit(EXIT_FAILURE);
+    }
+}
+
 static int
 set_dscp(int fd, uint8_t dscp)
 {
+    int val;
+
     if (dscp > 63) {
         return EINVAL;
     }
 
-    dscp = dscp << 2;
-    if (setsockopt(fd, IPPROTO_IP, IP_TOS, &dscp, sizeof dscp)) {
+    val = dscp << 2;
+    if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof val)) {
         return errno;
     }
 
@@ -171,7 +183,7 @@ lookup_ipv6(const char *host_name, struct in6_addr *addr)
  * Most Open vSwitch code should not use this because it causes deadlocks:
  * gethostbyname() sends out a DNS request but that starts a new flow for which
  * OVS must set up a flow, but it can't because it's waiting for a DNS reply.
- * The synchronous lookup also delays other activty.  (Of course we can solve
+ * The synchronous lookup also delays other activity.  (Of course we can solve
  * this but it doesn't seem worthwhile quite yet.)  */
 int
 lookup_hostname(const char *host_name, struct in_addr *addr)
@@ -384,12 +396,11 @@ bind_unix_socket(int fd, struct sockaddr *sun, socklen_t sun_len)
 /* Creates a Unix domain socket in the given 'style' (either SOCK_DGRAM or
  * SOCK_STREAM) that is bound to '*bind_path' (if 'bind_path' is non-null) and
  * connected to '*connect_path' (if 'connect_path' is non-null).  If 'nonblock'
- * is true, the socket is made non-blocking.  If 'passcred' is true, the socket
- * is configured to receive SCM_CREDENTIALS control messages.
+ * is true, the socket is made non-blocking.
  *
  * Returns the socket's fd if successful, otherwise a negative errno value. */
 int
-make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED,
+make_unix_socket(int style, bool nonblock,
                  const char *bind_path, const char *connect_path)
 {
     int error;
@@ -457,16 +468,6 @@ make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED,
         }
     }
 
-#ifdef SCM_CREDENTIALS
-    if (passcred) {
-        int enable = 1;
-        if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable))) {
-            error = errno;
-            goto error;
-        }
-    }
-#endif
-
     return fd;
 
 error:
@@ -892,6 +893,14 @@ xpipe(int fds[2])
     }
 }
 
+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));
+    }
+}
+
 static int
 getsockopt_int(int fd, int level, int option, const char *optname, int *valuep)
 {
@@ -1051,3 +1060,249 @@ describe_fd(int fd)
     }
     return ds_steal_cstr(&string);
 }
+
+/* Returns the total of the 'iov_len' members of the 'n_iovs' in 'iovs'.
+ * The caller must ensure that the total does not exceed SIZE_MAX. */
+size_t
+iovec_len(const struct iovec iovs[], size_t n_iovs)
+{
+    size_t len = 0;
+    size_t i;
+
+    for (i = 0; i < n_iovs; i++) {
+        len += iovs[i].iov_len;
+    }
+    return len;
+}
+
+/* Returns true if all of the 'n_iovs' iovecs in 'iovs' have length zero. */
+bool
+iovec_is_empty(const struct iovec iovs[], size_t n_iovs)
+{
+    size_t i;
+
+    for (i = 0; i < n_iovs; i++) {
+        if (iovs[i].iov_len) {
+            return false;
+        }
+    }
+    return true;
+}
+
+/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
+ * in 'fds' on Unix domain socket 'sock'.  Returns the number of bytes
+ * successfully sent or -1 if an error occurred.  On error, sets errno
+ * appropriately.  */
+int
+send_iovec_and_fds(int sock,
+                   const struct iovec *iovs, size_t n_iovs,
+                   const int fds[], size_t n_fds)
+{
+    assert(sock >= 0);
+    if (n_fds > 0) {
+        union {
+            struct cmsghdr cm;
+            char control[CMSG_SPACE(SOUTIL_MAX_FDS * sizeof *fds)];
+        } cmsg;
+        struct msghdr msg;
+
+        assert(!iovec_is_empty(iovs, n_iovs));
+        assert(n_fds <= SOUTIL_MAX_FDS);
+
+        memset(&cmsg, 0, sizeof cmsg);
+        cmsg.cm.cmsg_len = CMSG_LEN(n_fds * sizeof *fds);
+        cmsg.cm.cmsg_level = SOL_SOCKET;
+        cmsg.cm.cmsg_type = SCM_RIGHTS;
+        memcpy(CMSG_DATA(&cmsg.cm), fds, n_fds * sizeof *fds);
+
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_iov = (struct iovec *) iovs;
+        msg.msg_iovlen = n_iovs;
+        msg.msg_control = &cmsg.cm;
+        msg.msg_controllen = CMSG_SPACE(n_fds * sizeof *fds);
+        msg.msg_flags = 0;
+
+        return sendmsg(sock, &msg, 0);
+    } else {
+        return writev(sock, iovs, n_iovs);
+    }
+}
+
+/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
+ * in 'fds' on Unix domain socket 'sock'.  If 'skip_bytes' is nonzero, then the
+ * first 'skip_bytes' of data in the iovecs are not sent, and none of the file
+ * descriptors are sent.  The function continues to retry sending until an
+ * error (other than EINTR) occurs or all the data and fds are sent.
+ *
+ * Returns 0 if all the data and fds were successfully sent, otherwise a
+ * positive errno value.  Regardless of success, stores the number of bytes
+ * sent (always at least 'skip_bytes') in '*bytes_sent'.  (If at least one byte
+ * is sent, then all the fds have been sent.)
+ *
+ * 'skip_bytes' must be less than or equal to iovec_len(iovs, n_iovs). */
+int
+send_iovec_and_fds_fully(int sock,
+                         const struct iovec iovs[], size_t n_iovs,
+                         const int fds[], size_t n_fds,
+                         size_t skip_bytes, size_t *bytes_sent)
+{
+    *bytes_sent = 0;
+    while (n_iovs > 0) {
+        int retval;
+
+        if (skip_bytes) {
+            retval = skip_bytes;
+            skip_bytes = 0;
+        } else if (!*bytes_sent) {
+            retval = send_iovec_and_fds(sock, iovs, n_iovs, fds, n_fds);
+        } else {
+            retval = writev(sock, iovs, n_iovs);
+        }
+
+        if (retval > 0) {
+            *bytes_sent += retval;
+            while (retval > 0) {
+                const uint8_t *base = iovs->iov_base;
+                size_t len = iovs->iov_len;
+
+                if (retval < len) {
+                    size_t sent;
+                    int error;
+
+                    error = write_fully(sock, base + retval, len - retval,
+                                        &sent);
+                    *bytes_sent += sent;
+                    retval += sent;
+                    if (error) {
+                        return error;
+                    }
+                }
+                retval -= len;
+                iovs++;
+                n_iovs--;
+            }
+        } else if (retval == 0) {
+            if (iovec_is_empty(iovs, n_iovs)) {
+                break;
+            }
+            VLOG_WARN("send returned 0");
+            return EPROTO;
+        } else if (errno != EINTR) {
+            return errno;
+        }
+    }
+
+    return 0;
+}
+
+/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
+ * in 'fds' on Unix domain socket 'sock'.  The function continues to retry
+ * sending until an error (other than EAGAIN or EINTR) occurs or all the data
+ * and fds are sent.  Upon EAGAIN, the function blocks until the socket is
+ * ready for more data.
+ *
+ * Returns 0 if all the data and fds were successfully sent, otherwise a
+ * positive errno value. */
+int
+send_iovec_and_fds_fully_block(int sock,
+                               const struct iovec iovs[], size_t n_iovs,
+                               const int fds[], size_t n_fds)
+{
+    size_t sent = 0;
+
+    for (;;) {
+        int error;
+
+        error = send_iovec_and_fds_fully(sock, iovs, n_iovs,
+                                         fds, n_fds, sent, &sent);
+        if (error != EAGAIN) {
+            return error;
+        }
+        poll_fd_wait(sock, POLLOUT);
+        poll_block();
+    }
+}
+
+/* Attempts to receive from Unix domain socket 'sock' up to 'size' bytes of
+ * data into 'data' and up to SOUTIL_MAX_FDS file descriptors into 'fds'.
+ *
+ *      - Upon success, returns the number of bytes of data copied into 'data'
+ *        and stores the number of received file descriptors into '*n_fdsp'.
+ *
+ *      - On failure, returns a negative errno value and stores 0 in
+ *        '*n_fdsp'.
+ *
+ *      - On EOF, returns 0 and stores 0 in '*n_fdsp'. */
+int
+recv_data_and_fds(int sock,
+                  void *data, size_t size,
+                  int fds[SOUTIL_MAX_FDS], size_t *n_fdsp)
+{
+    union {
+        struct cmsghdr cm;
+        char control[CMSG_SPACE(SOUTIL_MAX_FDS * sizeof *fds)];
+    } cmsg;
+    struct msghdr msg;
+    int retval;
+    struct cmsghdr *p;
+    size_t i;
+
+    *n_fdsp = 0;
+
+    do {
+        struct iovec iov;
+
+        iov.iov_base = data;
+        iov.iov_len = size;
+
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+        msg.msg_control = &cmsg.cm;
+        msg.msg_controllen = sizeof cmsg.control;
+        msg.msg_flags = 0;
+
+        retval = recvmsg(sock, &msg, 0);
+    } while (retval < 0 && errno == EINTR);
+    if (retval <= 0) {
+        return retval < 0 ? -errno : 0;
+    }
+
+    for (p = CMSG_FIRSTHDR(&msg); p; p = CMSG_NXTHDR(&msg, p)) {
+        if (p->cmsg_level != SOL_SOCKET || p->cmsg_type != SCM_RIGHTS) {
+            VLOG_ERR("unexpected control message %d:%d",
+                     p->cmsg_level, p->cmsg_type);
+            goto error;
+        } else if (*n_fdsp) {
+            VLOG_ERR("multiple SCM_RIGHTS received");
+            goto error;
+        } else {
+            size_t n_fds = (p->cmsg_len - CMSG_LEN(0)) / sizeof *fds;
+            const int *fds_data = (const int *) CMSG_DATA(p);
+
+            assert(n_fds > 0);
+            if (n_fds > SOUTIL_MAX_FDS) {
+                VLOG_ERR("%zu fds received but only %d supported",
+                         n_fds, SOUTIL_MAX_FDS);
+                for (i = 0; i < n_fds; i++) {
+                    close(fds_data[i]);
+                }
+                goto error;
+            }
+
+            *n_fdsp = n_fds;
+            memcpy(fds, fds_data, n_fds * sizeof *fds);
+        }
+    }
+
+    return retval;
+
+error:
+    for (i = 0; i < *n_fdsp; i++) {
+        close(fds[i]);
+    }
+    *n_fdsp = 0;
+    return EPROTO;
+}
index 8ea40d5..a0e7970 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@
 #include <netinet/ip.h>
 
 int set_nonblocking(int fd);
+void xset_nonblocking(int fd);
+
 int get_max_fds(void);
 
 int lookup_ip(const char *host_name, struct in_addr *address);
@@ -38,7 +40,7 @@ int get_socket_rcvbuf(int sock);
 int check_connection_completion(int fd);
 int drain_rcvbuf(int fd);
 void drain_fd(int fd, size_t n_packets);
-int make_unix_socket(int style, bool nonblock, bool passcred,
+int make_unix_socket(int style, bool nonblock,
                      const char *bind_path, const char *connect_path);
 int get_unix_name_len(socklen_t sun_len);
 ovs_be32 guess_netmask(ovs_be32 ip);
@@ -69,4 +71,28 @@ char *describe_fd(int fd);
  * in <netinet/ip.h> is used. */
 #define DSCP_DEFAULT (IPTOS_PREC_INTERNETCONTROL >> 2)
 
+/* Maximum number of fds that we support sending or receiving at one time
+ * across a Unix domain socket. */
+#define SOUTIL_MAX_FDS 8
+
+/* Iovecs. */
+size_t iovec_len(const struct iovec *iovs, size_t n_iovs);
+bool iovec_is_empty(const struct iovec *iovs, size_t n_iovs);
+
+/* Functions particularly useful for Unix domain sockets. */
+void xsocketpair(int domain, int type, int protocol, int fds[2]);
+int send_iovec_and_fds(int sock,
+                       const struct iovec *iovs, size_t n_iovs,
+                       const int fds[], size_t n_fds);
+int send_iovec_and_fds_fully(int sock,
+                             const struct iovec *iovs, size_t n_iovs,
+                             const int fds[], size_t n_fds,
+                             size_t skip_bytes, size_t *bytes_sent);
+int send_iovec_and_fds_fully_block(int sock,
+                                   const struct iovec *iovs, size_t n_iovs,
+                                   const int fds[], size_t n_fds);
+int recv_data_and_fds(int sock,
+                      void *data, size_t size,
+                      int fds[SOUTIL_MAX_FDS], size_t *n_fdsp);
+
 #endif /* socket-util.h */
index 017b0a9..ed40637 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index c952f44..9887e8f 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 9a0936b..ee4224a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index a739fce..f63f4ab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
 #define SSET_H
 
 #include "hmap.h"
+#include "util.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -82,7 +83,7 @@ bool sset_equals(const struct sset *, const struct sset *);
 #define SSET_NODE_FROM_HMAP_NODE(HMAP_NODE) \
     CONTAINER_OF(HMAP_NODE, struct sset_node, hmap_node)
 #define SSET_NAME_FROM_HMAP_NODE(HMAP_NODE) \
-    ((const char *) (SSET_NODE_FROM_HMAP_NODE(HMAP_NODE)->name))
+    (CONST_CAST(const char *, (SSET_NODE_FROM_HMAP_NODE(HMAP_NODE)->name)))
 #define SSET_NODE_FROM_NAME(NAME) CONTAINER_OF(NAME, struct sset_node, name)
 #define SSET_FIRST(SSET) SSET_NAME_FROM_HMAP_NODE(hmap_first(&(SSET)->map))
 #define SSET_NEXT(SSET, NAME)                                           \
index e923053..3d293b6 100644 (file)
--- a/lib/stp.c
+++ b/lib/stp.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -151,7 +151,7 @@ stp_next_enabled_port(const struct stp *stp, const struct stp_port *port)
 {
     for (; port < &stp->ports[ARRAY_SIZE(stp->ports)]; port++) {
         if (port->state != STP_DISABLED) {
-            return (struct stp_port *) port;
+            return CONST_CAST(struct stp_port *, port);
         }
     }
     return NULL;
index 8ce093e..524b9dc 100644 (file)
--- a/lib/stp.h
+++ b/lib/stp.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 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.
index 4113e3f..6b782ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index b42615f..8026953 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index cdbbf5d..23bc1fb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 1eb7c94..0708c72 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
@@ -54,7 +54,7 @@ struct stream_class {
     /* Prefix for connection names, e.g. "tcp", "ssl", "unix". */
     const char *name;
 
-    /* True if this stream needs periodic probes to verify connectivty.  For
+    /* True if this stream needs periodic probes to verify connectivity.  For
      * streams which need probes, it can take a long time to notice the
      * connection was dropped. */
     bool needs_probes;
@@ -154,7 +154,7 @@ struct pstream_class {
     /* Prefix for connection names, e.g. "ptcp", "pssl", "punix". */
     const char *name;
 
-    /* True if this pstream needs periodic probes to verify connectivty.  For
+    /* True if this pstream needs periodic probes to verify connectivity.  For
      * pstreams which need probes, it can take a long time to notice the
      * connection was dropped. */
     bool needs_probes;
index ad572d3..4a9dd4f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
 #include <sys/socket.h>
 #include <netinet/tcp.h>
 #include <openssl/err.h>
+#include <openssl/rand.h>
 #include <openssl/ssl.h>
 #include <openssl/x509v3.h>
 #include <poll.h>
@@ -34,6 +35,7 @@
 #include <unistd.h>
 #include "coverage.h"
 #include "dynamic-string.h"
+#include "entropy.h"
 #include "leak-checker.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
@@ -228,7 +230,7 @@ new_ssl_stream(const char *name, int fd, enum session_type type,
         VLOG_ERR("CA certificate must be configured to use SSL");
         retval = ENOPROTOOPT;
     }
-    if (!SSL_CTX_check_private_key(ctx)) {
+    if (!retval && !SSL_CTX_check_private_key(ctx)) {
         VLOG_ERR("Private key does not match certificate public key: %s",
                  ERR_error_string(ERR_get_error(), NULL));
         retval = ENOPROTOOPT;
@@ -477,7 +479,7 @@ ssl_connect(struct stream *stream)
              * certificate, but that's more trouble than it's worth.  These
              * connections will succeed the next time they retry, assuming that
              * they have a certificate against the correct CA.) */
-            VLOG_ERR("rejecting SSL connection during bootstrap race window");
+            VLOG_INFO("rejecting SSL connection during bootstrap race window");
             return EPROTO;
         } else {
             return 0;
@@ -900,9 +902,39 @@ do_ssl_init(void)
     SSL_library_init();
     SSL_load_error_strings();
 
+    if (!RAND_status()) {
+        /* We occasionally see OpenSSL fail to seed its random number generator
+         * in heavily loaded hypervisors.  I suspect the following scenario:
+         *
+         * 1. OpenSSL calls read() to get 32 bytes from /dev/urandom.
+         * 2. The kernel generates 10 bytes of randomness and copies it out.
+         * 3. A signal arrives (perhaps SIGALRM).
+         * 4. The kernel interrupts the system call to service the signal.
+         * 5. Userspace gets 10 bytes of entropy.
+         * 6. OpenSSL doesn't read again to get the final 22 bytes.  Therefore
+         *    OpenSSL doesn't have enough entropy to consider itself
+         *    initialized.
+         *
+         * The only part I'm not entirely sure about is #6, because the OpenSSL
+         * code is so hard to read. */
+        uint8_t seed[32];
+        int retval;
+
+        VLOG_WARN("OpenSSL random seeding failed, reseeding ourselves");
+
+        retval = get_entropy(seed, sizeof seed);
+        if (retval) {
+            VLOG_ERR("failed to obtain entropy (%s)",
+                     ovs_retval_to_string(retval));
+            return retval > 0 ? retval : ENOPROTOOPT;
+        }
+
+        RAND_seed(seed, sizeof seed);
+    }
+
     /* New OpenSSL changed TLSv1_method() to return a "const" pointer, so the
      * cast is needed to avoid a warning with those newer versions. */
-    method = (SSL_METHOD *) TLSv1_method();
+    method = CONST_CAST(SSL_METHOD *, TLSv1_method());
     if (method == NULL) {
         VLOG_ERR("TLSv1_method: %s", ERR_error_string(ERR_get_error(), NULL));
         return ENOPROTOOPT;
index 83b9ddb..030f662 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 9762f88..9749293 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 40ef012..689dcf1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ unix_open(const char *name, char *suffix, struct stream **streamp,
     const char *connect_path = suffix;
     int fd;
 
-    fd = make_unix_socket(SOCK_STREAM, true, false, NULL, connect_path);
+    fd = make_unix_socket(SOCK_STREAM, true, NULL, connect_path);
     if (fd < 0) {
         VLOG_ERR("%s: connection failed (%s)", connect_path, strerror(-fd));
         return -fd;
@@ -79,7 +79,7 @@ punix_open(const char *name OVS_UNUSED, char *suffix,
 {
     int fd, error;
 
-    fd = make_unix_socket(SOCK_STREAM, true, true, suffix, NULL);
+    fd = make_unix_socket(SOCK_STREAM, true, suffix, NULL);
     if (fd < 0) {
         VLOG_ERR("%s: binding failed: %s", suffix, strerror(errno));
         return errno;
index eabace8..2c7bfc3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -778,7 +778,7 @@ stream_guess_content(const uint8_t *data, ssize_t size)
             return STREAM_SSL;
         case PAIR('{', '"'):
             return STREAM_JSONRPC;
-        case PAIR(OFP10_VERSION, OFPT_HELLO):
+        case PAIR(OFP10_VERSION, 0 /* OFPT_HELLO */):
             return STREAM_OPENFLOW;
         }
     }
index bd39017..8ed0ff5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 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.
index dfd0099..a14209e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 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.
index 9d6ea97..498deef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 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.
index f9ce788..082359d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011 Nicira Networks.
+ * Copyright (c) 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.
index 5e372bb..2b7b454 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011 Nicira Networks.
+ * Copyright (c) 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.
index 03c3747..381ee32 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 0ee058c..341e269 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * 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.
index 8141677..f24ddc6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -224,7 +224,7 @@ static void
 table_format_timestamp__(char *s, size_t size)
 {
     time_t now = time_wall();
-    strftime(s, size, "%Y-%m-%d %H:%M:%S", localtime(&now));
+    strftime(s, size, "%Y-%m-%d %H:%M:%S", gmtime(&now));
 }
 
 static void
index c29d7e3..6c8f763 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index bf2c9a5..f064d17 100644 (file)
--- a/lib/tag.c
+++ b/lib/tag.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 9cb581d..2050de0 100644 (file)
--- a/lib/tag.h
+++ b/lib/tag.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 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.
index 84d20a4..e767db6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index be7bce2..e9650ad 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index b60ece9..3d339e4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ VLOG_DEFINE_THIS_MODULE(timeval);
  * to CLOCK_REALTIME. */
 static clockid_t monotonic_clock;
 
-/* Has a timer tick occurred?
+/* Has a timer tick occurred? Only relevant if CACHE_TIME is 1.
  *
  * We initialize these to true to force time_init() to get called on the first
  * call to time_msec() or another function that queries the current time. */
@@ -94,8 +94,11 @@ time_init(void)
         VLOG_DBG("monotonic timer not available");
     }
 
-    set_up_signal(SA_RESTART);
-    set_up_timer();
+    if (CACHE_TIME) {
+        set_up_signal(SA_RESTART);
+        set_up_timer();
+    }
+
     boot_time = time_msec();
 }
 
@@ -168,7 +171,16 @@ void
 time_postfork(void)
 {
     time_init();
-    set_up_timer();
+
+    if (CACHE_TIME) {
+        set_up_timer();
+    } else {
+        /* If we are not caching  kernel time, the only reason the timer should
+         * exist is if time_alarm() was called and deadline is set */
+        if (deadline != TIME_MIN) {
+            set_up_timer();
+        }
+    }
 }
 
 static void
@@ -199,7 +211,9 @@ refresh_monotonic(void)
 
 /* Forces a refresh of the current time from the kernel.  It is not usually
  * necessary to call this function, since the time will be refreshed
- * automatically at least every TIME_UPDATE_INTERVAL milliseconds. */
+ * automatically at least every TIME_UPDATE_INTERVAL milliseconds.  If
+ * CACHE_TIME is 0, we will always refresh the current time so this
+ * function has no effect. */
 void
 time_refresh(void)
 {
@@ -275,9 +289,17 @@ time_alarm(unsigned int secs)
     sigset_t oldsigs;
 
     time_init();
+
     block_sigalrm(&oldsigs);
     deadline = secs ? time_add(time_now(), secs) : TIME_MIN;
     unblock_sigalrm(&oldsigs);
+
+    if (!CACHE_TIME) {
+        /* If we aren't timing the gaps between kernel time refreshes we need to
+         * to start the timer up now */
+        set_up_signal(SA_RESTART);
+        set_up_timer();
+    }
 }
 
 /* Like poll(), except:
@@ -366,7 +388,7 @@ sigalrm_handler(int sig_nr)
 static void
 refresh_wall_if_ticked(void)
 {
-    if (wall_tick) {
+    if (!CACHE_TIME || wall_tick) {
         refresh_wall();
     }
 }
@@ -374,7 +396,7 @@ refresh_wall_if_ticked(void)
 static void
 refresh_monotonic_if_ticked(void)
 {
-    if (monotonic_tick) {
+    if (!CACHE_TIME || monotonic_tick) {
         refresh_monotonic();
     }
 }
index e9ff183..1384848 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -36,6 +36,21 @@ BUILD_ASSERT_DECL(TYPE_IS_INTEGER(time_t));
  * ever encounter such a platform. */
 BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t));
 
+/* On x86-64 systems, Linux avoids using syscalls for clock_gettime().
+ *
+ * For systems which do invoke a system call we wait at least
+ * TIME_UPDATE_INTERVAL ms between clock_gettime() calls and cache the time for
+ * the interim.
+ *
+ * For systems which do not invoke a system call, we just call clock_gettime()
+ * whenever the time is requested.  As a result we don't start the background
+ * SIGALRM timer unless explicitly needed by time_alarm() */
+#if defined __x86_64__ && defined __linux__
+#define CACHE_TIME 0
+#else
+#define CACHE_TIME 1
+#endif
+
 #define TIME_MAX TYPE_MAXIMUM(time_t)
 #define TIME_MIN TYPE_MINIMUM(time_t)
 
diff --git a/lib/token-bucket.c b/lib/token-bucket.c
new file mode 100644 (file)
index 0000000..73f11a8
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "token-bucket.h"
+
+#include "poll-loop.h"
+#include "sat-math.h"
+#include "timeval.h"
+#include "util.h"
+
+/* Initializes 'tb' to accumulate 'rate' tokens per millisecond, with a
+ * maximum of 'burst' tokens.
+ *
+ * The token bucket is initially full.
+ *
+ * It may be more convenient to use TOKEN_BUCKET_INIT. */
+void
+token_bucket_init(struct token_bucket *tb,
+                  unsigned int rate, unsigned int burst)
+{
+    tb->rate = rate;
+    tb->burst = burst;
+    tb->tokens = 0;
+    tb->last_fill = LLONG_MIN;
+}
+
+/* Changes 'tb' to accumulate 'rate' tokens per millisecond, with a maximum of
+ * 'burst' tokens.
+ *
+ * 'tb' must already have been initialized with TOKEN_BUCKET_INIT or
+ * token_bucket_init(). */
+void
+token_bucket_set(struct token_bucket *tb,
+                 unsigned int rate, unsigned int burst)
+{
+    tb->rate = rate;
+    tb->burst = burst;
+    if (burst > tb->tokens) {
+        tb->tokens = burst;
+    }
+}
+
+/* Attempts to remove 'n' tokens from 'tb'.  Returns true if successful, false
+ * if 'tb' contained fewer than 'n' tokens (and thus 'n' tokens could not be
+ * removed) . */
+bool
+token_bucket_withdraw(struct token_bucket *tb, unsigned int n)
+{
+    if (tb->tokens < n) {
+        long long int now = time_msec();
+        if (now > tb->last_fill) {
+            unsigned long long int elapsed_ull
+                = (unsigned long long int) now - tb->last_fill;
+            unsigned int elapsed = MIN(UINT_MAX, elapsed_ull);
+            unsigned int add = sat_mul(tb->rate, elapsed);
+            unsigned int tokens = sat_add(tb->tokens, add);
+            tb->tokens = MIN(tokens, tb->burst);
+            tb->last_fill = now;
+        }
+
+        if (tb->tokens < n) {
+            return false;
+        }
+    }
+
+    tb->tokens -= n;
+    return true;
+}
+
+/* Causes the poll loop to wake up when at least 'n' tokens will be available
+ * for withdrawal from 'tb'. */
+void
+token_bucket_wait(struct token_bucket *tb, unsigned int n)
+{
+    if (tb->tokens >= n) {
+        poll_immediate_wake();
+    } else {
+        unsigned int need = n - tb->tokens;
+        poll_timer_wait_until(tb->last_fill + need / tb->rate + 1);
+    }
+}
diff --git a/lib/token-bucket.h b/lib/token-bucket.h
new file mode 100644 (file)
index 0000000..ef2b1ae
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TOKEN_BUCKET_H
+#define TOKEN_BUCKET_H 1
+
+#include <limits.h>
+#include <stdbool.h>
+
+struct token_bucket {
+    /* Configuration settings. */
+    unsigned int rate;          /* Tokens added per millisecond. */
+    unsigned int burst;         /* Max cumulative tokens credit. */
+
+    /* Current status. */
+    unsigned int tokens;        /* Current number of tokens. */
+    long long int last_fill;    /* Last time tokens added. */
+};
+
+#define TOKEN_BUCKET_INIT(RATE, BURST) { RATE, BURST, 0, LLONG_MIN }
+
+void token_bucket_init(struct token_bucket *,
+                       unsigned int rate, unsigned int burst);
+void token_bucket_set(struct token_bucket *,
+                       unsigned int rate, unsigned int burst);
+bool token_bucket_withdraw(struct token_bucket *, unsigned int n);
+void token_bucket_wait(struct token_bucket *, unsigned int n);
+
+#endif /* token-bucket.h */
index 5c75d9f..8c83ea6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 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.
index 87b060c..2654a27 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 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.
index e8fea86..31612a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
index b2078e6..f5139ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
index b495deb..4ab493d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 13a8096..ce43893 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * 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.
index f9880f3..cd99142 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include "byte-order.h"
 #include "coverage.h"
@@ -34,7 +35,14 @@ VLOG_DEFINE_THIS_MODULE(util);
 
 COVERAGE_DEFINE(util_xalloc);
 
+/* argv[0] without directory names. */
 const char *program_name;
+
+/* Ordinarily "" but set to "monitor" for a monitor process or "worker" for a
+ * worker process. */
+const char *subprogram_name = "";
+
+/* --version option output. */
 static char *program_version;
 
 void
@@ -192,9 +200,14 @@ ovs_abort(int err_no, const char *format, ...)
     va_list args;
 
     va_start(args, format);
-    ovs_error_valist(err_no, format, args);
-    va_end(args);
+    ovs_abort_valist(err_no, format, args);
+}
 
+/* Same as ovs_abort() except that the arguments are supplied as a va_list. */
+void
+ovs_abort_valist(int err_no, const char *format, va_list args)
+{
+    ovs_error_valist(err_no, format, args);
     abort();
 }
 
@@ -243,7 +256,12 @@ ovs_error_valist(int err_no, const char *format, va_list args)
 {
     int save_errno = errno;
 
-    fprintf(stderr, "%s: ", program_name);
+    if (subprogram_name[0]) {
+        fprintf(stderr, "%s(%s): ", program_name, subprogram_name);
+    } else {
+        fprintf(stderr, "%s: ", program_name);
+    }
+
     vfprintf(stderr, format, args);
     if (err_no != 0) {
         fprintf(stderr, " (%s)", ovs_retval_to_string(err_no));
@@ -630,6 +648,90 @@ abs_file_name(const char *dir, const char *file_name)
     }
 }
 
+/* Like readlink(), but returns the link name as a null-terminated string in
+ * allocated memory that the caller must eventually free (with free()).
+ * Returns NULL on error, in which case errno is set appropriately. */
+char *
+xreadlink(const char *filename)
+{
+    size_t size;
+
+    for (size = 64; ; size *= 2) {
+        char *buf = xmalloc(size);
+        ssize_t retval = readlink(filename, buf, size);
+        int error = errno;
+
+        if (retval >= 0 && retval < size) {
+            buf[retval] = '\0';
+            return buf;
+        }
+
+        free(buf);
+        if (retval < 0) {
+            errno = error;
+            return NULL;
+        }
+    }
+}
+
+/* Returns a version of 'filename' with symlinks in the final component
+ * dereferenced.  This differs from realpath() in that:
+ *
+ *     - 'filename' need not exist.
+ *
+ *     - If 'filename' does exist as a symlink, its referent need not exist.
+ *
+ *     - Only symlinks in the final component of 'filename' are dereferenced.
+ *
+ * The caller must eventually free the returned string (with free()). */
+char *
+follow_symlinks(const char *filename)
+{
+    struct stat s;
+    char *fn;
+    int i;
+
+    fn = xstrdup(filename);
+    for (i = 0; i < 10; i++) {
+        char *linkname;
+        char *next_fn;
+
+        if (lstat(fn, &s) != 0 || !S_ISLNK(s.st_mode)) {
+            return fn;
+        }
+
+        linkname = xreadlink(fn);
+        if (!linkname) {
+            VLOG_WARN("%s: readlink failed (%s)", filename, strerror(errno));
+            return fn;
+        }
+
+        if (linkname[0] == '/') {
+            /* Target of symlink is absolute so use it raw. */
+            next_fn = linkname;
+        } else {
+            /* Target of symlink is relative so add to 'fn''s directory. */
+            char *dir = dir_name(fn);
+
+            if (!strcmp(dir, ".")) {
+                next_fn = linkname;
+            } else {
+                char *separator = dir[strlen(dir) - 1] == '/' ? "" : "/";
+                next_fn = xasprintf("%s%s%s", dir, separator, linkname);
+                free(linkname);
+            }
+
+            free(dir);
+        }
+
+        free(fn);
+        fn = next_fn;
+    }
+
+    VLOG_WARN("%s: too many levels of symlinks", filename);
+    free(fn);
+    return xstrdup(filename);
+}
 
 /* Pass a value to this function if it is marked with
  * __attribute__((warn_unused_result)) and you genuinely want to ignore
index e5d1c3a..57527fc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #define BUILD_ASSERT_DECL_GCCONLY(EXPR) ((void) 0)
 #endif
 
+/* Casts 'pointer' to 'type' and issues a compiler warning if the cast changes
+ * anything other than an outermost "const" or "volatile" qualifier.
+ *
+ * The cast to int is present only to suppress an "expression using sizeof
+ * bool" warning from "sparse" (see
+ * http://permalink.gmane.org/gmane.comp.parsers.sparse/2967). */
+#define CONST_CAST(TYPE, POINTER)                               \
+    ((void) sizeof ((int) ((POINTER) == (TYPE) (POINTER))),     \
+     (TYPE) (POINTER))
+
 extern const char *program_name;
+extern const char *subprogram_name;
 
 /* Returns the number of elements in ARRAY. */
 #define ARRAY_SIZE(ARRAY) (sizeof ARRAY / sizeof *ARRAY)
@@ -187,6 +198,8 @@ void ovs_strzcpy(char *dst, const char *src, size_t size);
 
 void ovs_abort(int err_no, const char *format, ...)
     PRINTF_FORMAT(2, 3) NO_RETURN;
+void ovs_abort_valist(int err_no, const char *format, va_list)
+    PRINTF_FORMAT(2, 0) NO_RETURN;
 void ovs_fatal(int err_no, const char *format, ...)
     PRINTF_FORMAT(2, 3) NO_RETURN;
 void ovs_fatal_valist(int err_no, const char *format, va_list)
@@ -216,6 +229,9 @@ char *dir_name(const char *file_name);
 char *base_name(const char *file_name);
 char *abs_file_name(const char *dir, const char *file_name);
 
+char *xreadlink(const char *filename);
+char *follow_symlinks(const char *filename);
+
 void ignore(bool x OVS_UNUSED);
 int log_2_floor(uint32_t);
 int log_2_ceil(uint32_t);
index 8b02bf5..cfaf0c5 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 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.
index e62e8fe..37e01d0 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010 Nicira Networks
+/* 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.
index 48c95b4..1c11e57 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index d6da5f0..54ec2e6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * 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.
@@ -22,6 +22,7 @@
 
 #include <assert.h>
 #include "vconn.h"
+#include "openflow/openflow-common.h"
 \f
 /* Active virtual connection to an OpenFlow device. */
 
@@ -32,8 +33,8 @@ struct vconn {
     struct vconn_class *class;
     int state;
     int error;
-    int min_version;
-    int version;
+    enum ofp_version min_version;
+    enum ofp_version version;
     ovs_be32 remote_ip;
     ovs_be16 remote_port;
     ovs_be32 local_ip;
index 03e5881..d707e06 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 4d04c83..9271f4f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
 #include "fatal-signal.h"
 #include "flow.h"
 #include "ofp-errors.h"
+#include "ofp-msgs.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
@@ -137,10 +138,10 @@ vconn_usage(bool active, bool passive, bool bootstrap OVS_UNUSED)
     printf("\n");
     if (active) {
         printf("Active OpenFlow connection methods:\n");
-        printf("  tcp:IP[:PORT]         "
+        printf("  tcp:IP[:PORT]           "
                "PORT (default: %d) at remote IP\n", OFP_TCP_PORT);
 #ifdef HAVE_OPENSSL
-        printf("  ssl:IP[:PORT]         "
+        printf("  ssl:IP[:PORT]           "
                "SSL PORT (default: %d) at remote IP\n", OFP_SSL_PORT);
 #endif
         printf("  unix:FILE               Unix domain socket named FILE\n");
@@ -261,6 +262,12 @@ error:
 void
 vconn_run(struct vconn *vconn)
 {
+    if (vconn->state == VCS_CONNECTING ||
+        vconn->state == VCS_SEND_HELLO ||
+        vconn->state == VCS_RECV_HELLO) {
+        vconn_connect(vconn);
+    }
+
     if (vconn->class->run) {
         (vconn->class->run)(vconn);
     }
@@ -271,13 +278,20 @@ vconn_run(struct vconn *vconn)
 void
 vconn_run_wait(struct vconn *vconn)
 {
+    if (vconn->state == VCS_CONNECTING ||
+        vconn->state == VCS_SEND_HELLO ||
+        vconn->state == VCS_RECV_HELLO) {
+        vconn_connect_wait(vconn);
+    }
+
     if (vconn->class->run_wait) {
         (vconn->class->run_wait)(vconn);
     }
 }
 
 int
-vconn_open_block(const char *name, int min_version, struct vconn **vconnp)
+vconn_open_block(const char *name, enum ofp_version min_version,
+                 struct vconn **vconnp)
 {
     struct vconn *vconn;
     int error;
@@ -286,13 +300,7 @@ vconn_open_block(const char *name, int min_version, struct vconn **vconnp)
 
     error = vconn_open(name, min_version, &vconn, DSCP_DEFAULT);
     if (!error) {
-        while ((error = vconn_connect(vconn)) == EAGAIN) {
-            vconn_run(vconn);
-            vconn_run_wait(vconn);
-            vconn_connect_wait(vconn);
-            poll_block();
-        }
-        assert(error != EINPROGRESS);
+        error = vconn_connect_block(vconn);
     }
 
     if (error) {
@@ -363,7 +371,7 @@ vconn_get_local_port(const struct vconn *vconn)
 int
 vconn_get_version(const struct vconn *vconn)
 {
-    return vconn->version;
+    return vconn->version ? vconn->version : -1;
 }
 
 static void
@@ -385,7 +393,7 @@ vcs_send_hello(struct vconn *vconn)
     struct ofpbuf *b;
     int retval;
 
-    make_openflow(sizeof(struct ofp_header), OFPT_HELLO, &b);
+    b = ofpraw_alloc(OFPRAW_OFPT_HELLO, OFP10_VERSION, 0);
     retval = do_send(vconn, b);
     if (!retval) {
         vconn->state = VCS_RECV_HELLO;
@@ -406,9 +414,12 @@ vcs_recv_hello(struct vconn *vconn)
 
     retval = do_recv(vconn, &b);
     if (!retval) {
-        struct ofp_header *oh = b->data;
+        const struct ofp_header *oh = b->data;
+        enum ofptype type;
+        enum ofperr error;
 
-        if (oh->type == OFPT_HELLO) {
+        error = ofptype_decode(&type, b->data);
+        if (!error && type == OFPTYPE_HELLO) {
             if (b->size > sizeof *oh) {
                 struct ds msg = DS_EMPTY_INITIALIZER;
                 ds_put_format(&msg, "%s: extra-long hello:\n", vconn->name);
@@ -462,9 +473,8 @@ vcs_send_error(struct vconn *vconn)
 
     snprintf(s, sizeof s, "We support versions 0x%02x to 0x%02x inclusive but "
              "you support no later than version 0x%02"PRIx8".",
-             vconn->min_version, OFP10_VERSION, vconn->version);
-    b = ofperr_encode_hello(OFPERR_OFPHFC_INCOMPATIBLE,
-                            ofperr_domain_from_version(vconn->version), s);
+             vconn->min_version, OFP12_VERSION, vconn->version);
+    b = ofperr_encode_hello(OFPERR_OFPHFC_INCOMPATIBLE, vconn->version, s);
     retval = do_send(vconn, b);
     if (retval) {
         ofpbuf_delete(b);
@@ -529,10 +539,33 @@ vconn_connect(struct vconn *vconn)
 int
 vconn_recv(struct vconn *vconn, struct ofpbuf **msgp)
 {
-    int retval = vconn_connect(vconn);
+    struct ofpbuf *msg;
+    int retval;
+
+    retval = vconn_connect(vconn);
+    if (!retval) {
+        retval = do_recv(vconn, &msg);
+    }
     if (!retval) {
-        retval = do_recv(vconn, msgp);
+        const struct ofp_header *oh = msg->data;
+        if (oh->version != vconn->version) {
+            enum ofptype type;
+
+            if (ofptype_decode(&type, msg->data)
+                || (type != OFPTYPE_HELLO &&
+                    type != OFPTYPE_ERROR &&
+                    type != OFPTYPE_ECHO_REQUEST &&
+                    type != OFPTYPE_ECHO_REPLY)) {
+                VLOG_ERR_RL(&bad_ofmsg_rl, "%s: received OpenFlow version "
+                            "0x%02"PRIx8" != expected %02x",
+                            vconn->name, oh->version, vconn->version);
+                ofpbuf_delete(msg);
+                retval = EPROTO;
+            }
+        }
     }
+
+    *msgp = retval ? NULL : msg;
     return retval;
 }
 
@@ -541,40 +574,12 @@ do_recv(struct vconn *vconn, struct ofpbuf **msgp)
 {
     int retval = (vconn->class->recv)(vconn, msgp);
     if (!retval) {
-        struct ofp_header *oh;
-
         COVERAGE_INC(vconn_received);
         if (VLOG_IS_DBG_ENABLED()) {
             char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
             VLOG_DBG_RL(&ofmsg_rl, "%s: received: %s", vconn->name, s);
             free(s);
         }
-
-        oh = ofpbuf_at_assert(*msgp, 0, sizeof *oh);
-        if ((oh->version != vconn->version || oh->version == 0)
-            && oh->type != OFPT_HELLO
-            && oh->type != OFPT_ERROR
-            && oh->type != OFPT_ECHO_REQUEST
-            && oh->type != OFPT_ECHO_REPLY
-            && oh->type != OFPT_VENDOR)
-        {
-            if (vconn->version == 0) {
-                VLOG_ERR_RL(&bad_ofmsg_rl,
-                            "%s: received OpenFlow message type %"PRIu8" "
-                            "before version negotiation complete",
-                            vconn->name, oh->type);
-            } else {
-                VLOG_ERR_RL(&bad_ofmsg_rl,
-                            "%s: received OpenFlow version 0x%02"PRIx8" "
-                            "!= expected %02x",
-                            vconn->name, oh->version, vconn->version);
-            }
-            ofpbuf_delete(*msgp);
-            retval = EPROTO;
-        }
-    }
-    if (retval) {
-        *msgp = NULL;
     }
     return retval;
 }
@@ -605,7 +610,8 @@ do_send(struct vconn *vconn, struct ofpbuf *msg)
     int retval;
 
     assert(msg->size >= sizeof(struct ofp_header));
-    assert(((struct ofp_header *) msg->data)->length == htons(msg->size));
+
+    ofpmsg_update_length(msg);
     if (!VLOG_IS_DBG_ENABLED()) {
         COVERAGE_INC(vconn_sent);
         retval = (vconn->class->send)(vconn, msg);
@@ -621,6 +627,24 @@ do_send(struct vconn *vconn, struct ofpbuf *msg)
     return retval;
 }
 
+/* Same as vconn_connect(), except that it waits until the connection on
+ * 'vconn' completes or fails.  Thus, it will never return EAGAIN. */
+int
+vconn_connect_block(struct vconn *vconn)
+{
+    int error;
+
+    while ((error = vconn_connect(vconn)) == EAGAIN) {
+        vconn_run(vconn);
+        vconn_run_wait(vconn);
+        vconn_connect_wait(vconn);
+        poll_block();
+    }
+    assert(error != EINPROGRESS);
+
+    return error;
+}
+
 /* Same as vconn_send, except that it waits until 'msg' can be transmitted. */
 int
 vconn_send_block(struct vconn *vconn, struct ofpbuf *msg)
@@ -743,7 +767,7 @@ vconn_transact_noreply(struct vconn *vconn, struct ofpbuf *request,
     }
 
     /* Send barrier. */
-    barrier = ofputil_encode_barrier_request();
+    barrier = ofputil_encode_barrier_request(vconn_get_version(vconn));
     barrier_xid = ((struct ofp_header *) barrier->data)->xid;
     error = vconn_send_block(vconn, barrier);
     if (error) {
index 5971327..1a0bc60 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
 
 #include <stdbool.h>
 #include "openvswitch/types.h"
+#include "openflow/openflow.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -54,7 +55,9 @@ int vconn_transact_multiple_noreply(struct vconn *, struct list *requests,
 void vconn_run(struct vconn *);
 void vconn_run_wait(struct vconn *);
 
-int vconn_open_block(const char *name, int min_version, struct vconn **);
+int vconn_open_block(const char *name, enum ofp_version min_version,
+                     struct vconn **);
+int vconn_connect_block(struct vconn *);
 int vconn_send_block(struct vconn *, struct ofpbuf *);
 int vconn_recv_block(struct vconn *, struct ofpbuf **);
 
index 4c5c1eb..9c205a2 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 Nicira Networks
+/* Copyright (c) 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.
index fed0ba1..3e2e820 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 Nicira Networks
+/* Copyright (c) 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.
index 736b779..ffb8e73 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
 #include <sys/stat.h>
 
 #include "hash.h"
-#include "rtnetlink-link.h"
 #include "shash.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(vlandev);
 
 #ifdef __linux__
+#include "rtnetlink-link.h"
 #include <linux/if_vlan.h>
 #include <linux/sockios.h>
 #include "netdev-linux.h"
index cba0fa4..ab74ecd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 4c97ef4..0bd9bd1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include "dirs.h"
 #include "dynamic-string.h"
+#include "ofpbuf.h"
 #include "sat-math.h"
 #include "svec.h"
 #include "timeval.h"
 #include "unixctl.h"
 #include "util.h"
+#include "worker.h"
 
 VLOG_DEFINE_THIS_MODULE(vlog);
 
@@ -84,7 +87,7 @@ static struct facility facilities[VLF_N_FACILITIES] = {
 
 /* VLF_FILE configuration. */
 static char *log_file_name;
-static FILE *log_file;
+static int log_fd = -1;
 
 /* vlog initialized? */
 static bool vlog_inited;
@@ -93,6 +96,8 @@ static void format_log_message(const struct vlog_module *, enum vlog_level,
                                enum vlog_facility, unsigned int msg_num,
                                const char *message, va_list, struct ds *)
     PRINTF_FORMAT(5, 0);
+static void vlog_write_file(struct ds *);
+static void vlog_update_async_log_fd(void);
 
 /* Searches the 'n_names' in 'names'.  Returns the index of a match for
  * 'target', or 'n_names' if no name matches. */
@@ -186,7 +191,7 @@ update_min_level(struct vlog_module *module)
 
     module->min_level = VLL_OFF;
     for (facility = 0; facility < VLF_N_FACILITIES; facility++) {
-        if (log_file || facility != VLF_FILE) {
+        if (log_fd >= 0 || facility != VLF_FILE) {
             enum vlog_level level = module->levels[facility];
             if (level > module->min_level) {
                 module->min_level = level;
@@ -279,10 +284,10 @@ vlog_set_log_file(const char *file_name)
     int error;
 
     /* Close old log file. */
-    if (log_file) {
+    if (log_fd >= 0) {
         VLOG_INFO("closing log file");
-        fclose(log_file);
-        log_file = NULL;
+        close(log_fd);
+        log_fd = -1;
     }
 
     /* Update log file name and free old name.  The ordering is important
@@ -296,13 +301,16 @@ vlog_set_log_file(const char *file_name)
 
     /* Open new log file and update min_levels[] to reflect whether we actually
      * have a log_file. */
-    log_file = fopen(log_file_name, "a");
+    log_fd = open(log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
+    if (log_fd >= 0) {
+        vlog_update_async_log_fd();
+    }
     for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
         update_min_level(*mp);
     }
 
     /* Log success or failure. */
-    if (!log_file) {
+    if (log_fd < 0) {
         VLOG_WARN("failed to open %s for logging: %s",
                   log_file_name, strerror(errno));
         error = errno;
@@ -330,8 +338,8 @@ vlog_reopen_log_file(void)
     /* Skip re-opening if it would be a no-op because the old and new files are
      * the same.  (This avoids writing "closing log file" followed immediately
      * by "opened log file".) */
-    if (log_file
-        && !fstat(fileno(log_file), &old)
+    if (log_fd >= 0
+        && !fstat(log_fd, &old)
         && !stat(log_file_name, &new)
         && old.st_dev == new.st_dev
         && old.st_ino == new.st_ino) {
@@ -477,6 +485,7 @@ vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED,
 void
 vlog_init(void)
 {
+    static char *program_name_copy;
     time_t now;
 
     if (vlog_inited) {
@@ -484,15 +493,21 @@ vlog_init(void)
     }
     vlog_inited = true;
 
-    openlog(program_name, LOG_NDELAY, LOG_DAEMON);
+    /* openlog() is allowed to keep the pointer passed in, without making a
+     * copy.  The daemonize code sometimes frees and replaces 'program_name',
+     * so make a private copy just for openlog().  (We keep a pointer to the
+     * private copy to suppress memory leak warnings in case openlog() does
+     * make its own copy.) */
+    program_name_copy = program_name ? xstrdup(program_name) : NULL;
+    openlog(program_name_copy, LOG_NDELAY, LOG_DAEMON);
 
     now = time_wall();
     if (now < 0) {
         struct tm tm;
         char s[128];
 
-        localtime_r(&now, &tm);
-        strftime(s, sizeof s, "%a, %d %b %Y %H:%M:%S %z", &tm);
+        gmtime_r(&now, &tm);
+        strftime(s, sizeof s, "%a, %d %b %Y %H:%M:%S", &tm);
         VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now);
     }
 
@@ -646,6 +661,14 @@ format_log_message(const struct vlog_module *module, enum vlog_level level,
         case 'r':
             ds_put_format(s, "%lld", time_msec() - time_boot_msec());
             break;
+        case 't':
+            ds_put_cstr(s, subprogram_name[0] ? subprogram_name : "main");
+            break;
+        case 'T':
+            if (subprogram_name[0]) {
+                ds_put_format(s, "(%s)", subprogram_name);
+            }
+            break;
         default:
             ds_put_char(s, p[-1]);
             break;
@@ -674,7 +697,7 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
 {
     bool log_to_console = module->levels[VLF_CONSOLE] >= level;
     bool log_to_syslog = module->levels[VLF_SYSLOG] >= level;
-    bool log_to_file = module->levels[VLF_FILE] >= level && log_file;
+    bool log_to_file = module->levels[VLF_FILE] >= level && log_fd >= 0;
     if (log_to_console || log_to_syslog || log_to_file) {
         int save_errno = errno;
         static unsigned int msg_num;
@@ -710,8 +733,7 @@ vlog_valist(const struct vlog_module *module, enum vlog_level level,
             format_log_message(module, level, VLF_FILE, msg_num,
                                message, args, &s);
             ds_put_char(&s, '\n');
-            fputs(ds_cstr(&s), log_file);
-            fflush(log_file);
+            vlog_write_file(&s);
         }
 
         ds_destroy(&s);
@@ -730,11 +752,17 @@ vlog(const struct vlog_module *module, enum vlog_level level,
     va_end(args);
 }
 
+/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure
+ * exit code.  Always writes the message to stderr, even if the console
+ * facility is disabled.
+ *
+ * Choose this function instead of vlog_abort_valist() if the daemon monitoring
+ * facility shouldn't automatically restart the current daemon.  */
 void
 vlog_fatal_valist(const struct vlog_module *module_,
                   const char *message, va_list args)
 {
-    struct vlog_module *module = (struct vlog_module *) module_;
+    struct vlog_module *module = CONST_CAST(struct vlog_module *, module_);
 
     /* Don't log this message to the console to avoid redundancy with the
      * message written by the later ovs_fatal_valist(). */
@@ -744,6 +772,12 @@ vlog_fatal_valist(const struct vlog_module *module_,
     ovs_fatal_valist(0, message, args);
 }
 
+/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure
+ * exit code.  Always writes the message to stderr, even if the console
+ * facility is disabled.
+ *
+ * Choose this function instead of vlog_abort() if the daemon monitoring
+ * facility shouldn't automatically restart the current daemon.  */
 void
 vlog_fatal(const struct vlog_module *module, const char *message, ...)
 {
@@ -754,6 +788,40 @@ vlog_fatal(const struct vlog_module *module, const char *message, ...)
     va_end(args);
 }
 
+/* Logs 'message' to 'module' at maximum verbosity, then calls abort().  Always
+ * writes the message to stderr, even if the console facility is disabled.
+ *
+ * Choose this function instead of vlog_fatal_valist() if the daemon monitoring
+ * facility should automatically restart the current daemon.  */
+void
+vlog_abort_valist(const struct vlog_module *module_,
+                  const char *message, va_list args)
+{
+    struct vlog_module *module = (struct vlog_module *) module_;
+
+    /* Don't log this message to the console to avoid redundancy with the
+     * message written by the later ovs_abort_valist(). */
+    module->levels[VLF_CONSOLE] = VLL_OFF;
+
+    vlog_valist(module, VLL_EMER, message, args);
+    ovs_abort_valist(0, message, args);
+}
+
+/* Logs 'message' to 'module' at maximum verbosity, then calls abort().  Always
+ * writes the message to stderr, even if the console facility is disabled.
+ *
+ * Choose this function instead of vlog_fatal() if the daemon monitoring
+ * facility should automatically restart the current daemon.  */
+void
+vlog_abort(const struct vlog_module *module, const char *message, ...)
+{
+    va_list args;
+
+    va_start(args, message);
+    vlog_abort_valist(module, message, args);
+    va_end(args);
+}
+
 bool
 vlog_should_drop(const struct vlog_module *module, enum vlog_level level,
                  struct vlog_rate_limit *rl)
@@ -762,28 +830,15 @@ vlog_should_drop(const struct vlog_module *module, enum vlog_level level,
         return true;
     }
 
-    if (rl->tokens < VLOG_MSG_TOKENS) {
+    if (!token_bucket_withdraw(&rl->token_bucket, VLOG_MSG_TOKENS)) {
         time_t now = time_now();
-        if (rl->last_fill > now) {
-            /* Last filled in the future?  Time must have gone backward, or
-             * 'rl' has not been used before. */
-            rl->tokens = rl->burst;
-        } else if (rl->last_fill < now) {
-            unsigned int add = sat_mul(rl->rate, now - rl->last_fill);
-            unsigned int tokens = sat_add(rl->tokens, add);
-            rl->tokens = MIN(tokens, rl->burst);
-            rl->last_fill = now;
-        }
-        if (rl->tokens < VLOG_MSG_TOKENS) {
-            if (!rl->n_dropped) {
-                rl->first_dropped = now;
-            }
-            rl->last_dropped = now;
-            rl->n_dropped++;
-            return true;
+        if (!rl->n_dropped) {
+            rl->first_dropped = now;
         }
+        rl->last_dropped = now;
+        rl->n_dropped++;
+        return true;
     }
-    rl->tokens -= VLOG_MSG_TOKENS;
 
     if (rl->n_dropped) {
         time_t now = time_now();
@@ -823,3 +878,46 @@ vlog_usage(void)
            "                          (default: %s/%s.log)\n",
            ovs_logdir(), program_name);
 }
+\f
+static bool vlog_async_inited = false;
+
+static worker_request_func vlog_async_write_request_cb;
+
+static void
+vlog_write_file(struct ds *s)
+{
+    if (worker_is_running()) {
+        worker_request(s->string, s->length,
+                       &log_fd, vlog_async_inited ? 0 : 1,
+                       vlog_async_write_request_cb, NULL, NULL);
+        vlog_async_inited = true;
+    } else {
+        ignore(write(log_fd, s->string, s->length));
+    }
+}
+
+static void
+vlog_update_async_log_fd(void)
+{
+    if (worker_is_running()) {
+        worker_request(NULL, 0, &log_fd, 1, vlog_async_write_request_cb,
+                       NULL, NULL);
+        vlog_async_inited = true;
+    }
+}
+
+static void
+vlog_async_write_request_cb(struct ofpbuf *request,
+                            const int *fd, size_t n_fds)
+{
+    if (n_fds > 0) {
+        if (log_fd >= 0) {
+            close(log_fd);
+        }
+        log_fd = *fd;
+    }
+
+    if (request->size > 0) {
+        ignore(write(log_fd, request->data, request->size));
+    }
+}
index 5954d21..9570b0e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@
 #include <stdbool.h>
 #include <time.h>
 #include "compiler.h"
+#include "sat-math.h"
+#include "token-bucket.h"
 #include "util.h"
 
 #ifdef  __cplusplus
@@ -49,10 +51,10 @@ const char *vlog_get_level_name(enum vlog_level);
 enum vlog_level vlog_get_level_val(const char *name);
 
 /* Facilities that we can log to. */
-#define VLOG_FACILITIES                                         \
-    VLOG_FACILITY(SYSLOG, "%05N|%c|%p|%m")                      \
-    VLOG_FACILITY(CONSOLE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c|%p|%m")  \
-    VLOG_FACILITY(FILE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c|%p|%m")
+#define VLOG_FACILITIES                                                 \
+    VLOG_FACILITY(SYSLOG, "%05N|%c%T|%p|%m")                            \
+    VLOG_FACILITY(CONSOLE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m")    \
+    VLOG_FACILITY(FILE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m")
 enum vlog_facility {
 #define VLOG_FACILITY(NAME, PATTERN) VLF_##NAME,
     VLOG_FACILITIES
@@ -87,35 +89,24 @@ struct vlog_module *vlog_module_from_name(const char *name);
 
 /* Rate-limiter for log messages. */
 struct vlog_rate_limit {
-    /* Configuration settings. */
-    unsigned int rate;          /* Tokens per second. */
-    unsigned int burst;         /* Max cumulative tokens credit. */
-
-    /* Current status. */
-    unsigned int tokens;        /* Current number of tokens. */
-    time_t last_fill;           /* Last time tokens added. */
+    struct token_bucket token_bucket;
     time_t first_dropped;       /* Time first message was dropped. */
     time_t last_dropped;        /* Time of most recent message drop. */
     unsigned int n_dropped;     /* Number of messages dropped. */
 };
 
-/* Number of tokens to emit a message.  We add 'rate' tokens per second, which
- * is 60 times the unit used for 'rate', thus 60 tokens are required to emit
- * one message. */
-#define VLOG_MSG_TOKENS 60
+/* Number of tokens to emit a message.  We add 'rate' tokens per millisecond,
+ * thus 60,000 tokens are required to emit one message per minute. */
+#define VLOG_MSG_TOKENS (60 * 1000)
 
 /* Initializer for a struct vlog_rate_limit, to set up a maximum rate of RATE
  * messages per minute and a maximum burst size of BURST messages. */
-#define VLOG_RATE_LIMIT_INIT(RATE, BURST)                   \
-        {                                                   \
-            RATE,                           /* rate */      \
-            (MIN(BURST, UINT_MAX / VLOG_MSG_TOKENS)         \
-             * VLOG_MSG_TOKENS),            /* burst */     \
-            0,                              /* tokens */    \
-            0,                              /* last_fill */ \
-            0,                              /* first_dropped */ \
-            0,                              /* last_dropped */ \
-            0,                              /* n_dropped */ \
+#define VLOG_RATE_LIMIT_INIT(RATE, BURST)                               \
+        {                                                               \
+            TOKEN_BUCKET_INIT(RATE, SAT_MUL(BURST, VLOG_MSG_TOKENS)),   \
+            0,                              /* first_dropped */         \
+            0,                              /* last_dropped */          \
+            0,                              /* n_dropped */             \
         }
 
 /* Configuring how each module logs messages. */
@@ -151,6 +142,11 @@ void vlog_fatal(const struct vlog_module *, const char *format, ...)
 void vlog_fatal_valist(const struct vlog_module *, const char *format, va_list)
     PRINTF_FORMAT (2, 0) NO_RETURN;
 
+void vlog_abort(const struct vlog_module *, const char *format, ...)
+    PRINTF_FORMAT (2, 3) NO_RETURN;
+void vlog_abort_valist(const struct vlog_module *, const char *format, va_list)
+    PRINTF_FORMAT (2, 0) NO_RETURN;
+
 void vlog_rate_limit(const struct vlog_module *, enum vlog_level,
                      struct vlog_rate_limit *, const char *, ...)
     PRINTF_FORMAT (4, 5);
@@ -169,6 +165,7 @@ void vlog_rate_limit(const struct vlog_module *, enum vlog_level,
  * Guaranteed to preserve errno.
  */
 #define VLOG_FATAL(...) vlog_fatal(THIS_MODULE, __VA_ARGS__)
+#define VLOG_ABORT(...) vlog_abort(THIS_MODULE, __VA_ARGS__)
 #define VLOG_EMER(...) VLOG(VLL_EMER, __VA_ARGS__)
 #define VLOG_ERR(...) VLOG(VLL_ERR, __VA_ARGS__)
 #define VLOG_WARN(...) VLOG(VLL_WARN, __VA_ARGS__)
index a8c8223..39edaee 100644 (file)
@@ -43,12 +43,17 @@ a word but has no effect.
 .IP "\fB\-v\fR"
 .IQ "\fB\-\-verbose\fR"
 Sets the maximum logging verbosity level, equivalent to
-\fB\-\-verbose=ANY:ANY:dbg\fR.
+\fB\-\-verbose=dbg\fR.
 .
+.\" Python vlog doesn't implement -vPATTERN so only document it if
+.\" \*(PY is empty:
+.ie dPY
+.el \{
 .IP "\fB\-vPATTERN:\fIfacility\fB:\fIpattern\fR"
 .IQ "\fB\-\-verbose=PATTERN:\fIfacility\fB:\fIpattern\fR"
 Sets the log pattern for \fIfacility\fR to \fIpattern\fR.  Refer to
 \fBovs\-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
+\}
 .
 .TP
 \fB\-\-log\-file\fR[\fB=\fIfile\fR]
diff --git a/lib/worker.c b/lib/worker.c
new file mode 100644 (file)
index 0000000..bc44885
--- /dev/null
@@ -0,0 +1,447 @@
+/* Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "worker.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "command-line.h"
+#include "daemon.h"
+#include "ofpbuf.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "util.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(worker);
+
+/* Header for an RPC request. */
+struct worker_request {
+    size_t request_len;              /* Length of the payload in bytes. */
+    worker_request_func *request_cb; /* Function to call in worker process. */
+    worker_reply_func *reply_cb;     /* Function to call in main process. */
+    void *reply_aux;                 /* Auxiliary data for 'reply_cb'. */
+};
+
+/* Header for an RPC reply. */
+struct worker_reply {
+    size_t reply_len;            /* Length of the payload in bytes. */
+    worker_reply_func *reply_cb; /* Function to call in main process. */
+    void *reply_aux;             /* Auxiliary data for 'reply_cb'. */
+};
+
+/* Receive buffer for a RPC request or reply. */
+struct rxbuf {
+    /* Header. */
+    struct ofpbuf header;       /* Header data. */
+    int fds[SOUTIL_MAX_FDS];    /* File descriptors. */
+    size_t n_fds;
+
+    /* Payload. */
+    struct ofpbuf payload;      /* Payload data. */
+};
+
+static int client_sock = -1;
+static struct rxbuf client_rx;
+
+static void rxbuf_init(struct rxbuf *);
+static void rxbuf_clear(struct rxbuf *);
+static int rxbuf_run(struct rxbuf *, int sock, size_t header_len);
+
+static struct iovec *prefix_iov(void *data, size_t len,
+                                const struct iovec *iovs, size_t n_iovs);
+
+static void worker_broke(void);
+
+static void worker_main(int fd) NO_RETURN;
+
+/* Starts a worker process as a subprocess of the current process.  Currently
+ * only a single worker process is supported, so this function may only be
+ * called once.
+ *
+ * The client should call worker_run() and worker_wait() from its main loop.
+ *
+ * Call this function between daemonize_start() and daemonize_complete(). */
+void
+worker_start(void)
+{
+    int work_fds[2];
+
+    assert(client_sock < 0);
+
+    /* Create non-blocking socket pair. */
+    xsocketpair(AF_UNIX, SOCK_STREAM, 0, work_fds);
+    xset_nonblocking(work_fds[0]);
+    xset_nonblocking(work_fds[1]);
+
+    if (!fork_and_clean_up()) {
+        /* In child (worker) process. */
+        daemonize_post_detach();
+        close(work_fds[0]);
+        worker_main(work_fds[1]);
+        NOT_REACHED();
+    }
+
+    /* In parent (main) process. */
+    close(work_fds[1]);
+    client_sock = work_fds[0];
+    rxbuf_init(&client_rx);
+}
+
+/* Returns true if this process has started a worker and the worker is not
+ * known to have malfunctioned. */
+bool
+worker_is_running(void)
+{
+    return client_sock >= 0;
+}
+
+/* If a worker process was started, processes RPC replies from it, calling the
+ * registered 'reply_cb' callbacks.
+ *
+ * If the worker process died or malfunctioned, aborts. */
+void
+worker_run(void)
+{
+    if (worker_is_running()) {
+        int error;
+
+        error = rxbuf_run(&client_rx, client_sock,
+                          sizeof(struct worker_reply));
+        if (!error) {
+            struct worker_reply *reply = client_rx.header.data;
+            reply->reply_cb(&client_rx.payload, client_rx.fds,
+                            client_rx.n_fds, reply->reply_aux);
+            rxbuf_clear(&client_rx);
+        } else if (error != EAGAIN) {
+            worker_broke();
+            VLOG_ABORT("receive from worker failed (%s)",
+                       ovs_retval_to_string(error));
+        }
+    }
+}
+
+/* Causes the poll loop to wake up if we need to process RPC replies. */
+void
+worker_wait(void)
+{
+    if (worker_is_running()) {
+        poll_fd_wait(client_sock, POLLIN);
+    }
+}
+\f
+/* Interface for main process to interact with the worker. */
+
+/* Sends an RPC request to the worker process.  The worker process will call
+ * 'request_cb' passing the 'size' (zero or more) bytes of data in 'data' as
+ * arguments as well as the 'n_fds' (SOUTIL_MAX_FDS or fewer) file descriptors
+ * in 'fds'.
+ *
+ * If and only if 'reply_cb' is nonnull, 'request_cb' must call worker_reply()
+ * or worker_reply_iovec() with a reply.  The main process will later call
+ * 'reply_cb' with the reply data (if any) and file descriptors (if any).
+ *
+ * 'request_cb' receives copies (as if by dup()) of the file descriptors in
+ * fds[].  'request_cb' takes ownership of these copies, and the caller of
+ * worker_request() retains its ownership of the originals.
+ *
+ * This function may block until the RPC request has been sent (if the socket
+ * buffer fills up) but it does not wait for the reply (if any).  If this
+ * function blocks, it may invoke reply callbacks for previous requests.
+ *
+ * The worker process executes RPC requests in strict order of submission and
+ * runs each request to completion before beginning the next request.  The main
+ * process invokes reply callbacks in strict order of request submission. */
+void
+worker_request(const void *data, size_t size,
+               const int fds[], size_t n_fds,
+               worker_request_func *request_cb,
+               worker_reply_func *reply_cb, void *aux)
+{
+    if (size > 0) {
+        struct iovec iov;
+
+        iov.iov_base = (void *) data;
+        iov.iov_len = size;
+        worker_request_iovec(&iov, 1, fds, n_fds, request_cb, reply_cb, aux);
+    } else {
+        worker_request_iovec(NULL, 0, fds, n_fds, request_cb, reply_cb, aux);
+    }
+}
+
+static int
+worker_send_iovec(const struct iovec iovs[], size_t n_iovs,
+                  const int fds[], size_t n_fds)
+{
+    size_t sent = 0;
+
+    for (;;) {
+        int error;
+
+        /* Try to send the rest of the request. */
+        error = send_iovec_and_fds_fully(client_sock, iovs, n_iovs,
+                                         fds, n_fds, sent, &sent);
+        if (error != EAGAIN) {
+            return error;
+        }
+
+        /* Process replies to avoid deadlock. */
+        worker_run();
+
+        poll_fd_wait(client_sock, POLLIN | POLLOUT);
+        poll_block();
+    }
+}
+
+/* Same as worker_request() except that the data to send is specified as an
+ * array of iovecs. */
+void
+worker_request_iovec(const struct iovec iovs[], size_t n_iovs,
+                     const int fds[], size_t n_fds,
+                     worker_request_func *request_cb,
+                     worker_reply_func *reply_cb, void *aux)
+{
+    struct worker_request rq;
+    struct iovec *all_iovs;
+    int error;
+
+    assert(worker_is_running());
+
+    rq.request_len = iovec_len(iovs, n_iovs);
+    rq.request_cb = request_cb;
+    rq.reply_cb = reply_cb;
+    rq.reply_aux = aux;
+
+    all_iovs = prefix_iov(&rq, sizeof rq, iovs, 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));
+    }
+    free(all_iovs);
+}
+
+/* Closes the client socket, if any, so that worker_is_running() will return
+ * false.
+ *
+ * The client does this just before aborting if the worker process dies or
+ * malfunctions, to prevent the logging subsystem from trying to use the
+ * worker to log the failure. */
+static void
+worker_broke(void)
+{
+    if (client_sock >= 0) {
+        close(client_sock);
+        client_sock = -1;
+    }
+}
+\f
+/* Interfaces for RPC implementations (running in the worker process). */
+
+static int server_sock = -1;
+static bool expect_reply;
+static struct worker_request request;
+
+/* When a call to worker_request() or worker_request_iovec() provides a
+ * 'reply_cb' callback, the 'request_cb' implementation must call this function
+ * to send its reply.  The main process will call 'reply_cb' passing the
+ * 'size' (zero or more) bytes of data in 'data' as arguments as well as the
+ * 'n_fds' (SOUTIL_MAX_FDS or fewer) file descriptors in 'fds'.
+ *
+ * If a call to worker_request() or worker_request_iovec() provides no
+ * 'reply_cb' callback, the 'request_cb' implementation must not call this
+ * function.
+ *
+ * 'reply_cb' receives copies (as if by dup()) of the file descriptors in
+ * fds[].  'reply_cb' takes ownership of these copies, and the caller of
+ * worker_reply() retains its ownership of the originals.
+ *
+ * This function blocks until the RPC reply has been sent (if the socket buffer
+ * fills up) but it does not wait for the main process to receive or to process
+ * the reply. */
+void
+worker_reply(const void *data, size_t size, const int fds[], size_t n_fds)
+{
+    if (size > 0) {
+        struct iovec iov;
+
+        iov.iov_base = (void *) data;
+        iov.iov_len = size;
+        worker_reply_iovec(&iov, 1, fds, n_fds);
+    } else {
+        worker_reply_iovec(NULL, 0, fds, n_fds);
+    }
+}
+
+/* Same as worker_reply() except that the data to send is specified as an array
+ * of iovecs. */
+void
+worker_reply_iovec(const struct iovec *iovs, size_t n_iovs,
+                       const int fds[], size_t n_fds)
+{
+    struct worker_reply reply;
+    struct iovec *all_iovs;
+    int error;
+
+    assert(expect_reply);
+    expect_reply = false;
+
+    reply.reply_len = iovec_len(iovs, n_iovs);
+    reply.reply_cb = request.reply_cb;
+    reply.reply_aux = request.reply_aux;
+
+    all_iovs = prefix_iov(&reply, sizeof reply, iovs, n_iovs);
+
+    error = send_iovec_and_fds_fully_block(server_sock, all_iovs, n_iovs + 1,
+                                           fds, n_fds);
+    if (error == EPIPE) {
+        /* Parent probably died.  Continue processing any RPCs still buffered,
+         * to avoid missing log messages. */
+        VLOG_INFO("send failed (%s)", strerror(error));
+    } else if (error) {
+        VLOG_ABORT("send failed (%s)", strerror(error));
+    }
+
+    free(all_iovs);
+}
+
+static void
+worker_main(int fd)
+{
+    struct rxbuf rx;
+
+    server_sock = fd;
+
+    subprogram_name = "worker";
+    proctitle_set("%s: worker process for pid %lu",
+                  program_name, (unsigned long int) getppid());
+    VLOG_INFO("worker process started");
+
+    rxbuf_init(&rx);
+    for (;;) {
+        int error;
+
+        error = rxbuf_run(&rx, server_sock, sizeof(struct worker_request));
+        if (!error) {
+            request = *(struct worker_request *) rx.header.data;
+
+            expect_reply = request.reply_cb != NULL;
+            request.request_cb(&rx.payload, rx.fds, rx.n_fds);
+            assert(!expect_reply);
+
+            rxbuf_clear(&rx);
+        } else if (error == EOF && !rx.header.size) {
+            /* Main process closed the IPC socket.  Exit cleanly. */
+            break;
+        } else if (error != EAGAIN) {
+            VLOG_ABORT("RPC receive failed (%s)", strerror(error));
+        }
+
+        poll_fd_wait(server_sock, POLLIN);
+        poll_block();
+    }
+
+    VLOG_INFO("worker process exiting");
+    exit(0);
+}
+\f
+static void
+rxbuf_init(struct rxbuf *rx)
+{
+    ofpbuf_init(&rx->header, 0);
+    rx->n_fds = 0;
+    ofpbuf_init(&rx->payload, 0);
+}
+
+static void
+rxbuf_clear(struct rxbuf *rx)
+{
+    ofpbuf_clear(&rx->header);
+    rx->n_fds = 0;
+    ofpbuf_clear(&rx->payload);
+}
+
+static int
+rxbuf_run(struct rxbuf *rx, int sock, size_t header_len)
+{
+    for (;;) {
+        if (!rx->header.size) {
+            int retval;
+
+            ofpbuf_clear(&rx->header);
+            ofpbuf_prealloc_tailroom(&rx->header, header_len);
+
+            retval = recv_data_and_fds(sock, rx->header.data, header_len,
+                                       rx->fds, &rx->n_fds);
+            if (retval <= 0) {
+                return retval ? -retval : EOF;
+            }
+            rx->header.size += retval;
+        } else if (rx->header.size < header_len) {
+            size_t bytes_read;
+            int error;
+
+            error = read_fully(sock, ofpbuf_tail(&rx->header),
+                               header_len - rx->header.size, &bytes_read);
+            rx->header.size += bytes_read;
+            if (error) {
+                return error;
+            }
+        } else {
+            size_t payload_len = *(size_t *) rx->header.data;
+
+            if (rx->payload.size < payload_len) {
+                size_t left = payload_len - rx->payload.size;
+                size_t bytes_read;
+                int error;
+
+                ofpbuf_prealloc_tailroom(&rx->payload, left);
+                error = read_fully(sock, ofpbuf_tail(&rx->payload), left,
+                                   &bytes_read);
+                rx->payload.size += bytes_read;
+                if (error) {
+                    return error;
+                }
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    return EAGAIN;
+}
+
+static struct iovec *
+prefix_iov(void *data, size_t len, const struct iovec *iovs, size_t n_iovs)
+{
+    struct iovec *dst;
+
+    dst = xmalloc((n_iovs + 1) * sizeof *dst);
+    dst[0].iov_base = data;
+    dst[0].iov_len = len;
+    memcpy(dst + 1, iovs, n_iovs * sizeof *iovs);
+
+    return dst;
+}
diff --git a/lib/worker.h b/lib/worker.h
new file mode 100644 (file)
index 0000000..135d50d
--- /dev/null
@@ -0,0 +1,68 @@
+/* Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WORKER_H
+#define WORKER_H 1
+
+/* Worker processes.
+ *
+ * Thes functions allow an OVS daemon to fork off a "worker process" to do
+ * tasks that may unavoidably block in the kernel.  The worker executes remote
+ * procedure calls on behalf of the main process.
+ *
+ * Tasks that may unavoidably block in the kernel include writes to regular
+ * files, sends to Generic Netlink sockets (which as of this writing use a
+ * global lock), and other unusual operations.
+ *
+ * The worker functions *will* block if the finite buffer between a main
+ * process and its worker process fills up.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "compiler.h"
+
+struct iovec;
+struct ofpbuf;
+
+/* The main process calls this function to start a worker. */
+void worker_start(void);
+
+/* Interface for main process to interact with the worker. */
+typedef void worker_request_func(struct ofpbuf *request,
+                                 const int fds[], size_t n_fds);
+typedef void worker_reply_func(struct ofpbuf *reply,
+                               const int fds[], size_t n_fds, void *aux);
+
+bool worker_is_running(void);
+void worker_run(void);
+void worker_wait(void);
+
+void worker_request(const void *data, size_t size,
+                    const int fds[], size_t n_fds,
+                    worker_request_func *request_cb,
+                    worker_reply_func *reply_cb, void *aux);
+void worker_request_iovec(const struct iovec *iovs, size_t n_iovs,
+                          const int fds[], size_t n_fds,
+                          worker_request_func *request_cb,
+                          worker_reply_func *reply_cb, void *aux);
+
+/* Interfaces for RPC implementations (running in the worker process). */
+void worker_reply(const void *data, size_t size,
+                  const int fds[], size_t n_fds);
+void worker_reply_iovec(const struct iovec *iovs, size_t n_iovs,
+                        const int fds[], size_t n_fds);
+
+#endif /* worker.h */
index d4f8fa6..939f296 100644 (file)
@@ -1,6 +1,6 @@
 # -*- autoconf -*-
 
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ AC_DEFUN([OVS_CHECK_COVERAGE],
      [AC_HELP_STRING([--enable-coverage], 
                      [Enable gcov coverage tool.])],
      [case "${enableval}" in
-        (lcov|yes) coverage=true ;;
+        (yes) coverage=true ;;
         (no)  coverage=false ;;
         (*) AC_MSG_ERROR([bad value ${enableval} for --enable-coverage]) ;;
       esac],
@@ -132,6 +132,16 @@ AC_DEFUN([OVS_CHECK_LOGDIR],
      [LOGDIR='${localstatedir}/log/${PACKAGE}'])
    AC_SUBST([LOGDIR])])
 
+dnl Checks for the directory in which to store the Open vSwitch database.
+AC_DEFUN([OVS_CHECK_DBDIR],
+  [AC_ARG_WITH(
+     [dbdir],
+     AC_HELP_STRING([--with-dbdir=DIR],
+                    [directory used for conf.db [[SYSCONFDIR/PACKAGE]]]),
+     [DBDIR=$withval],
+     [DBDIR='${sysconfdir}/${PACKAGE}'])
+   AC_SUBST([DBDIR])])
+
 dnl Defines HAVE_BACKTRACE if backtrace() is declared in <execinfo.h>
 dnl and exists in libc.
 AC_DEFUN([OVS_CHECK_BACKTRACE],
index 1773263..01700c3 100644 (file)
@@ -37,6 +37,7 @@ ovsdb/ovsdb-server.1: \
        lib/coverage-unixctl.man \
        lib/daemon-syn.man \
        lib/daemon.man \
+       lib/memory-unixctl.man \
        lib/ssl-bootstrap-syn.man \
        lib/ssl-bootstrap.man \
        lib/ssl-syn.man \
@@ -55,6 +56,7 @@ lib/common.man:
 lib/coverage-unixctl.man:
 lib/daemon-syn.man:
 lib/daemon.man:
+lib/memory-unixctl.man:
 lib/ssl-bootstrap-syn.man:
 lib/ssl-bootstrap.man:
 lib/ssl-syn.man:
@@ -120,6 +122,14 @@ utilities/ovs-dpctl.8.in:
 lib/common.man:
 lib/vlog.man:
 
+utilities/ovs-l3ping.8: \
+       utilities/ovs-l3ping.8.in \
+       lib/common-syn.man \
+       lib/common.man
+utilities/ovs-l3ping.8.in:
+lib/common-syn.man:
+lib/common.man:
+
 utilities/ovs-ofctl.8: \
        utilities/ovs-ofctl.8.in \
        lib/common.man \
@@ -225,6 +235,7 @@ vswitchd/ovs-vswitchd.8: \
        lib/common.man \
        lib/daemon.man \
        lib/leak-checker.man \
+       lib/memory-unixctl.man \
        lib/ssl-bootstrap.man \
        lib/ssl.man \
        lib/stress-unixctl.man \
@@ -237,6 +248,7 @@ vswitchd/ovs-vswitchd.8.in:
 lib/common.man:
 lib/daemon.man:
 lib/leak-checker.man:
+lib/memory-unixctl.man:
 lib/ssl-bootstrap.man:
 lib/ssl.man:
 lib/stress-unixctl.man:
index 6d98de7..ab889af 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
index 158b26d..14572db 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 47b148b..6529b8d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011 Nicira Networks.
+ * Copyright (c) 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.
index 281fdd3..aaf9b3e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,6 +25,8 @@
 #include "fail-open.h"
 #include "in-band.h"
 #include "odp-util.h"
+#include "ofp-actions.h"
+#include "ofp-msgs.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "ofproto-provider.h"
@@ -33,6 +35,7 @@
 #include "pktbuf.h"
 #include "rconn.h"
 #include "shash.h"
+#include "simap.h"
 #include "stream.h"
 #include "timeval.h"
 #include "vconn.h"
@@ -86,6 +89,13 @@ struct ofconn {
      * that the message might be generated, a 0-bit disables it. */
     uint32_t master_async_config[OAM_N_TYPES]; /* master, other */
     uint32_t slave_async_config[OAM_N_TYPES];  /* slave */
+
+    /* Flow monitors. */
+    struct hmap monitors;       /* Contains "struct ofmonitor"s. */
+    struct list updates;        /* List of "struct ofpbuf"s. */
+    bool sent_abbrev_update;    /* Does 'updates' contain NXFME_ABBREV? */
+    struct rconn_packet_counter *monitor_counter;
+    uint64_t monitor_paused;
 };
 
 static struct ofconn *ofconn_create(struct connmgr *, struct rconn *,
@@ -160,6 +170,8 @@ struct connmgr {
 
 static void update_in_band_remotes(struct connmgr *);
 static void add_snooper(struct connmgr *, struct vconn *);
+static void ofmonitor_run(struct connmgr *);
+static void ofmonitor_wait(struct connmgr *);
 
 /* Creates and returns a new connection manager owned by 'ofproto'.  'name' is
  * a name for the ofproto suitable for using in log messages.
@@ -265,6 +277,7 @@ connmgr_run(struct connmgr *mgr,
     LIST_FOR_EACH_SAFE (ofconn, next_ofconn, node, &mgr->all_conns) {
         ofconn_run(ofconn, handle_openflow);
     }
+    ofmonitor_run(mgr);
 
     /* Fail-open maintenance.  Do this after processing the ofconns since
      * fail-open checks the status of the controller rconn. */
@@ -324,6 +337,7 @@ connmgr_wait(struct connmgr *mgr, bool handling_openflow)
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
         ofconn_wait(ofconn, handling_openflow);
     }
+    ofmonitor_wait(mgr);
     if (handling_openflow && mgr->in_band) {
         in_band_wait(mgr->in_band);
     }
@@ -338,6 +352,30 @@ connmgr_wait(struct connmgr *mgr, bool handling_openflow)
     }
 }
 
+/* Adds some memory usage statistics for 'mgr' into 'usage', for use with
+ * memory_report(). */
+void
+connmgr_get_memory_usage(const struct connmgr *mgr, struct simap *usage)
+{
+    const struct ofconn *ofconn;
+    unsigned int packets = 0;
+    unsigned int ofconns = 0;
+
+    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+        int i;
+
+        ofconns++;
+
+        packets += rconn_count_txqlen(ofconn->rconn);
+        for (i = 0; i < N_SCHEDULERS; i++) {
+            packets += pinsched_count_txqlen(ofconn->schedulers[i]);
+        }
+        packets += pktbuf_count_packets(ofconn->pktbuf);
+    }
+    simap_increase(usage, "ofconns", ofconns);
+    simap_increase(usage, "packets", packets);
+}
+
 /* Returns the ofproto that owns 'ofconn''s connmgr. */
 struct ofproto *
 ofconn_get_ofproto(const struct ofconn *ofconn)
@@ -439,7 +477,7 @@ connmgr_free_controller_info(struct shash *info)
     SHASH_FOR_EACH (node, info) {
         struct ofproto_controller_info *cinfo = node->data;
         while (cinfo->pairs.n) {
-            free((char *) cinfo->pairs.values[--cinfo->pairs.n]);
+            free(CONST_CAST(char *, cinfo->pairs.values[--cinfo->pairs.n]));
         }
         free(cinfo);
     }
@@ -467,10 +505,14 @@ connmgr_set_controllers(struct connmgr *mgr,
 
         if (!vconn_verify_name(c->target)) {
             if (!find_controller_by_target(mgr, c->target)) {
+                VLOG_INFO("%s: added primary controller \"%s\"",
+                          mgr->name, c->target);
                 add_controller(mgr, c->target, c->dscp);
             }
         } else if (!pvconn_verify_name(c->target)) {
             if (!ofservice_lookup(mgr, c->target)) {
+                VLOG_INFO("%s: added service controller \"%s\"",
+                          mgr->name, c->target);
                 ofservice_create(mgr, c->target, c->dscp);
             }
         } else {
@@ -485,10 +527,13 @@ connmgr_set_controllers(struct connmgr *mgr,
     /* Delete controllers that are no longer configured.
      * Update configuration of all now-existing controllers. */
     HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, hmap_node, &mgr->controllers) {
+        const char *target = ofconn_get_target(ofconn);
         struct ofproto_controller *c;
 
-        c = shash_find_data(&new_controllers, ofconn_get_target(ofconn));
+        c = shash_find_data(&new_controllers, target);
         if (!c) {
+            VLOG_INFO("%s: removed primary controller \"%s\"",
+                      mgr->name, target);
             ofconn_destroy(ofconn);
         } else {
             ofconn_reconfigure(ofconn, c);
@@ -498,11 +543,13 @@ connmgr_set_controllers(struct connmgr *mgr,
     /* Delete services that are no longer configured.
      * Update configuration of all now-existing services. */
     HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, node, &mgr->services) {
+        const char *target = pvconn_get_name(ofservice->pvconn);
         struct ofproto_controller *c;
 
-        c = shash_find_data(&new_controllers,
-                            pvconn_get_name(ofservice->pvconn));
+        c = shash_find_data(&new_controllers, target);
         if (!c) {
+            VLOG_INFO("%s: removed service controller \"%s\"",
+                      mgr->name, target);
             ofservice_destroy(mgr, ofservice);
         } else {
             ofservice_reconfigure(ofservice, c);
@@ -895,15 +942,14 @@ ofconn_send_error(const struct ofconn *ofconn,
         static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(10, 10);
 
         if (!VLOG_DROP_INFO(&err_rl)) {
-            const struct ofputil_msg_type *type;
             const char *type_name;
             size_t request_len;
+            enum ofpraw raw;
 
             request_len = ntohs(request->length);
-            type_name = (!ofputil_decode_msg_type_partial(request,
-                                                          MIN(64, request_len),
-                                                          &type)
-                         ? ofputil_msg_type_name(type)
+            type_name = (!ofpraw_decode_partial(&raw, request,
+                                                MIN(64, request_len))
+                         ? ofpraw_get_name(raw)
                          : "invalid");
 
             VLOG_INFO("%s: sending %s error reply to %s message",
@@ -967,6 +1013,9 @@ ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type,
 
     list_init(&ofconn->opgroups);
 
+    hmap_init(&ofconn->monitors);
+    list_init(&ofconn->updates);
+
     ofconn_flush(ofconn);
 
     return ofconn;
@@ -977,6 +1026,7 @@ ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type,
 static void
 ofconn_flush(struct ofconn *ofconn)
 {
+    struct ofmonitor *monitor, *next_monitor;
     int i;
 
     ofconn->role = NX_ROLE_OTHER;
@@ -1045,6 +1095,14 @@ ofconn_flush(struct ofconn *ofconn)
         memset(ofconn->slave_async_config, 0,
                sizeof ofconn->slave_async_config);
     }
+
+    HMAP_FOR_EACH_SAFE (monitor, next_monitor, ofconn_node,
+                        &ofconn->monitors) {
+        ofmonitor_destroy(monitor);
+    }
+    rconn_packet_counter_destroy(ofconn->monitor_counter);
+    ofconn->monitor_counter = rconn_packet_counter_create();
+    ofpbuf_list_delete(&ofconn->updates); /* ...but it should be empty. */
 }
 
 static void
@@ -1061,6 +1119,7 @@ ofconn_destroy(struct ofconn *ofconn)
     rconn_packet_counter_destroy(ofconn->packet_in_counter);
     rconn_packet_counter_destroy(ofconn->reply_counter);
     pktbuf_destroy(ofconn->pktbuf);
+    rconn_packet_counter_destroy(ofconn->monitor_counter);
     free(ofconn);
 }
 
@@ -1080,6 +1139,12 @@ ofconn_reconfigure(struct ofconn *ofconn, const struct ofproto_controller *c)
     rconn_set_probe_interval(ofconn->rconn, probe_interval);
 
     ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit);
+
+    /* If dscp value changed reconnect. */
+    if (c->dscp != rconn_get_dscp(ofconn->rconn)) {
+        rconn_set_dscp(ofconn->rconn, c->dscp);
+        rconn_reconnect(ofconn->rconn);
+    }
 }
 
 /* Returns true if it makes sense for 'ofconn' to receive and process OpenFlow
@@ -1087,7 +1152,7 @@ ofconn_reconfigure(struct ofconn *ofconn, const struct ofproto_controller *c)
 static bool
 ofconn_may_recv(const struct ofconn *ofconn)
 {
-    int count = rconn_packet_counter_read (ofconn->reply_counter);
+    int count = ofconn->reply_counter->n_packets;
     return (!ofconn->blocked || ofconn->retry) && count < OFCONN_REPLY_MAX;
 }
 
@@ -1224,7 +1289,7 @@ static void
 ofconn_send(const struct ofconn *ofconn, struct ofpbuf *msg,
             struct rconn_packet_counter *counter)
 {
-    update_openflow_length(msg);
+    ofpmsg_update_length(msg);
     rconn_send(ofconn->rconn, msg, counter);
 }
 \f
@@ -1331,7 +1396,7 @@ schedule_packet_in(struct ofconn *ofconn, struct ofputil_packet_in pin)
         pin.send_len = pin.packet_len;
     } else {
         /* Caller should have initialized 'send_len' to 'max_len' specified in
-         * struct ofp_action_output. */
+         * output action. */
     }
     if (pin.buffer_id != UINT32_MAX) {
         pin.send_len = MIN(pin.send_len, ofconn->miss_send_len);
@@ -1342,7 +1407,8 @@ schedule_packet_in(struct ofconn *ofconn, struct ofputil_packet_in pin)
      * while (until a later call to pinsched_run()). */
     pinsched_send(ofconn->schedulers[pin.reason == OFPR_NO_MATCH ? 0 : 1],
                   pin.fmd.in_port,
-                  ofputil_encode_packet_in(&pin, ofconn->packet_in_format),
+                  ofputil_encode_packet_in(&pin, ofconn->protocol,
+                                           ofconn->packet_in_format),
                   do_send_packet_in, ofconn);
 }
 \f
@@ -1533,15 +1599,17 @@ connmgr_flushed(struct connmgr *mgr)
      * traffic until a controller has been defined and it tells us to do so. */
     if (!connmgr_has_controllers(mgr)
         && mgr->fail_mode == OFPROTO_FAIL_STANDALONE) {
-        union ofp_action action;
+        struct ofpbuf ofpacts;
         struct cls_rule rule;
 
-        memset(&action, 0, sizeof action);
-        action.type = htons(OFPAT10_OUTPUT);
-        action.output.len = htons(sizeof action);
-        action.output.port = htons(OFPP_NORMAL);
+        ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
+        ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
+        ofpact_pad(&ofpacts);
+
         cls_rule_init_catchall(&rule, 0);
-        ofproto_add_flow(mgr->ofproto, &rule, &action, 1);
+        ofproto_add_flow(mgr->ofproto, &rule, ofpacts.data, ofpacts.size);
+
+        ofpbuf_uninit(&ofpacts);
     }
 }
 \f
@@ -1603,3 +1671,239 @@ ofservice_lookup(struct connmgr *mgr, const char *target)
     }
     return NULL;
 }
+\f
+/* Flow monitors (NXST_FLOW_MONITOR). */
+
+/* A counter incremented when something significant happens to an OpenFlow
+ * rule.
+ *
+ *     - When a rule is added, its 'add_seqno' and 'modify_seqno' are set to
+ *       the current value (which is then incremented).
+ *
+ *     - When a rule is modified, its 'modify_seqno' is set to the current
+ *       value (which is then incremented).
+ *
+ * Thus, by comparing an old value of monitor_seqno against a rule's
+ * 'add_seqno', one can tell whether the rule was added before or after the old
+ * value was read, and similarly for 'modify_seqno'.
+ *
+ * 32 bits should normally be sufficient (and would be nice, to save space in
+ * each rule) but then we'd have to have some special cases for wraparound.
+ *
+ * We initialize monitor_seqno to 1 to allow 0 to be used as an invalid
+ * value. */
+static uint64_t monitor_seqno = 1;
+
+COVERAGE_DEFINE(ofmonitor_pause);
+COVERAGE_DEFINE(ofmonitor_resume);
+
+enum ofperr
+ofmonitor_create(const struct ofputil_flow_monitor_request *request,
+                 struct ofconn *ofconn, struct ofmonitor **monitorp)
+{
+    struct ofmonitor *m;
+
+    *monitorp = NULL;
+
+    m = ofmonitor_lookup(ofconn, request->id);
+    if (m) {
+        return OFPERR_NXBRC_FM_DUPLICATE_ID;
+    }
+
+    m = xmalloc(sizeof *m);
+    m->ofconn = ofconn;
+    hmap_insert(&ofconn->monitors, &m->ofconn_node, hash_int(request->id, 0));
+    m->id = request->id;
+    m->flags = request->flags;
+    m->out_port = request->out_port;
+    m->table_id = request->table_id;
+    m->match = request->match;
+
+    *monitorp = m;
+    return 0;
+}
+
+struct ofmonitor *
+ofmonitor_lookup(struct ofconn *ofconn, uint32_t id)
+{
+    struct ofmonitor *m;
+
+    HMAP_FOR_EACH_IN_BUCKET (m, ofconn_node, hash_int(id, 0),
+                             &ofconn->monitors) {
+        if (m->id == id) {
+            return m;
+        }
+    }
+    return NULL;
+}
+
+void
+ofmonitor_destroy(struct ofmonitor *m)
+{
+    if (m) {
+        hmap_remove(&m->ofconn->monitors, &m->ofconn_node);
+        free(m);
+    }
+}
+
+void
+ofmonitor_report(struct connmgr *mgr, struct rule *rule,
+                 enum nx_flow_update_event event,
+                 enum ofp_flow_removed_reason reason,
+                 const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid)
+{
+    enum nx_flow_monitor_flags update;
+    struct ofconn *ofconn;
+
+    switch (event) {
+    case NXFME_ADDED:
+        update = NXFMF_ADD;
+        rule->add_seqno = rule->modify_seqno = monitor_seqno++;
+        break;
+
+    case NXFME_DELETED:
+        update = NXFMF_DELETE;
+        break;
+
+    case NXFME_MODIFIED:
+        update = NXFMF_MODIFY;
+        rule->modify_seqno = monitor_seqno++;
+        break;
+
+    default:
+    case NXFME_ABBREV:
+        NOT_REACHED();
+    }
+
+    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+        enum nx_flow_monitor_flags flags = 0;
+        struct ofmonitor *m;
+
+        if (ofconn->monitor_paused) {
+            /* Only send NXFME_DELETED notifications for flows that were added
+             * before we paused. */
+            if (event != NXFME_DELETED
+                || rule->add_seqno > ofconn->monitor_paused) {
+                continue;
+            }
+        }
+
+        HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
+            if (m->flags & update
+                && (m->table_id == 0xff || m->table_id == rule->table_id)
+                && ofoperation_has_out_port(rule->pending, m->out_port)
+                && cls_rule_is_loose_match(&rule->cr, &m->match)) {
+                flags |= m->flags;
+            }
+        }
+
+        if (flags) {
+            if (list_is_empty(&ofconn->updates)) {
+                ofputil_start_flow_update(&ofconn->updates);
+                ofconn->sent_abbrev_update = false;
+            }
+
+            if (ofconn != abbrev_ofconn || ofconn->monitor_paused) {
+                struct ofputil_flow_update fu;
+
+                fu.event = event;
+                fu.reason = event == NXFME_DELETED ? reason : 0;
+                fu.idle_timeout = rule->idle_timeout;
+                fu.hard_timeout = rule->hard_timeout;
+                fu.table_id = rule->table_id;
+                fu.cookie = rule->flow_cookie;
+                fu.match = &rule->cr;
+                if (flags & NXFMF_ACTIONS) {
+                    fu.ofpacts = rule->ofpacts;
+                    fu.ofpacts_len = rule->ofpacts_len;
+                } else {
+                    fu.ofpacts = NULL;
+                    fu.ofpacts_len = 0;
+                }
+                ofputil_append_flow_update(&fu, &ofconn->updates);
+            } else if (!ofconn->sent_abbrev_update) {
+                struct ofputil_flow_update fu;
+
+                fu.event = NXFME_ABBREV;
+                fu.xid = abbrev_xid;
+                ofputil_append_flow_update(&fu, &ofconn->updates);
+
+                ofconn->sent_abbrev_update = true;
+            }
+        }
+    }
+}
+
+void
+ofmonitor_flush(struct connmgr *mgr)
+{
+    struct ofconn *ofconn;
+
+    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+        struct ofpbuf *msg, *next;
+
+        LIST_FOR_EACH_SAFE (msg, next, list_node, &ofconn->updates) {
+            list_remove(&msg->list_node);
+            ofconn_send(ofconn, msg, ofconn->monitor_counter);
+            if (!ofconn->monitor_paused
+                && ofconn->monitor_counter->n_bytes > 128 * 1024) {
+                struct ofpbuf *pause;
+
+                COVERAGE_INC(ofmonitor_pause);
+                ofconn->monitor_paused = monitor_seqno++;
+                pause = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_PAUSED,
+                                         OFP10_VERSION, htonl(0), 0);
+                ofconn_send(ofconn, pause, ofconn->monitor_counter);
+            }
+        }
+    }
+}
+
+static void
+ofmonitor_resume(struct ofconn *ofconn)
+{
+    struct ofpbuf *resumed;
+    struct ofmonitor *m;
+    struct list rules;
+    struct list msgs;
+
+    list_init(&rules);
+    HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
+        ofmonitor_collect_resume_rules(m, ofconn->monitor_paused, &rules);
+    }
+
+    list_init(&msgs);
+    ofmonitor_compose_refresh_updates(&rules, &msgs);
+
+    resumed = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_RESUMED, OFP10_VERSION,
+                               htonl(0), 0);
+    list_push_back(&msgs, &resumed->list_node);
+    ofconn_send_replies(ofconn, &msgs);
+
+    ofconn->monitor_paused = 0;
+}
+
+static void
+ofmonitor_run(struct connmgr *mgr)
+{
+    struct ofconn *ofconn;
+
+    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+        if (ofconn->monitor_paused && !ofconn->monitor_counter->n_packets) {
+            COVERAGE_INC(ofmonitor_resume);
+            ofmonitor_resume(ofconn);
+        }
+    }
+}
+
+static void
+ofmonitor_wait(struct connmgr *mgr)
+{
+    struct ofconn *ofconn;
+
+    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+        if (ofconn->monitor_paused && !ofconn->monitor_counter->n_packets) {
+            poll_immediate_wake();
+        }
+    }
+}
index 81ca51b..24a33fb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
 #ifndef CONNMGR_H
 #define CONNMGR_H 1
 
+#include "classifier.h"
 #include "hmap.h"
 #include "list.h"
 #include "ofp-errors.h"
@@ -30,6 +31,8 @@ struct ofopgroup;
 struct ofputil_flow_removed;
 struct ofputil_packet_in;
 struct ofputil_phy_port;
+struct rule;
+struct simap;
 struct sset;
 
 /* ofproto supports two kinds of OpenFlow connections:
@@ -70,6 +73,8 @@ void connmgr_run(struct connmgr *,
                                          struct ofpbuf *ofp_msg));
 void connmgr_wait(struct connmgr *, bool handling_openflow);
 
+void connmgr_get_memory_usage(const struct connmgr *, struct simap *usage);
+
 struct ofproto *ofconn_get_ofproto(const struct ofconn *);
 
 void connmgr_retry(struct connmgr *);
@@ -156,4 +161,34 @@ bool connmgr_may_set_up_flow(struct connmgr *, const struct flow *,
 /* Fail-open and in-band implementation. */
 void connmgr_flushed(struct connmgr *);
 
+/* A flow monitor managed by NXST_FLOW_MONITOR and related requests. */
+struct ofmonitor {
+    struct ofconn *ofconn;      /* Owning 'ofconn'. */
+    struct hmap_node ofconn_node; /* In ofconn's 'monitors' hmap. */
+    uint32_t id;
+
+    enum nx_flow_monitor_flags flags;
+
+    /* Matching. */
+    uint16_t out_port;
+    uint8_t table_id;
+    struct cls_rule match;
+};
+
+struct ofputil_flow_monitor_request;
+
+enum ofperr ofmonitor_create(const struct ofputil_flow_monitor_request *,
+                             struct ofconn *, struct ofmonitor **);
+struct ofmonitor *ofmonitor_lookup(struct ofconn *, uint32_t id);
+void ofmonitor_destroy(struct ofmonitor *);
+
+void ofmonitor_report(struct connmgr *, struct rule *,
+                      enum nx_flow_update_event, enum ofp_flow_removed_reason,
+                      const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid);
+void ofmonitor_flush(struct connmgr *);
+
+void ofmonitor_collect_resume_rules(struct ofmonitor *, uint64_t seqno,
+                                    struct list *rules);
+void ofmonitor_compose_refresh_updates(struct list *rules, struct list *msgs);
+
 #endif /* connmgr.h */
index edc9397..495197e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
 #include "flow.h"
 #include "mac-learning.h"
 #include "odp-util.h"
+#include "ofp-actions.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "ofproto.h"
@@ -121,7 +122,7 @@ send_bogus_packet_ins(struct fail_open *fo)
 
     ofpbuf_init(&b, 128);
     eth_addr_nicira_random(mac);
-    compose_benign_packet(&b, "Open vSwitch Controller Probe", 0xa033, mac);
+    compose_rarp(&b, mac);
 
     memset(&pin, 0, sizeof pin);
     pin.packet = b.data;
@@ -214,18 +215,19 @@ fail_open_flushed(struct fail_open *fo)
     int disconn_secs = connmgr_failure_duration(fo->connmgr);
     bool open = disconn_secs >= trigger_duration(fo);
     if (open) {
-        union ofp_action action;
+        struct ofpbuf ofpacts;
         struct cls_rule rule;
 
         /* Set up a flow that matches every packet and directs them to
          * OFPP_NORMAL. */
-        memset(&action, 0, sizeof action);
-        action.type = htons(OFPAT10_OUTPUT);
-        action.output.len = htons(sizeof action);
-        action.output.port = htons(OFPP_NORMAL);
+        ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
+        ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
+        ofpact_pad(&ofpacts);
 
         cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY);
-        ofproto_add_flow(fo->ofproto, &rule, &action, 1);
+        ofproto_add_flow(fo->ofproto, &rule, ofpacts.data, ofpacts.size);
+
+        ofpbuf_uninit(&ofpacts);
     }
 }
 
index 51fa0b9..4e91ea0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index f738998..43461ad 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
 #include "netdev.h"
 #include "netlink.h"
 #include "odp-util.h"
+#include "ofp-actions.h"
 #include "ofproto.h"
 #include "ofpbuf.h"
 #include "ofproto-provider.h"
@@ -402,32 +403,17 @@ update_rules(struct in_band *ib)
 bool
 in_band_run(struct in_band *ib)
 {
-    struct {
-        struct nx_action_set_queue nxsq;
-        union ofp_action oa;
-    } actions;
-    const void *a;
-    size_t na;
+    uint64_t ofpacts_stub[128 / 8];
+    struct ofpbuf ofpacts;
 
     struct in_band_rule *rule, *next;
 
-    memset(&actions, 0, sizeof actions);
-    actions.oa.output.type = htons(OFPAT10_OUTPUT);
-    actions.oa.output.len = htons(sizeof actions.oa);
-    actions.oa.output.port = htons(OFPP_NORMAL);
-    actions.oa.output.max_len = htons(0);
-    if (ib->queue_id < 0) {
-        a = &actions.oa;
-        na = sizeof actions.oa / sizeof(union ofp_action);
-    } else {
-        actions.nxsq.type = htons(OFPAT10_VENDOR);
-        actions.nxsq.len = htons(sizeof actions.nxsq);
-        actions.nxsq.vendor = htonl(NX_VENDOR_ID);
-        actions.nxsq.subtype = htons(NXAST_SET_QUEUE);
-        actions.nxsq.queue_id = htonl(ib->queue_id);
-        a = &actions;
-        na = sizeof actions / sizeof(union ofp_action);
+    ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+
+    if (ib->queue_id >= 0) {
+        ofpact_put_SET_QUEUE(&ofpacts)->queue_id = ib->queue_id;
     }
+    ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
 
     refresh_local(ib);
     refresh_remotes(ib);
@@ -437,7 +423,8 @@ in_band_run(struct in_band *ib)
     HMAP_FOR_EACH_SAFE (rule, next, cls_rule.hmap_node, &ib->rules) {
         switch (rule->op) {
         case ADD:
-            ofproto_add_flow(ib->ofproto, &rule->cls_rule, a, na);
+            ofproto_add_flow(ib->ofproto, &rule->cls_rule,
+                             ofpacts.data, ofpacts.size);
             break;
 
         case DELETE:
@@ -451,6 +438,8 @@ in_band_run(struct in_band *ib)
         }
     }
 
+    ofpbuf_uninit(&ofpacts);
+
     return ib->n_remotes || !hmap_is_empty(&ib->rules);
 }
 
index 9de72ee..7b610cb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 724b184..6dac9a0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 994f928..aec3968 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 2c57f2f..c01ff15 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index 3b15c38..506dadb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -93,7 +93,9 @@ governor_run(struct governor *g)
 void
 governor_wait(struct governor *g)
 {
-    poll_timer_wait_until(g->start + MAX_ELAPSED);
+    if (g->size > MIN_SIZE) {
+        poll_timer_wait_until(g->start + MAX_ELAPSED);
+    }
 }
 
 /* Returns true if 'g' has been doing only a minimal amount of work and thus
@@ -114,9 +116,9 @@ governor_is_idle(struct governor *g)
 bool
 governor_should_install_flow(struct governor *g, uint32_t hash, int n)
 {
+    int old_count, new_count;
     bool install_flow;
     uint8_t *e;
-    int count;
 
     assert(n > 0);
 
@@ -133,19 +135,38 @@ governor_should_install_flow(struct governor *g, uint32_t hash, int n)
         governor_new_generation(g, new_size);
     }
 
+    /* If we've set up most of the flows we've seen, then we're wasting time
+     * handling most packets one at a time, so in this case instead set up most
+     * flows directly and use the remaining flows as a sample set to adjust our
+     * criteria later.
+     *
+     * The definition of "most" is conservative, but the sample size is tuned
+     * based on a few experiments with TCP_CRR mode in netperf. */
+    if (g->n_setups >= g->n_flows - g->n_flows / 16
+        && g->n_flows >= 64
+        && hash & 0x3f) {
+        g->n_shortcuts++;
+        return true;
+    }
+
     /* Do hash table processing.
      *
      * Even-numbered hash values use high-order nibbles.
      * Odd-numbered hash values use low-order nibbles. */
     e = &g->table[(hash >> 1) & (g->size - 1)];
-    count = n + (hash & 1 ? *e >> 4 : *e & 0x0f);
-    if (count >= FLOW_SETUP_THRESHOLD) {
+    old_count = (hash & 1 ? *e >> 4 : *e & 0x0f);
+    if (!old_count) {
+        g->n_flows++;
+    }
+    new_count = n + old_count;
+    if (new_count >= FLOW_SETUP_THRESHOLD) {
+        g->n_setups++;
         install_flow = true;
-        count = 0;
+        new_count = 0;
     } else {
         install_flow = false;
     }
-    *e = hash & 1 ? (count << 4) | (*e & 0x0f) : (*e & 0xf0) | count;
+    *e = hash & 1 ? (new_count << 4) | (*e & 0x0f) : (*e & 0xf0) | new_count;
 
     return install_flow;
 }
@@ -162,27 +183,33 @@ governor_new_generation(struct governor *g, unsigned int size)
     if (g->size != size) {
         if (!g->size) {
             VLOG_INFO("%s: engaging governor with %u kB hash table",
-                      g->name, g->size / 1024);
+                      g->name, size / 1024);
         } else {
             VLOG_INFO("%s: processed %u packets in %.2f s, "
-                      "%s hash table to %u kB",
+                      "%s hash table to %u kB "
+                      "(%u hashes, %u setups, %u shortcuts)",
                       g->name, g->n_packets,
                       (time_msec() - g->start) / 1000.0,
                       size > g->size ? "enlarging" : "shrinking",
-                      size / 1024);
+                      size / 1024,
+                      g->n_flows, g->n_setups, g->n_shortcuts);
         }
 
         free(g->table);
         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("%s: 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,
-                 size / 1024);
+                 size / 1024, g->n_flows, g->n_setups, g->n_shortcuts);
     }
 
     /* Clear data for next generation. */
     memset(g->table, 0, size * sizeof *g->table);
     g->start = time_msec();
     g->n_packets = 0;
+    g->n_flows /= 2;
+    g->n_setups /= 2;
+    g->n_shortcuts = 0;
 }
index ad022d5..6dbd0d5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -38,6 +38,11 @@ struct governor {
     unsigned int size;          /* Table size in bytes. */
     long long int start;        /* Time when the table was last cleared. */
     unsigned int n_packets;     /* Number of packets processed. */
+
+    /* Statistics for skipping counters when most flows get set up. */
+    unsigned int n_flows;       /* Number of unique flows seen. */
+    unsigned int n_setups;      /* Number of flows set up based on counters. */
+    unsigned int n_shortcuts;   /* Number of flows set up based on history. */
 };
 
 struct governor *governor_create(const char *name);
index 73da2af..23f5498 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  * Copyright (c) 2009 InMon Corp.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
 #include <config.h>
 #include "ofproto-dpif-sflow.h"
 #include <inttypes.h>
+#include <sys/socket.h>
 #include <net/if.h>
 #include <stdlib.h>
 #include "collectors.h"
@@ -491,7 +492,7 @@ dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds,
 void
 dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
                     const struct flow *flow,
-                    const struct user_action_cookie *cookie)
+                    const union user_action_cookie *cookie)
 {
     SFL_FLOW_SAMPLE_TYPE fs;
     SFLFlow_sample_element hdrElem;
@@ -500,6 +501,7 @@ dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
     SFLSampler *sampler;
     struct dpif_sflow_port *in_dsp;
     struct netdev_stats stats;
+    ovs_be16 vlan_tci;
     int error;
 
     /* Build a flow sample */
@@ -549,21 +551,11 @@ dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
     switchElem.flowType.sw.src_priority = vlan_tci_to_pcp(flow->vlan_tci);
 
     /* Retrieve data from user_action_cookie. */
-    switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(cookie->vlan_tci);
-    switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(cookie->vlan_tci);
-
-    /* Set output port, as defined by http://www.sflow.org/sflow_version_5.txt
-       (search for "Input/output port information"). */
-    if (!cookie->n_output) {
-        /* This value indicates that the packet was dropped for an unknown
-         * reason. */
-        fs.output = 0x40000000 | 256;
-    } else if (cookie->n_output > 1 || !cookie->data) {
-        /* Setting the high bit means "multiple output ports". */
-        fs.output = 0x80000000 | cookie->n_output;
-    } else {
-        fs.output = cookie->data;
-    }
+    vlan_tci = cookie->sflow.vlan_tci;
+    switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(vlan_tci);
+    switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(vlan_tci);
+
+    fs.output = cookie->sflow.output;
 
     /* Submit the flow sample to be encoded into the next datagram. */
     SFLADD_ELEMENT(&fs, &hdrElem);
index 0b6c1b4..6af8dc0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2009, 2010 InMon Corp.
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ void dpif_sflow_wait(struct dpif_sflow *);
 void dpif_sflow_received(struct dpif_sflow *,
                          struct ofpbuf *,
                          const struct flow *,
-                         const struct user_action_cookie *);
+                         const union user_action_cookie *);
 
 int dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *, uint16_t);
 
index 1c5d331..78cb186 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "odp-util.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
+#include "ofp-actions.h"
+#include "ofp-parse.h"
 #include "ofp-print.h"
 #include "ofproto-dpif-governor.h"
 #include "ofproto-dpif-sflow.h"
 #include "poll-loop.h"
+#include "simap.h"
 #include "timer.h"
 #include "unaligned.h"
 #include "unixctl.h"
 
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif);
 
-COVERAGE_DEFINE(ofproto_dpif_ctlr_action);
 COVERAGE_DEFINE(ofproto_dpif_expired);
-COVERAGE_DEFINE(ofproto_dpif_no_packet_in);
 COVERAGE_DEFINE(ofproto_dpif_xlate);
 COVERAGE_DEFINE(facet_changed_rule);
-COVERAGE_DEFINE(facet_invalidated);
 COVERAGE_DEFINE(facet_revalidate);
 COVERAGE_DEFINE(facet_unexpected);
 COVERAGE_DEFINE(facet_suppress);
 
 /* Maximum depth of flow table recursion (due to resubmit actions) in a
  * flow translation. */
-#define MAX_RESUBMIT_RECURSION 32
+#define MAX_RESUBMIT_RECURSION 64
 
 /* Number of implemented OpenFlow tables. */
 enum { N_TABLES = 255 };
-BUILD_ASSERT_DECL(N_TABLES >= 1 && 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;
@@ -105,7 +106,12 @@ static struct rule_dpif *rule_dpif_cast(const struct rule *rule)
 }
 
 static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *,
-                                          const struct flow *, uint8_t table);
+                                          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 *);
@@ -177,7 +183,7 @@ 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(struct ofproto_dpif *,
+static struct ofbundle *lookup_input_bundle(const struct ofproto_dpif *,
                                             uint16_t in_port, bool warn,
                                             struct ofport_dpif **in_ofportp);
 
@@ -239,6 +245,11 @@ struct action_xlate_ctx {
      * 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.
      *
@@ -251,8 +262,7 @@ struct action_xlate_ctx {
 
     struct ofpbuf *odp_actions; /* Datapath actions. */
     tag_type tags;              /* Tags associated with actions. */
-    bool may_set_up_flow;       /* True ordinarily; false if the actions must
-                                 * be reassessed for every packet. */
+    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? */
@@ -279,11 +289,45 @@ static void action_xlate_ctx_init(struct action_xlate_ctx *,
                                   ovs_be16 initial_tci, struct rule_dpif *,
                                   uint8_t tcp_flags, const struct ofpbuf *);
 static void xlate_actions(struct action_xlate_ctx *,
-                          const union ofp_action *in, size_t n_in,
+                          const struct ofpact *ofpacts, size_t ofpacts_len,
                           struct ofpbuf *odp_actions);
 static void xlate_actions_for_side_effects(struct action_xlate_ctx *,
-                                           const union ofp_action *in,
-                                           size_t n_in);
+                                           const struct ofpact *ofpacts,
+                                           size_t ofpacts_len);
+
+static size_t put_userspace_action(const struct ofproto_dpif *,
+                                   struct ofpbuf *odp_actions,
+                                   const struct flow *,
+                                   const union user_action_cookie *);
+
+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:
+ *
+ *   - SF_NOT_INSTALLED: Not installed in the datapath.  This will only be the
+ *     case just after the subfacet is created, just before the subfacet is
+ *     destroyed, or if the datapath returns an error when we try to install a
+ *     subfacet.
+ *
+ *   - SF_FAST_PATH: The subfacet's actions are installed in the datapath.
+ *
+ *   - SF_SLOW_PATH: An action that sends every packet for the subfacet through
+ *     ofproto_dpif is installed in the datapath.
+ */
+enum subfacet_path {
+    SF_NOT_INSTALLED,           /* No datapath flow for this subfacet. */
+    SF_FAST_PATH,               /* Full actions are installed. */
+    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.
  *
@@ -315,7 +359,8 @@ struct subfacet {
     size_t actions_len;         /* Number of bytes in actions[]. */
     struct nlattr *actions;     /* Datapath actions. */
 
-    bool installed;             /* Installed in datapath? */
+    enum slow_path_reason slow; /* 0 if fast path may be used. */
+    enum subfacet_path path;    /* Installed in datapath? */
 
     /* This value is normally the same as ->facet->flow.vlan_tci.  Only VLAN
      * splinters can cause it to differ.  This value should be removed when
@@ -325,7 +370,8 @@ struct subfacet {
 
 static struct subfacet *subfacet_create(struct facet *, enum odp_key_fitness,
                                         const struct nlattr *key,
-                                        size_t key_len, ovs_be16 initial_tci);
+                                        size_t key_len, ovs_be16 initial_tci,
+                                        long long int now);
 static struct subfacet *subfacet_find(struct ofproto_dpif *,
                                       const struct nlattr *key, size_t key_len);
 static void subfacet_destroy(struct subfacet *);
@@ -342,9 +388,11 @@ static void subfacet_make_actions(struct subfacet *,
                                   struct ofpbuf *odp_actions);
 static int subfacet_install(struct subfacet *,
                             const struct nlattr *actions, size_t actions_len,
-                            struct dpif_flow_stats *);
+                            struct dpif_flow_stats *, enum slow_path_reason);
 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 facet associates a "struct flow", which represents the Open vSwitch
@@ -403,7 +451,6 @@ struct facet {
      * 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 may_install;            /* Reassess actions for every packet? */
     bool has_learn;              /* Actions include NXAST_LEARN? */
     bool has_normal;             /* Actions output to OFPP_NORMAL? */
     bool has_fin_timeout;        /* Actions include NXAST_FIN_TIMEOUT? */
@@ -425,7 +472,7 @@ static struct facet *facet_find(struct ofproto_dpif *,
                                 const struct flow *, uint32_t hash);
 static struct facet *facet_lookup_valid(struct ofproto_dpif *,
                                         const struct flow *, uint32_t hash);
-static bool facet_revalidate(struct facet *);
+static void facet_revalidate(struct facet *);
 static bool facet_check_consistency(struct facet *);
 
 static void facet_flush_stats(struct facet *);
@@ -493,8 +540,7 @@ struct vlan_splinter {
 
 static uint32_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
                                        uint32_t realdev, ovs_be16 vlan_tci);
-static uint16_t vsp_vlandev_to_realdev(const struct ofproto_dpif *,
-                                       uint16_t vlandev, int *vid);
+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);
 
@@ -506,6 +552,7 @@ 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_cfm(struct ofport *, const struct cfm_settings *);
 static void ofport_clear_priorities(struct ofport_dpif *);
@@ -526,12 +573,37 @@ struct table_dpif {
     uint32_t basis;                   /* Keeps each table's tags separate. */
 };
 
+/* Reasons that we might need to revalidate every facet, and corresponding
+ * coverage counters.
+ *
+ * A value of 0 means that there is no need to revalidate.
+ *
+ * It would be nice to have some cleaner way to integrate with coverage
+ * counters, but with only a few reasons I guess this is good enough for
+ * now. */
+enum revalidate_reason {
+    REV_RECONFIGURE = 1,       /* Switch configuration changed. */
+    REV_STP,                   /* Spanning tree protocol port status change. */
+    REV_PORT_TOGGLED,          /* Port enabled or disabled by CFM, LACP, ...*/
+    REV_FLOW_TABLE,            /* Flow table changed. */
+    REV_INCONSISTENCY          /* Facet self-check failed. */
+};
+COVERAGE_DEFINE(rev_reconfigure);
+COVERAGE_DEFINE(rev_stp);
+COVERAGE_DEFINE(rev_port_toggled);
+COVERAGE_DEFINE(rev_flow_table);
+COVERAGE_DEFINE(rev_inconsistency);
+
 struct ofproto_dpif {
     struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
     struct ofproto up;
     struct dpif *dpif;
     int max_ports;
 
+    /* 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;
 
@@ -554,7 +626,7 @@ struct ofproto_dpif {
 
     /* Revalidation. */
     struct table_dpif tables[N_TABLES];
-    bool need_revalidate;
+    enum revalidate_reason need_revalidate;
     struct tag_set revalidate_set;
 
     /* Support for debugging async flow mods. */
@@ -589,9 +661,9 @@ ofproto_dpif_cast(const struct ofproto *ofproto)
     return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
 }
 
-static struct ofport_dpif *get_ofp_port(struct ofproto_dpif *,
+static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *,
                                         uint16_t ofp_port);
-static struct ofport_dpif *get_odp_port(struct ofproto_dpif *,
+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 *, ovs_be16 initial_tci,
@@ -613,9 +685,9 @@ 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 size_t compose_sflow_action(const struct ofproto_dpif *,
+                                   struct ofpbuf *odp_actions,
+                                   const struct flow *, uint32_t odp_port);
 static void add_mirror_actions(struct action_xlate_ctx *ctx,
                                const struct flow *flow);
 /* Global variables. */
@@ -651,6 +723,8 @@ del(const char *type, const char *name)
 \f
 /* Basic life-cycle. */
 
+static int add_internal_flows(struct ofproto_dpif *);
+
 static struct ofproto *
 alloc(void)
 {
@@ -715,7 +789,7 @@ construct(struct ofproto *ofproto_)
         table->other_table = NULL;
         table->basis = random_uint32();
     }
-    ofproto->need_revalidate = false;
+    ofproto->need_revalidate = 0;
     tag_set_init(&ofproto->revalidate_set);
 
     list_init(&ofproto->completions);
@@ -733,10 +807,76 @@ construct(struct ofproto *ofproto_)
     memset(&ofproto->stats, 0, sizeof ofproto->stats);
 
     ofproto_init_tables(ofproto_, N_TABLES);
+    error = add_internal_flows(ofproto);
+    ofproto->up.tables[TBL_INTERNAL].flags = OFTABLE_HIDDEN | OFTABLE_READONLY;
+
+    return error;
+}
+
+static int
+add_internal_flow(struct ofproto_dpif *ofproto, int id,
+                  const struct ofpbuf *ofpacts, struct rule_dpif **rulep)
+{
+    struct ofputil_flow_mod fm;
+    int error;
+
+    cls_rule_init_catchall(&fm.cr, 0);
+    cls_rule_set_reg(&fm.cr, 0, id);
+    fm.new_cookie = htonll(0);
+    fm.cookie = htonll(0);
+    fm.cookie_mask = htonll(0);
+    fm.table_id = TBL_INTERNAL;
+    fm.command = OFPFC_ADD;
+    fm.idle_timeout = 0;
+    fm.hard_timeout = 0;
+    fm.buffer_id = 0;
+    fm.out_port = 0;
+    fm.flags = 0;
+    fm.ofpacts = ofpacts->data;
+    fm.ofpacts_len = ofpacts->size;
+
+    error = ofproto_flow_mod(&ofproto->up, &fm);
+    if (error) {
+        VLOG_ERR_RL(&rl, "failed to add internal flow %d (%s)",
+                    id, ofperr_to_string(error));
+        return error;
+    }
+
+    *rulep = rule_dpif_lookup__(ofproto, &fm.cr.flow, TBL_INTERNAL);
+    assert(*rulep != NULL);
 
     return 0;
 }
 
+static int
+add_internal_flows(struct ofproto_dpif *ofproto)
+{
+    struct ofpact_controller *controller;
+    uint64_t ofpacts_stub[128 / 8];
+    struct ofpbuf ofpacts;
+    int error;
+    int id;
+
+    ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+    id = 1;
+
+    controller = ofpact_put_CONTROLLER(&ofpacts);
+    controller->max_len = UINT16_MAX;
+    controller->controller_id = 0;
+    controller->reason = OFPR_NO_MATCH;
+    ofpact_pad(&ofpacts);
+
+    error = add_internal_flow(ofproto, id++, &ofpacts, &ofproto->miss_rule);
+    if (error) {
+        return error;
+    }
+
+    ofpbuf_clear(&ofpacts);
+    error = add_internal_flow(ofproto, id++, &ofpacts,
+                              &ofproto->no_packet_in_rule);
+    return error;
+}
+
 static void
 complete_operations(struct ofproto_dpif *ofproto)
 {
@@ -792,8 +932,13 @@ static int
 run_fast(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+    struct ofport_dpif *ofport;
     unsigned int work;
 
+    HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+        port_run_fast(ofport);
+    }
+
     /* Handle one or more batches of upcalls, until there's nothing left to do
      * or until we do a fixed total amount of work.
      *
@@ -861,13 +1006,21 @@ run(struct ofproto *ofproto_)
         || !tag_set_is_empty(&ofproto->revalidate_set)) {
         struct tag_set revalidate_set = ofproto->revalidate_set;
         bool revalidate_all = ofproto->need_revalidate;
-        struct facet *facet, *next;
+        struct facet *facet;
+
+        switch (ofproto->need_revalidate) {
+        case REV_RECONFIGURE:   COVERAGE_INC(rev_reconfigure);   break;
+        case REV_STP:           COVERAGE_INC(rev_stp);           break;
+        case REV_PORT_TOGGLED:  COVERAGE_INC(rev_port_toggled);  break;
+        case REV_FLOW_TABLE:    COVERAGE_INC(rev_flow_table);    break;
+        case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
+        }
 
         /* Clear the revalidation flags. */
         tag_set_init(&ofproto->revalidate_set);
-        ofproto->need_revalidate = false;
+        ofproto->need_revalidate = 0;
 
-        HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &ofproto->facets) {
+        HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
             if (revalidate_all
                 || tag_set_intersects(&revalidate_set, facet->tags)) {
                 facet_revalidate(facet);
@@ -883,7 +1036,7 @@ run(struct ofproto *ofproto_)
                              struct facet, hmap_node);
         if (!tag_set_intersects(&ofproto->revalidate_set, facet->tags)) {
             if (!facet_check_consistency(facet)) {
-                ofproto->need_revalidate = true;
+                ofproto->need_revalidate = REV_INCONSISTENCY;
             }
         }
     }
@@ -951,6 +1104,15 @@ wait(struct ofproto *ofproto_)
     }
 }
 
+static void
+get_memory_usage(const struct ofproto *ofproto_, struct simap *usage)
+{
+    const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+    simap_increase(usage, "facets", hmap_count(&ofproto->facets));
+    simap_increase(usage, "subfacets", hmap_count(&ofproto->subfacets));
+}
+
 static void
 flush(struct ofproto *ofproto_)
 {
@@ -965,7 +1127,7 @@ flush(struct ofproto *ofproto_)
         struct subfacet *subfacet;
 
         LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-            subfacet->installed = false;
+            subfacet->path = SF_NOT_INSTALLED;
             subfacet->dp_packet_count = 0;
             subfacet->dp_byte_count = 0;
         }
@@ -994,7 +1156,7 @@ get_features(struct ofproto *ofproto_ OVS_UNUSED,
 }
 
 static void
-get_tables(struct ofproto *ofproto_, struct ofp_table_stats *ots)
+get_tables(struct ofproto *ofproto_, struct ofp10_table_stats *ots)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct dpif_dp_stats s;
@@ -1027,7 +1189,7 @@ port_construct(struct ofport *port_)
     struct ofport_dpif *port = ofport_dpif_cast(port_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
 
-    ofproto->need_revalidate = true;
+    ofproto->need_revalidate = REV_RECONFIGURE;
     port->odp_port = ofp_port_to_odp_port(port->up.ofp_port);
     port->bundle = NULL;
     port->cfm = NULL;
@@ -1053,7 +1215,7 @@ port_destruct(struct ofport *port_)
     struct ofport_dpif *port = ofport_dpif_cast(port_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
 
-    ofproto->need_revalidate = true;
+    ofproto->need_revalidate = REV_RECONFIGURE;
     bundle_remove(port_);
     set_cfm(port_, NULL);
     if (ofproto->sflow) {
@@ -1082,8 +1244,9 @@ port_reconfigured(struct ofport *port_, enum ofputil_port_config old_config)
     enum ofputil_port_config changed = old_config ^ port->up.pp.config;
 
     if (changed & (OFPUTIL_PC_NO_RECV | OFPUTIL_PC_NO_RECV_STP |
-                   OFPUTIL_PC_NO_FWD | OFPUTIL_PC_NO_FLOOD)) {
-        ofproto->need_revalidate = true;
+                   OFPUTIL_PC_NO_FWD | OFPUTIL_PC_NO_FLOOD |
+                   OFPUTIL_PC_NO_PACKET_IN)) {
+        ofproto->need_revalidate = REV_RECONFIGURE;
 
         if (changed & OFPUTIL_PC_NO_FLOOD && port->bundle) {
             bundle_update(port->bundle);
@@ -1106,13 +1269,13 @@ set_sflow(struct ofproto *ofproto_,
             HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
                 dpif_sflow_add_port(ds, &ofport->up);
             }
-            ofproto->need_revalidate = true;
+            ofproto->need_revalidate = REV_RECONFIGURE;
         }
         dpif_sflow_set_options(ds, sflow_options);
     } else {
         if (ds) {
             dpif_sflow_destroy(ds);
-            ofproto->need_revalidate = true;
+            ofproto->need_revalidate = REV_RECONFIGURE;
             ofproto->sflow = NULL;
         }
     }
@@ -1132,7 +1295,7 @@ set_cfm(struct ofport *ofport_, const struct cfm_settings *s)
             struct ofproto_dpif *ofproto;
 
             ofproto = ofproto_dpif_cast(ofport->up.ofproto);
-            ofproto->need_revalidate = true;
+            ofproto->need_revalidate = REV_RECONFIGURE;
             ofport->cfm = cfm_create(netdev_get_name(ofport->up.netdev));
         }
 
@@ -1155,6 +1318,14 @@ get_cfm_fault(const struct ofport *ofport_)
     return ofport->cfm ? cfm_get_fault(ofport->cfm) : -1;
 }
 
+static int
+get_cfm_opup(const struct ofport *ofport_)
+{
+    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+    return ofport->cfm ? cfm_get_opup(ofport->cfm) : -1;
+}
+
 static int
 get_cfm_remote_mpids(const struct ofport *ofport_, const uint64_t **rmps,
                      size_t *n_rmps)
@@ -1212,7 +1383,7 @@ set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s)
 
     /* Only revalidate flows if the configuration changed. */
     if (!s != !ofproto->stp) {
-        ofproto->need_revalidate = true;
+        ofproto->need_revalidate = REV_RECONFIGURE;
     }
 
     if (s) {
@@ -1285,7 +1456,7 @@ update_stp_port_state(struct ofport_dpif *ofport)
         fwd_change = stp_forward_in_state(ofport->stp_state)
                         != stp_forward_in_state(state);
 
-        ofproto->need_revalidate = true;
+        ofproto->need_revalidate = REV_STP;
         ofport->stp_state = state;
         ofport->stp_state_entered = time_msec();
 
@@ -1483,12 +1654,12 @@ set_queues(struct ofport *ofport_,
             pdscp = xmalloc(sizeof *pdscp);
             pdscp->priority = priority;
             pdscp->dscp = dscp;
-            ofproto->need_revalidate = true;
+            ofproto->need_revalidate = REV_RECONFIGURE;
         }
 
         if (pdscp->dscp != dscp) {
             pdscp->dscp = dscp;
-            ofproto->need_revalidate = true;
+            ofproto->need_revalidate = REV_RECONFIGURE;
         }
 
         hmap_insert(&new, &pdscp->hmap_node, hash_int(pdscp->priority, 0));
@@ -1496,7 +1667,7 @@ set_queues(struct ofport *ofport_,
 
     if (!hmap_is_empty(&ofport->priorities)) {
         ofport_clear_priorities(ofport);
-        ofproto->need_revalidate = true;
+        ofproto->need_revalidate = REV_RECONFIGURE;
     }
 
     hmap_swap(&new, &ofport->priorities);
@@ -1523,7 +1694,7 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos)
     struct mac_learning *ml = ofproto->ml;
     struct mac_entry *mac, *next_mac;
 
-    ofproto->need_revalidate = true;
+    ofproto->need_revalidate = REV_RECONFIGURE;
     LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
         if (mac->port.p == bundle) {
             if (all_ofprotos) {
@@ -1600,7 +1771,7 @@ bundle_del_port(struct ofport_dpif *port)
 {
     struct ofbundle *bundle = port->bundle;
 
-    bundle->ofproto->need_revalidate = true;
+    bundle->ofproto->need_revalidate = REV_RECONFIGURE;
 
     list_remove(&port->bundle_node);
     port->bundle = NULL;
@@ -1628,7 +1799,7 @@ bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
     }
 
     if (port->bundle != bundle) {
-        bundle->ofproto->need_revalidate = true;
+        bundle->ofproto->need_revalidate = REV_RECONFIGURE;
         if (port->bundle) {
             bundle_del_port(port);
         }
@@ -1641,7 +1812,7 @@ bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
         }
     }
     if (lacp) {
-        port->bundle->ofproto->need_revalidate = true;
+        port->bundle->ofproto->need_revalidate = REV_RECONFIGURE;
         lacp_slave_register(bundle->lacp, port, lacp);
     }
 
@@ -1669,7 +1840,7 @@ bundle_destroy(struct ofbundle *bundle)
                 mirror_destroy(m);
             } else if (hmapx_find_and_delete(&m->srcs, bundle)
                        || hmapx_find_and_delete(&m->dsts, bundle)) {
-                ofproto->need_revalidate = true;
+                ofproto->need_revalidate = REV_RECONFIGURE;
             }
         }
     }
@@ -1741,7 +1912,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
     /* LACP. */
     if (s->lacp) {
         if (!bundle->lacp) {
-            ofproto->need_revalidate = true;
+            ofproto->need_revalidate = REV_RECONFIGURE;
             bundle->lacp = lacp_create();
         }
         lacp_configure(bundle->lacp, s->lacp);
@@ -1804,7 +1975,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         break;
 
     case PORT_VLAN_TRUNK:
-        trunks = (unsigned long *) s->trunks;
+        trunks = CONST_CAST(unsigned long *, s->trunks);
         break;
 
     case PORT_VLAN_NATIVE_UNTAGGED:
@@ -1821,7 +1992,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
             bitmap_set1(trunks, vlan);
             bitmap_set0(trunks, 0);
         } else {
-            trunks = (unsigned long *) s->trunks;
+            trunks = CONST_CAST(unsigned long *, s->trunks);
         }
         break;
 
@@ -1847,11 +2018,11 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         bundle->ofproto->has_bonded_bundles = true;
         if (bundle->bond) {
             if (bond_reconfigure(bundle->bond, s->bond)) {
-                ofproto->need_revalidate = true;
+                ofproto->need_revalidate = REV_RECONFIGURE;
             }
         } else {
             bundle->bond = bond_create(s->bond);
-            ofproto->need_revalidate = true;
+            ofproto->need_revalidate = REV_RECONFIGURE;
         }
 
         LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
@@ -2156,7 +2327,7 @@ mirror_set(struct ofproto *ofproto_, void *aux,
         }
     }
 
-    ofproto->need_revalidate = true;
+    ofproto->need_revalidate = REV_RECONFIGURE;
     ofproto->has_mirrors = true;
     mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
     mirror_update_dups(ofproto);
@@ -2177,7 +2348,7 @@ mirror_destroy(struct ofmirror *mirror)
     }
 
     ofproto = mirror->ofproto;
-    ofproto->need_revalidate = true;
+    ofproto->need_revalidate = REV_RECONFIGURE;
     mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
 
     mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
@@ -2246,8 +2417,7 @@ static void
 forward_bpdu_changed(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    /* Revalidate cached flows whenever forward_bpdu option changes. */
-    ofproto->need_revalidate = true;
+    ofproto->need_revalidate = REV_RECONFIGURE;
 }
 
 static void
@@ -2260,14 +2430,14 @@ set_mac_idle_time(struct ofproto *ofproto_, unsigned int idle_time)
 /* Ports. */
 
 static struct ofport_dpif *
-get_ofp_port(struct ofproto_dpif *ofproto, uint16_t ofp_port)
+get_ofp_port(const struct ofproto_dpif *ofproto, uint16_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(struct ofproto_dpif *ofproto, uint32_t odp_port)
+get_odp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
 {
     return get_ofp_port(ofproto, odp_port_to_ofp_port(odp_port));
 }
@@ -2281,6 +2451,19 @@ ofproto_port_from_dpif_port(struct ofproto_port *ofproto_port,
     ofproto_port->ofp_port = odp_port_to_ofp_port(dpif_port->port_no);
 }
 
+static void
+port_run_fast(struct ofport_dpif *ofport)
+{
+    if (ofport->cfm && cfm_should_send_ccm(ofport->cfm)) {
+        struct ofpbuf packet;
+
+        ofpbuf_init(&packet, 0);
+        cfm_compose_ccm(ofport->cfm, &packet, ofport->up.pp.hw_addr);
+        send_packet(ofport, &packet);
+        ofpbuf_uninit(&packet);
+    }
+}
+
 static void
 port_run(struct ofport_dpif *ofport)
 {
@@ -2290,20 +2473,16 @@ port_run(struct ofport_dpif *ofport)
 
     ofport->carrier_seq = carrier_seq;
 
+    port_run_fast(ofport);
     if (ofport->cfm) {
-        cfm_run(ofport->cfm);
+        int cfm_opup = cfm_get_opup(ofport->cfm);
 
-        if (cfm_should_send_ccm(ofport->cfm)) {
-            struct ofpbuf packet;
+        cfm_run(ofport->cfm);
+        enable = enable && !cfm_get_fault(ofport->cfm);
 
-            ofpbuf_init(&packet, 0);
-            cfm_compose_ccm(ofport->cfm, &packet, ofport->up.pp.hw_addr);
-            send_packet(ofport, &packet);
-            ofpbuf_uninit(&packet);
+        if (cfm_opup >= 0) {
+            enable = enable && cfm_opup;
         }
-
-        enable = enable && !cfm_get_fault(ofport->cfm)
-            && cfm_get_opup(ofport->cfm);
     }
 
     if (ofport->bundle) {
@@ -2317,7 +2496,7 @@ port_run(struct ofport_dpif *ofport)
         struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
 
         if (ofproto->has_bundle_action) {
-            ofproto->need_revalidate = true;
+            ofproto->need_revalidate = REV_PORT_TOGGLED;
         }
     }
 
@@ -2351,7 +2530,7 @@ static int
 port_add(struct ofproto *ofproto_, struct netdev *netdev, uint16_t *ofp_portp)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    uint16_t odp_port;
+    uint16_t odp_port = UINT16_MAX;
     int error;
 
     error = dpif_port_add(ofproto->dpif, netdev, &odp_port);
@@ -2407,7 +2586,7 @@ port_get_stats(const struct ofport *ofport_, struct netdev_stats *stats)
 
         /* ofproto->stats.rx_packets represents packets that were received on
          * some port and we processed internally and dropped (e.g. STP).
-         * Account fro them as if they had been forwarded to OFPP_LOCAL. */
+         * Account for them as if they had been forwarded to OFPP_LOCAL. */
 
         if (stats->tx_packets != UINT64_MAX) {
             stats->tx_packets += ofproto->stats.rx_packets;
@@ -2526,6 +2705,7 @@ struct flow_miss {
     size_t key_len;
     ovs_be16 initial_tci;
     struct list packets;
+    enum dpif_upcall_type upcall_type;
 };
 
 struct flow_miss_op {
@@ -2556,40 +2736,37 @@ send_packet_in_miss(struct ofproto_dpif *ofproto, const struct ofpbuf *packet,
 
     flow_get_metadata(flow, &pin.fmd);
 
-    /* Registers aren't meaningful on a miss. */
-    memset(pin.fmd.reg_masks, 0, sizeof pin.fmd.reg_masks);
-
     connmgr_send_packet_in(ofproto->up.connmgr, &pin);
 }
 
-static bool
+static enum slow_path_reason
 process_special(struct ofproto_dpif *ofproto, const struct flow *flow,
                 const struct ofpbuf *packet)
 {
     struct ofport_dpif *ofport = get_ofp_port(ofproto, flow->in_port);
 
     if (!ofport) {
-        return false;
+        return 0;
     }
 
     if (ofport->cfm && cfm_should_process_flow(ofport->cfm, flow)) {
         if (packet) {
             cfm_process_heartbeat(ofport->cfm, packet);
         }
-        return true;
+        return SLOW_CFM;
     } 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 true;
+        return SLOW_LACP;
     } else if (ofproto->stp && stp_should_process_flow(flow)) {
         if (packet) {
             stp_process_packet(ofport, packet);
         }
-        return true;
+        return SLOW_STP;
     }
-    return false;
+    return 0;
 }
 
 static struct flow_miss *
@@ -2692,6 +2869,7 @@ handle_flow_miss_without_facet(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;
 
@@ -2704,13 +2882,13 @@ handle_flow_miss_without_facet(struct flow_miss *miss,
 
         ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
 
-        dpif_flow_stats_extract(&miss->flow, packet, &stats);
+        dpif_flow_stats_extract(&miss->flow, packet, now, &stats);
         rule_credit_stats(rule, &stats);
 
         action_xlate_ctx_init(&ctx, ofproto, &miss->flow, miss->initial_tci,
                               rule, 0, packet);
         ctx.resubmit_stats = &stats;
-        xlate_actions(&ctx, rule->up.actions, rule->up.n_actions,
+        xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
                       &odp_actions);
 
         if (odp_actions.size) {
@@ -2729,17 +2907,27 @@ handle_flow_miss_without_facet(struct flow_miss *miss,
 }
 
 /* Handles 'miss', which matches 'facet'.  May add any required datapath
- * operations to 'ops', incrementing '*n_ops' for each new op. */
+ * operations to 'ops', incrementing '*n_ops' for each new op.
+ *
+ * All of the packets in 'miss' are considered to have arrived at time 'now'.
+ * This is really important only for new facets: if we just called time_msec()
+ * 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. */
 static void
 handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
+                            long long int now,
                             struct flow_miss_op *ops, size_t *n_ops)
 {
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+    enum subfacet_path want_path;
     struct subfacet *subfacet;
     struct ofpbuf *packet;
 
     subfacet = subfacet_create(facet,
                                miss->key_fitness, miss->key, miss->key_len,
-                               miss->initial_tci);
+                               miss->initial_tci, now);
 
     LIST_FOR_EACH (packet, list_node, &miss->packets) {
         struct flow_miss_op *op = &ops[*n_ops];
@@ -2749,11 +2937,11 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
         handle_flow_miss_common(facet->rule, packet, &miss->flow);
 
         ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
-        if (!facet->may_install || !subfacet->actions) {
+        if (!subfacet->actions || subfacet->slow) {
             subfacet_make_actions(subfacet, packet, &odp_actions);
         }
 
-        dpif_flow_stats_extract(&facet->flow, packet, &stats);
+        dpif_flow_stats_extract(&facet->flow, packet, now, &stats);
         subfacet_update_stats(subfacet, &stats);
 
         if (subfacet->actions_len) {
@@ -2761,7 +2949,7 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
 
             init_flow_miss_execute_op(miss, packet, op);
             op->subfacet = subfacet;
-            if (facet->may_install) {
+            if (!subfacet->slow) {
                 execute->actions = subfacet->actions;
                 execute->actions_len = subfacet->actions_len;
                 ofpbuf_uninit(&odp_actions);
@@ -2777,7 +2965,8 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
         }
     }
 
-    if (facet->may_install && subfacet->key_fitness != ODP_FIT_TOO_LITTLE) {
+    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;
 
@@ -2787,33 +2976,15 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
         put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
         put->key = miss->key;
         put->key_len = miss->key_len;
-        put->actions = subfacet->actions;
-        put->actions_len = subfacet->actions_len;
-        put->stats = NULL;
-    }
-}
-
-/* Handles flow miss 'miss' on 'ofproto'.  The flow does not match any flow in
- * the OpenFlow flow table. */
-static void
-handle_flow_miss_no_rule(struct ofproto_dpif *ofproto, struct flow_miss *miss)
-{
-    uint16_t in_port = miss->flow.in_port;
-    struct ofport_dpif *port = get_ofp_port(ofproto, in_port);
-
-    if (!port) {
-        VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, in_port);
-    }
-
-    if (port && port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
-        /* XXX install 'drop' flow entry */
-        COVERAGE_INC(ofproto_dpif_no_packet_in);
-    } else {
-        const struct ofpbuf *packet;
-
-        LIST_FOR_EACH (packet, list_node, &miss->packets) {
-            send_packet_in_miss(ofproto, packet, &miss->flow);
+        if (want_path == SF_FAST_PATH) {
+            put->actions = subfacet->actions;
+            put->actions_len = subfacet->actions_len;
+        } else {
+            compose_slow_path(ofproto, &facet->flow, subfacet->slow,
+                              op->stub, sizeof op->stub,
+                              &put->actions, &put->actions_len);
         }
+        put->stats = NULL;
     }
 }
 
@@ -2824,6 +2995,7 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
                  struct flow_miss_op *ops, size_t *n_ops)
 {
     struct facet *facet;
+    long long int now;
     uint32_t hash;
 
     /* The caller must ensure that miss->hmap_node.hash contains
@@ -2832,18 +3004,19 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
 
     facet = facet_lookup_valid(ofproto, &miss->flow, hash);
     if (!facet) {
-        struct rule_dpif *rule = rule_dpif_lookup(ofproto, &miss->flow, 0);
-        if (!rule) {
-            handle_flow_miss_no_rule(ofproto, miss);
-            return;
-        } else if (!flow_miss_should_make_facet(ofproto, miss, hash)) {
+        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);
             return;
         }
 
         facet = facet_create(rule, &miss->flow, hash);
+        now = facet->used;
+    } else {
+        now = time_msec();
     }
-    handle_flow_miss_with_facet(miss, facet, ops, n_ops);
+    handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
 }
 
 /* Like odp_flow_key_to_flow(), this function converts the 'key_len' bytes of
@@ -2869,8 +3042,6 @@ ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto,
                               struct ofpbuf *packet)
 {
     enum odp_key_fitness fitness;
-    uint16_t realdev;
-    int vid;
 
     fitness = odp_flow_key_to_flow(key, key_len, flow);
     if (fitness == ODP_FIT_ERROR) {
@@ -2878,12 +3049,7 @@ ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto,
     }
     *initial_tci = flow->vlan_tci;
 
-    realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid);
-    if (realdev) {
-        /* 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->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
+    if (vsp_adjust_flow(ofproto, flow)) {
         if (packet) {
             /* Make the packet resemble the flow, so that it gets sent to an
              * OpenFlow controller properly, so that it looks correct for
@@ -2952,14 +3118,6 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
         flow_extract(upcall->packet, miss->flow.skb_priority,
                      miss->flow.tun_id, miss->flow.in_port, &miss->flow);
 
-        /* Handle 802.1ag, LACP, and STP specially. */
-        if (process_special(ofproto, &miss->flow, upcall->packet)) {
-            ofproto_update_local_port_stats(&ofproto->up,
-                                            0, upcall->packet->size);
-            ofproto->n_matches++;
-            continue;
-        }
-
         /* Add other packets to a to-do list. */
         hash = flow_hash(&miss->flow, 0);
         existing_miss = flow_miss_find(&todo, &miss->flow, hash);
@@ -2967,6 +3125,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
             hmap_insert(&todo, &miss->hmap_node, hash);
             miss->key = upcall->key;
             miss->key_len = upcall->key_len;
+            miss->upcall_type = upcall->type;
             list_init(&miss->packets);
 
             n_misses++;
@@ -3000,7 +3159,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
 
         case DPIF_OP_FLOW_PUT:
             if (!op->dpif_op.error) {
-                op->subfacet->installed = true;
+                op->subfacet->path = subfacet_want_path(op->subfacet->slow);
             }
             break;
 
@@ -3013,17 +3172,50 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
     hmap_destroy(&todo);
 }
 
+static enum { SFLOW_UPCALL, MISS_UPCALL, BAD_UPCALL }
+classify_upcall(const struct dpif_upcall *upcall)
+{
+    union user_action_cookie cookie;
+
+    /* First look at the upcall type. */
+    switch (upcall->type) {
+    case DPIF_UC_ACTION:
+        break;
+
+    case DPIF_UC_MISS:
+        return MISS_UPCALL;
+
+    case DPIF_N_UC_TYPES:
+    default:
+        VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, upcall->type);
+        return BAD_UPCALL;
+    }
+
+    /* "action" upcalls need a closer look. */
+    memcpy(&cookie, &upcall->userdata, sizeof(cookie));
+    switch (cookie.type) {
+    case USER_ACTION_COOKIE_SFLOW:
+        return SFLOW_UPCALL;
+
+    case USER_ACTION_COOKIE_SLOW_PATH:
+        return MISS_UPCALL;
+
+    case USER_ACTION_COOKIE_UNSPEC:
+    default:
+        VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata);
+        return BAD_UPCALL;
+    }
+}
+
 static void
-handle_userspace_upcall(struct ofproto_dpif *ofproto,
-                        struct dpif_upcall *upcall)
+handle_sflow_upcall(struct ofproto_dpif *ofproto,
+                    const struct dpif_upcall *upcall)
 {
-    struct user_action_cookie cookie;
+    union user_action_cookie cookie;
     enum odp_key_fitness fitness;
     ovs_be16 initial_tci;
     struct flow flow;
 
-    memcpy(&cookie, &upcall->userdata, sizeof(cookie));
-
     fitness = ofproto_dpif_extract_flow_key(ofproto, upcall->key,
                                             upcall->key_len, &flow,
                                             &initial_tci, upcall->packet);
@@ -3031,14 +3223,8 @@ handle_userspace_upcall(struct ofproto_dpif *ofproto,
         return;
     }
 
-    if (cookie.type == USER_ACTION_COOKIE_SFLOW) {
-        if (ofproto->sflow) {
-            dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
-                                &cookie);
-        }
-    } else {
-        VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata);
-    }
+    memcpy(&cookie, &upcall->userdata, sizeof(cookie));
+    dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, &cookie);
 }
 
 static int
@@ -3053,7 +3239,6 @@ handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
 
     assert(max_batch <= FLOW_MISS_MAX_BATCH);
 
-    n_processed = 0;
     n_misses = 0;
     for (n_processed = 0; n_processed < max_batch; n_processed++) {
         struct dpif_upcall *upcall = &misses[n_misses];
@@ -3068,25 +3253,26 @@ handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
             break;
         }
 
-        switch (upcall->type) {
-        case DPIF_UC_ACTION:
-            handle_userspace_upcall(ofproto, upcall);
-            ofpbuf_uninit(buf);
-            break;
-
-        case DPIF_UC_MISS:
+        switch (classify_upcall(upcall)) {
+        case MISS_UPCALL:
             /* Handle it later. */
             n_misses++;
             break;
 
-        case DPIF_N_UC_TYPES:
-        default:
-            VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32,
-                         upcall->type);
+        case SFLOW_UPCALL:
+            if (ofproto->sflow) {
+                handle_sflow_upcall(ofproto, upcall);
+            }
+            ofpbuf_uninit(buf);
+            break;
+
+        case BAD_UPCALL:
+            ofpbuf_uninit(buf);
             break;
         }
     }
 
+    /* Handle deferred MISS_UPCALL processing. */
     handle_miss_upcalls(ofproto, misses, n_misses);
     for (i = 0; i < n_misses; i++) {
         ofpbuf_uninit(&miss_bufs[i]);
@@ -3147,6 +3333,60 @@ expire(struct ofproto_dpif *ofproto)
     return MIN(dp_max_idle, 1000);
 }
 
+/* Updates flow table statistics given that the datapath just reported 'stats'
+ * as 'subfacet''s statistics. */
+static void
+update_subfacet_stats(struct subfacet *subfacet,
+                      const struct dpif_flow_stats *stats)
+{
+    struct facet *facet = subfacet->facet;
+
+    if (stats->n_packets >= subfacet->dp_packet_count) {
+        uint64_t extra = stats->n_packets - subfacet->dp_packet_count;
+        facet->packet_count += extra;
+    } else {
+        VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
+    }
+
+    if (stats->n_bytes >= subfacet->dp_byte_count) {
+        facet->byte_count += stats->n_bytes - subfacet->dp_byte_count;
+    } else {
+        VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
+    }
+
+    subfacet->dp_packet_count = stats->n_packets;
+    subfacet->dp_byte_count = stats->n_bytes;
+
+    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);
+        facet->accounted_bytes = facet->byte_count;
+    }
+    facet_push_stats(facet);
+}
+
+/* 'key' with length 'key_len' bytes is a flow in 'dpif' that we know nothing
+ * about, or a flow that shouldn't be installed but was anyway.  Delete it. */
+static void
+delete_unexpected_flow(struct dpif *dpif,
+                       const struct nlattr *key, size_t key_len)
+{
+    if (!VLOG_DROP_WARN(&rl)) {
+        struct ds s;
+
+        ds_init(&s);
+        odp_flow_key_format(key, key_len, &s);
+        VLOG_WARN("unexpected flow from datapath %s", ds_cstr(&s));
+        ds_destroy(&s);
+    }
+
+    COVERAGE_INC(facet_unexpected);
+    dpif_flow_del(dpif, key, key_len, NULL);
+}
+
 /* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
  *
  * This function also pushes statistics updates to rules which each facet
@@ -3171,48 +3411,19 @@ update_stats(struct ofproto_dpif *p)
         struct subfacet *subfacet;
 
         subfacet = subfacet_find(p, key, key_len);
-        if (subfacet && subfacet->installed) {
-            struct facet *facet = subfacet->facet;
-
-            if (stats->n_packets >= subfacet->dp_packet_count) {
-                uint64_t extra = stats->n_packets - subfacet->dp_packet_count;
-                facet->packet_count += extra;
-            } else {
-                VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
-            }
-
-            if (stats->n_bytes >= subfacet->dp_byte_count) {
-                facet->byte_count += stats->n_bytes - subfacet->dp_byte_count;
-            } else {
-                VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
-            }
-
-            subfacet->dp_packet_count = stats->n_packets;
-            subfacet->dp_byte_count = stats->n_bytes;
-
-            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);
-                facet->accounted_bytes = facet->byte_count;
-            }
-            facet_push_stats(facet);
-        } else {
-            if (!VLOG_DROP_WARN(&rl)) {
-                struct ds s;
+        switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
+        case SF_FAST_PATH:
+            update_subfacet_stats(subfacet, stats);
+            break;
 
-                ds_init(&s);
-                odp_flow_key_format(key, key_len, &s);
-                VLOG_WARN("unexpected flow from datapath %s", ds_cstr(&s));
-                ds_destroy(&s);
-            }
+        case SF_SLOW_PATH:
+            /* Stats are updated per-packet. */
+            break;
 
-            COVERAGE_INC(facet_unexpected);
-            /* There's a flow in the datapath that we know nothing about, or a
-             * flow that shouldn't be installed but was anyway.  Delete it. */
-            dpif_flow_del(p->dpif, key, key_len, NULL);
+        case SF_NOT_INSTALLED:
+        default:
+            delete_unexpected_flow(p->dpif, key, key_len);
+            break;
         }
     }
     dpif_flow_dump_done(&dump);
@@ -3330,7 +3541,7 @@ expire_batch(struct ofproto_dpif *ofproto, struct subfacet **subfacets, int n)
     dpif_operate(ofproto->dpif, opsp, n);
     for (i = 0; i < n; i++) {
         subfacet_reset_dp_stats(subfacets[i], &stats[i]);
-        subfacets[i]->installed = false;
+        subfacets[i]->path = SF_NOT_INSTALLED;
         subfacet_destroy(subfacets[i]);
     }
 }
@@ -3338,7 +3549,12 @@ expire_batch(struct ofproto_dpif *ofproto, struct subfacet **subfacets, int n)
 static void
 expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
 {
-    long long int cutoff = time_msec() - dp_max_idle;
+    /* Cutoff time for most flows. */
+    long long int normal_cutoff = time_msec() - dp_max_idle;
+
+    /* We really want to keep flows for special protocols around, so use a more
+     * conservative cutoff. */
+    long long int special_cutoff = time_msec() - 10000;
 
     struct subfacet *subfacet, *next_subfacet;
     struct subfacet *batch[EXPIRE_MAX_BATCH];
@@ -3347,8 +3563,13 @@ expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
     n_batch = 0;
     HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
                         &ofproto->subfacets) {
+        long long int cutoff;
+
+        cutoff = (subfacet->slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)
+                  ? special_cutoff
+                  : normal_cutoff);
         if (subfacet->used < cutoff) {
-            if (subfacet->installed) {
+            if (subfacet->path != SF_NOT_INSTALLED) {
                 batch[n_batch++] = subfacet;
                 if (n_batch >= EXPIRE_MAX_BATCH) {
                     expire_batch(ofproto, batch, n_batch);
@@ -3374,6 +3595,11 @@ rule_expire(struct rule_dpif *rule)
     long long int now;
     uint8_t reason;
 
+    if (rule->up.pending) {
+        /* We'll have to expire it later. */
+        return;
+    }
+
     /* Has 'rule' expired? */
     now = time_msec();
     if (rule->up.hard_timeout
@@ -3514,8 +3740,8 @@ facet_learn(struct facet *facet)
                           facet->flow.vlan_tci,
                           facet->rule, facet->tcp_flags, NULL);
     ctx.may_learn = true;
-    xlate_actions_for_side_effects(&ctx, facet->rule->up.actions,
-                                   facet->rule->up.n_actions);
+    xlate_actions_for_side_effects(&ctx, facet->rule->up.ofpacts,
+                                   facet->rule->up.ofpacts_len);
 }
 
 static void
@@ -3576,10 +3802,18 @@ facet_account(struct facet *facet)
 static bool
 facet_is_controller_flow(struct facet *facet)
 {
-    return (facet
-            && facet->rule->up.n_actions == 1
-            && action_outputs_to_port(&facet->rule->up.actions[0],
-                                      htons(OFPP_CONTROLLER)));
+    if (facet) {
+        const struct rule *rule = &facet->rule->up;
+        const struct ofpact *ofpacts = rule->ofpacts;
+        size_t ofpacts_len = rule->ofpacts_len;
+
+        if (ofpacts_len > 0 &&
+            ofpacts->type == OFPACT_CONTROLLER &&
+            ofpact_next(ofpacts) >= ofpact_end(ofpacts, ofpacts_len)) {
+            return true;
+        }
+    }
+    return false;
 }
 
 /* Folds all of 'facet''s statistics into its rule.  Also updates the
@@ -3655,21 +3889,56 @@ static struct facet *
 facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow,
                    uint32_t hash)
 {
-    struct facet *facet = facet_find(ofproto, flow, hash);
+    struct facet *facet;
 
-    /* The facet we found might not be valid, since we could be in need of
-     * revalidation.  If it is not valid, don't return it. */
+    facet = facet_find(ofproto, flow, hash);
     if (facet
         && (ofproto->need_revalidate
-            || tag_set_intersects(&ofproto->revalidate_set, facet->tags))
-        && !facet_revalidate(facet)) {
-        COVERAGE_INC(facet_invalidated);
-        return NULL;
+            || tag_set_intersects(&ofproto->revalidate_set, facet->tags))) {
+        facet_revalidate(facet);
     }
 
     return facet;
 }
 
+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)
 {
@@ -3686,17 +3955,10 @@ facet_check_consistency(struct facet *facet)
     bool ok;
 
     /* Check the rule for consistency. */
-    rule = rule_dpif_lookup(ofproto, &facet->flow, 0);
-    if (!rule) {
-        if (!VLOG_DROP_WARN(&rl)) {
-            char *s = flow_to_string(&facet->flow);
-            VLOG_WARN("%s: facet should not exist", s);
-            free(s);
-        }
-        return false;
-    } else if (rule != facet->rule) {
+    rule = rule_dpif_lookup(ofproto, &facet->flow);
+    ok = rule == facet->rule;
+    if (!ok) {
         may_log = !VLOG_DROP_WARN(&rl);
-        ok = false;
         if (may_log) {
             struct ds s;
 
@@ -3713,75 +3975,77 @@ facet_check_consistency(struct facet *facet)
             VLOG_WARN("%s", ds_cstr(&s));
             ds_destroy(&s);
         }
-    } else {
-        ok = true;
     }
 
     /* 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 odputil_keybuf keybuf;
         struct action_xlate_ctx ctx;
-        bool actions_changed;
-        bool should_install;
+        struct ofpbuf key;
+        struct ds s;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
                               subfacet->initial_tci, rule, 0, NULL);
-        xlate_actions(&ctx, rule->up.actions, rule->up.n_actions,
+        xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
                       &odp_actions);
 
-        should_install = (ctx.may_set_up_flow
-                          && subfacet->key_fitness != ODP_FIT_TOO_LITTLE);
-        if (!should_install && !subfacet->installed) {
-            /* The actions for uninstallable flows may vary from one packet to
-             * the next, so don't compare the 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;
         }
 
-        actions_changed = (subfacet->actions_len != odp_actions.size
-                           || memcmp(subfacet->actions, odp_actions.data,
-                                     subfacet->actions_len));
-        if (should_install != subfacet->installed || actions_changed) {
-            if (ok) {
-                may_log = !VLOG_DROP_WARN(&rl);
-                ok = false;
-            }
-
-            if (may_log) {
-                struct odputil_keybuf keybuf;
-                struct ofpbuf key;
-                struct ds s;
+        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;
+        }
 
-                ds_init(&s);
-                subfacet_get_key(subfacet, &keybuf, &key);
-                odp_flow_key_format(key.data, key.size, &s);
+        if (!subfacet_should_install(subfacet, subfacet->slow, &odp_actions)) {
+            continue;
+        }
 
-                ds_put_cstr(&s, ": inconsistency in subfacet");
-                if (should_install != subfacet->installed) {
-                    enum odp_key_fitness fitness = subfacet->key_fitness;
+        /* Inconsistency! */
+        if (ok) {
+            may_log = !VLOG_DROP_WARN(&rl);
+            ok = false;
+        }
+        if (!may_log) {
+            /* Rate-limited, skip reporting. */
+            continue;
+        }
 
-                    ds_put_format(&s, " (should%s have been installed)",
-                                  should_install ? "" : " not");
-                    ds_put_format(&s, " (may_set_up_flow=%s, fitness=%s)",
-                                  ctx.may_set_up_flow ? "true" : "false",
-                                  odp_key_fitness_to_string(fitness));
-                }
-                if (actions_changed) {
-                    ds_put_cstr(&s, " (actions were: ");
-                    format_odp_actions(&s, subfacet->actions,
-                                       subfacet->actions_len);
-                    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);
-                    ds_put_char(&s, ')');
-                }
-                VLOG_WARN("%s", ds_cstr(&s));
-                ds_destroy(&s);
-            }
+        ds_init(&s);
+        subfacet_get_key(subfacet, &keybuf, &key);
+        odp_flow_key_format(key.data, key.size, &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) {
+            ds_put_cstr(&s, " (actions were: ");
+            format_odp_actions(&s, subfacet->actions,
+                               subfacet->actions_len);
+            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);
+            ds_put_char(&s, ')');
         }
+        VLOG_WARN("%s", ds_cstr(&s));
+        ds_destroy(&s);
     }
     ofpbuf_uninit(&odp_actions);
 
@@ -3794,12 +4058,8 @@ facet_check_consistency(struct facet *facet)
  *     'facet' to the new rule and recompiles its actions.
  *
  *   - If the rule found is the same as 'facet''s current rule, leaves 'facet'
- *     where it is and recompiles its actions anyway.
- *
- *   - If there is none, destroys 'facet'.
- *
- * Returns true if 'facet' still exists, false if it has been destroyed. */
-static bool
+ *     where it is and recompiles its actions anyway. */
+static void
 facet_revalidate(struct facet *facet)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
@@ -3815,18 +4075,11 @@ facet_revalidate(struct facet *facet)
 
     struct rule_dpif *new_rule;
     struct subfacet *subfacet;
-    bool actions_changed;
     int i;
 
     COVERAGE_INC(facet_revalidate);
 
-    /* Determine the new rule. */
-    new_rule = rule_dpif_lookup(ofproto, &facet->flow, 0);
-    if (!new_rule) {
-        /* No new rule, so delete the facet. */
-        facet_remove(facet);
-        return false;
-    }
+    new_rule = rule_dpif_lookup(ofproto, &facet->flow);
 
     /* Calculate new datapath actions.
      *
@@ -3841,28 +4094,20 @@ facet_revalidate(struct facet *facet)
     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) {
-        bool should_install;
+        enum slow_path_reason slow;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
                               subfacet->initial_tci, new_rule, 0, NULL);
-        xlate_actions(&ctx, new_rule->up.actions, new_rule->up.n_actions,
+        xlate_actions(&ctx, new_rule->up.ofpacts, new_rule->up.ofpacts_len,
                       &odp_actions);
-        actions_changed = (subfacet->actions_len != odp_actions.size
-                           || memcmp(subfacet->actions, odp_actions.data,
-                                     subfacet->actions_len));
-
-        should_install = (ctx.may_set_up_flow
-                          && subfacet->key_fitness != ODP_FIT_TOO_LITTLE);
-        if (actions_changed || should_install != subfacet->installed) {
-            if (should_install) {
-                struct dpif_flow_stats stats;
 
-                subfacet_install(subfacet,
-                                 odp_actions.data, odp_actions.size, &stats);
-                subfacet_update_stats(subfacet, &stats);
-            } else {
-                subfacet_uninstall(subfacet);
-            }
+        slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
+        if (subfacet_should_install(subfacet, slow, &odp_actions)) {
+            struct dpif_flow_stats stats;
+
+            subfacet_install(subfacet,
+                             odp_actions.data, odp_actions.size, &stats, slow);
+            subfacet_update_stats(subfacet, &stats);
 
             if (!new_actions) {
                 new_actions = xcalloc(list_size(&facet->subfacets),
@@ -3884,23 +4129,24 @@ facet_revalidate(struct facet *facet)
     /* 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->may_install = ctx.may_set_up_flow;
     facet->has_learn = ctx.has_learn;
     facet->has_normal = ctx.has_normal;
     facet->has_fin_timeout = ctx.has_fin_timeout;
     facet->mirrors = ctx.mirrors;
-    if (new_actions) {
-        i = 0;
-        LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-            if (new_actions[i].odp_actions) {
-                free(subfacet->actions);
-                subfacet->actions = new_actions[i].odp_actions;
-                subfacet->actions_len = new_actions[i].actions_len;
-            }
-            i++;
+
+    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;
         }
-        free(new_actions);
+        i++;
     }
+    free(new_actions);
+
     if (facet->rule != new_rule) {
         COVERAGE_INC(facet_changed_rule);
         list_remove(&facet->list_node);
@@ -3909,8 +4155,6 @@ facet_revalidate(struct facet *facet)
         facet->used = new_rule->up.created;
         facet->prev_used = facet->used;
     }
-
-    return true;
 }
 
 /* Updates 'facet''s used time.  Caller is responsible for calling
@@ -3984,7 +4228,8 @@ flow_push_stats(struct rule_dpif *rule,
     action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule,
                           0, NULL);
     ctx.resubmit_stats = stats;
-    xlate_actions_for_side_effects(&ctx, rule->up.actions, rule->up.n_actions);
+    xlate_actions_for_side_effects(&ctx, rule->up.ofpacts,
+                                   rule->up.ofpacts_len);
 }
 \f
 /* Subfacets. */
@@ -4018,26 +4263,31 @@ subfacet_find__(struct ofproto_dpif *ofproto,
  * subfacet_make_actions(). */
 static struct subfacet *
 subfacet_create(struct facet *facet, enum odp_key_fitness key_fitness,
-                const struct nlattr *key, size_t key_len, ovs_be16 initial_tci)
+                const struct nlattr *key, size_t key_len,
+                ovs_be16 initial_tci, long long int now)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
     uint32_t key_hash = odp_flow_key_hash(key, key_len);
     struct subfacet *subfacet;
 
-    subfacet = subfacet_find__(ofproto, key, key_len, key_hash, &facet->flow);
-    if (subfacet) {
-        if (subfacet->facet == facet) {
-            return subfacet;
+    if (list_is_empty(&facet->subfacets)) {
+        subfacet = &facet->one_subfacet;
+    } else {
+        subfacet = subfacet_find__(ofproto, key, key_len, key_hash,
+                                   &facet->flow);
+        if (subfacet) {
+            if (subfacet->facet == facet) {
+                return subfacet;
+            }
+
+            /* This shouldn't happen. */
+            VLOG_ERR_RL(&rl, "subfacet with wrong facet");
+            subfacet_destroy(subfacet);
         }
 
-        /* This shouldn't happen. */
-        VLOG_ERR_RL(&rl, "subfacet with wrong facet");
-        subfacet_destroy(subfacet);
+        subfacet = xmalloc(sizeof *subfacet);
     }
 
-    subfacet = (list_is_empty(&facet->subfacets)
-                ? &facet->one_subfacet
-                : xmalloc(sizeof *subfacet));
     hmap_insert(&ofproto->subfacets, &subfacet->hmap_node, key_hash);
     list_push_back(&facet->subfacets, &subfacet->list_node);
     subfacet->facet = facet;
@@ -4049,12 +4299,15 @@ subfacet_create(struct facet *facet, enum odp_key_fitness key_fitness,
         subfacet->key = NULL;
         subfacet->key_len = 0;
     }
-    subfacet->used = time_msec();
+    subfacet->used = now;
     subfacet->dp_packet_count = 0;
     subfacet->dp_byte_count = 0;
     subfacet->actions_len = 0;
     subfacet->actions = NULL;
-    subfacet->installed = false;
+    subfacet->slow = (subfacet->key_fitness == ODP_FIT_TOO_LITTLE
+                      ? SLOW_MATCH
+                      : 0);
+    subfacet->path = SF_NOT_INSTALLED;
     subfacet->initial_tci = initial_tci;
 
     return subfacet;
@@ -4141,15 +4394,15 @@ subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
 
     action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
                           rule, 0, packet);
-    xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, odp_actions);
+    xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, odp_actions);
     facet->tags = ctx.tags;
-    facet->may_install = ctx.may_set_up_flow;
     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);
@@ -4167,10 +4420,13 @@ subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
 static int
 subfacet_install(struct subfacet *subfacet,
                  const struct nlattr *actions, size_t actions_len,
-                 struct dpif_flow_stats *stats)
+                 struct dpif_flow_stats *stats,
+                 enum slow_path_reason slow)
 {
     struct facet *facet = subfacet->facet;
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+    enum subfacet_path path = subfacet_want_path(slow);
+    uint64_t slow_path_stub[128 / 8];
     struct odputil_keybuf keybuf;
     enum dpif_flow_put_flags flags;
     struct ofpbuf key;
@@ -4181,6 +4437,12 @@ subfacet_install(struct subfacet *subfacet,
         flags |= DPIF_FP_ZERO_STATS;
     }
 
+    if (path == SF_SLOW_PATH) {
+        compose_slow_path(ofproto, &facet->flow, slow,
+                          slow_path_stub, sizeof slow_path_stub,
+                          &actions, &actions_len);
+    }
+
     subfacet_get_key(subfacet, &keybuf, &key);
     ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
                         actions, actions_len, stats);
@@ -4189,14 +4451,24 @@ subfacet_install(struct subfacet *subfacet,
         subfacet_reset_dp_stats(subfacet, stats);
     }
 
+    if (!ret) {
+        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)
 {
-    if (subfacet->installed) {
+    if (subfacet->path != SF_NOT_INSTALLED) {
         struct rule_dpif *rule = subfacet->facet->rule;
         struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
         struct odputil_keybuf keybuf;
@@ -4210,7 +4482,7 @@ subfacet_uninstall(struct subfacet *subfacet)
         if (!error) {
             subfacet_update_stats(subfacet, &stats);
         }
-        subfacet->installed = false;
+        subfacet->path = SF_NOT_INSTALLED;
     } else {
         assert(subfacet->dp_packet_count == 0);
         assert(subfacet->dp_byte_count == 0);
@@ -4273,8 +4545,21 @@ subfacet_update_stats(struct subfacet *subfacet,
 /* Rules. */
 
 static struct rule_dpif *
-rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow,
-                 uint8_t table_id)
+rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
+{
+    struct rule_dpif *rule;
+
+    rule = rule_dpif_lookup__(ofproto, flow, 0);
+    if (rule) {
+        return rule;
+    }
+
+    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 cls_rule *cls_rule;
     struct classifier *cls;
@@ -4298,6 +4583,23 @@ rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow,
     return rule_dpif_cast(rule_from_cls_rule(cls_rule));
 }
 
+static 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);
+    if (!port) {
+        VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, flow->in_port);
+        return ofproto->miss_rule;
+    }
+
+    if (port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
+        return ofproto->no_packet_in_rule;
+    }
+    return ofproto->miss_rule;
+}
+
 static void
 complete_operation(struct rule_dpif *rule)
 {
@@ -4336,8 +4638,8 @@ rule_construct(struct rule *rule_)
     uint8_t table_id;
     enum ofperr error;
 
-    error = validate_actions(rule->up.actions, rule->up.n_actions,
-                             &rule->up.cr.flow, ofproto->max_ports);
+    error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len,
+                          &rule->up.cr.flow, ofproto->max_ports);
     if (error) {
         return error;
     }
@@ -4422,14 +4724,14 @@ rule_execute(struct rule *rule_, const struct flow *flow,
     uint64_t odp_actions_stub[1024 / 8];
     struct ofpbuf odp_actions;
 
-    dpif_flow_stats_extract(flow, packet, &stats);
+    dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
     rule_credit_stats(rule, &stats);
 
     ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
     action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
                           rule, stats.tcp_flags, packet);
     ctx.resubmit_stats = &stats;
-    xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, &odp_actions);
+    xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions);
 
     execute_odp_actions(ofproto, flow, odp_actions.data,
                         odp_actions.size, packet);
@@ -4446,8 +4748,8 @@ rule_modify_actions(struct rule *rule_)
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
     enum ofperr error;
 
-    error = validate_actions(rule->up.actions, rule->up.n_actions,
-                             &rule->up.cr.flow, ofproto->max_ports);
+    error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len,
+                          &rule->up.cr.flow, ofproto->max_ports);
     if (error) {
         ofoperation_complete(rule->up.pending, error);
         return;
@@ -4469,7 +4771,7 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
     struct flow flow;
     int error;
 
-    flow_extract((struct ofpbuf *) packet, 0, 0, 0, &flow);
+    flow_extract(packet, 0, 0, 0, &flow);
     odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
                                       flow.vlan_tci);
     if (odp_port != ofport->odp_port) {
@@ -4500,15 +4802,49 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
 \f
 /* OpenFlow to datapath action translation. */
 
-static void do_xlate_actions(const union ofp_action *in, size_t n_in,
-                             struct action_xlate_ctx *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
+ * path.  (This is purely informational: it allows a human viewing "ovs-dpctl
+ * dump-flows" output to see why a flow is in the slow path.)
+ *
+ * The 'stub_size' bytes in 'stub' will be used to store the action.
+ * 'stub_size' must be large enough for the action.
+ *
+ * The action and its size will be stored in '*actionsp' and '*actions_lenp',
+ * respectively. */
+static void
+compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
+                  enum slow_path_reason slow,
+                  uint64_t *stub, size_t stub_size,
+                  const struct nlattr **actionsp, size_t *actions_lenp)
+{
+    union user_action_cookie cookie;
+    struct ofpbuf buf;
+
+    cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
+    cookie.slow_path.unused = 0;
+    cookie.slow_path.reason = slow;
+
+    ofpbuf_use_stack(&buf, stub, stub_size);
+    if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
+        uint32_t pid = dpif_port_get_pid(ofproto->dpif, UINT16_MAX);
+        odp_put_userspace_action(pid, &cookie, &buf);
+    } else {
+        put_userspace_action(ofproto, &buf, flow, &cookie);
+    }
+    *actionsp = buf.data;
+    *actions_lenp = buf.size;
+}
+
 static size_t
 put_userspace_action(const struct ofproto_dpif *ofproto,
                      struct ofpbuf *odp_actions,
                      const struct flow *flow,
-                     const struct user_action_cookie *cookie)
+                     const union user_action_cookie *cookie)
 {
     uint32_t pid;
 
@@ -4518,6 +4854,38 @@ put_userspace_action(const struct ofproto_dpif *ofproto,
     return odp_put_userspace_action(pid, cookie, odp_actions);
 }
 
+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)
+{
+    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. */
 static size_t
 compose_sflow_action(const struct ofproto_dpif *ofproto,
@@ -4525,24 +4893,15 @@ compose_sflow_action(const struct ofproto_dpif *ofproto,
                      const struct flow *flow,
                      uint32_t odp_port)
 {
-    uint32_t port_ifindex;
     uint32_t probability;
-    struct user_action_cookie cookie;
+    union user_action_cookie cookie;
     size_t sample_offset, actions_offset;
-    int cookie_offset, n_output;
+    int cookie_offset;
 
     if (!ofproto->sflow || flow->in_port == OFPP_NONE) {
         return 0;
     }
 
-    if (odp_port == OVSP_NONE) {
-        port_ifindex = 0;
-        n_output = 0;
-    } else {
-        port_ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port);
-        n_output = 1;
-    }
-
     sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
 
     /* Number of packets out of UINT_MAX to sample. */
@@ -4550,11 +4909,8 @@ compose_sflow_action(const struct ofproto_dpif *ofproto,
     nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
 
     actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
-
-    cookie.type = USER_ACTION_COOKIE_SFLOW;
-    cookie.data = port_ifindex;
-    cookie.n_output = n_output;
-    cookie.vlan_tci = 0;
+    compose_sflow_cookie(ofproto, htons(0), odp_port,
+                         odp_port == OVSP_NONE ? 0 : 1, &cookie);
     cookie_offset = put_userspace_action(ofproto, odp_actions, flow, &cookie);
 
     nl_msg_end_nested(odp_actions, actions_offset);
@@ -4582,27 +4938,18 @@ static void
 fix_sflow_action(struct action_xlate_ctx *ctx)
 {
     const struct flow *base = &ctx->base_flow;
-    struct user_action_cookie *cookie;
+    union user_action_cookie *cookie;
 
     if (!ctx->user_cookie_offset) {
         return;
     }
 
     cookie = ofpbuf_at(ctx->odp_actions, ctx->user_cookie_offset,
-                     sizeof(*cookie));
-    assert(cookie != NULL);
+                       sizeof(*cookie));
     assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
 
-    if (ctx->sflow_n_outputs) {
-        cookie->data = dpif_sflow_odp_port_to_ifindex(ctx->ofproto->sflow,
-                                                    ctx->sflow_odp_port);
-    }
-    if (ctx->sflow_n_outputs >= 255) {
-        cookie->n_output = 255;
-    } else {
-        cookie->n_output = ctx->sflow_n_outputs;
-    }
-    cookie->vlan_tci = base->vlan_tci;
+    compose_sflow_cookie(ctx->ofproto, base->vlan_tci,
+                         ctx->sflow_odp_port, ctx->sflow_n_outputs, cookie);
 }
 
 static void
@@ -4657,7 +5004,7 @@ compose_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port)
 
 static void
 xlate_table_action(struct action_xlate_ctx *ctx,
-                   uint16_t in_port, uint8_t table_id)
+                   uint16_t in_port, uint8_t table_id, bool may_packet_in)
 {
     if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
         struct ofproto_dpif *ofproto = ctx->ofproto;
@@ -4671,7 +5018,7 @@ xlate_table_action(struct action_xlate_ctx *ctx,
         /* Look up a flow with 'in_port' as the input port. */
         old_in_port = ctx->flow.in_port;
         ctx->flow.in_port = in_port;
-        rule = rule_dpif_lookup(ofproto, &ctx->flow, table_id);
+        rule = rule_dpif_lookup__(ofproto, &ctx->flow, table_id);
 
         /* Tag the flow. */
         if (table_id > 0 && table_id < N_TABLES) {
@@ -4693,6 +5040,17 @@ xlate_table_action(struct action_xlate_ctx *ctx,
             ctx->resubmit_hook(ctx, rule);
         }
 
+        if (rule == NULL && may_packet_in) {
+            /* TODO: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(ofproto, &ctx->flow);
+        }
+
         if (rule) {
             struct rule_dpif *old_rule = ctx->rule;
 
@@ -4702,7 +5060,7 @@ xlate_table_action(struct action_xlate_ctx *ctx,
 
             ctx->recurse++;
             ctx->rule = rule;
-            do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx);
+            do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
             ctx->rule = old_rule;
             ctx->recurse--;
         }
@@ -4718,18 +5076,23 @@ xlate_table_action(struct action_xlate_ctx *ctx,
 }
 
 static void
-xlate_resubmit_table(struct action_xlate_ctx *ctx,
-                     const struct nx_action_resubmit *nar)
+xlate_ofpact_resubmit(struct action_xlate_ctx *ctx,
+                      const struct ofpact_resubmit *resubmit)
 {
     uint16_t in_port;
     uint8_t table_id;
 
-    in_port = (nar->in_port == htons(OFPP_IN_PORT)
-               ? ctx->flow.in_port
-               : ntohs(nar->in_port));
-    table_id = nar->table == 255 ? ctx->table_id : nar->table;
+    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);
+    xlate_table_action(ctx, in_port, table_id, false);
 }
 
 static void
@@ -4762,7 +5125,7 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len,
     struct ofputil_packet_in pin;
     struct ofpbuf *packet;
 
-    ctx->may_set_up_flow = false;
+    ctx->slow |= SLOW_CONTROLLER;
     if (!ctx->packet) {
         return;
     }
@@ -4821,7 +5184,7 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len,
 }
 
 static bool
-compose_dec_ttl(struct action_xlate_ctx *ctx)
+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)) {
@@ -4832,7 +5195,12 @@ compose_dec_ttl(struct action_xlate_ctx *ctx)
         ctx->flow.nw_ttl--;
         return false;
     } else {
-        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
+        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;
@@ -4840,8 +5208,8 @@ compose_dec_ttl(struct action_xlate_ctx *ctx)
 }
 
 static void
-xlate_output_action__(struct action_xlate_ctx *ctx,
-                      uint16_t port, uint16_t max_len)
+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;
 
@@ -4852,7 +5220,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
         compose_output_action(ctx, ctx->flow.in_port);
         break;
     case OFPP_TABLE:
-        xlate_table_action(ctx, ctx->flow.in_port, ctx->table_id);
+        xlate_table_action(ctx, ctx->flow.in_port, 0, may_packet_in);
         break;
     case OFPP_NORMAL:
         xlate_normal(ctx);
@@ -4888,44 +5256,32 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
 
 static void
 xlate_output_reg_action(struct action_xlate_ctx *ctx,
-                        const struct nx_action_output_reg *naor)
+                        const struct ofpact_output_reg *or)
 {
-    struct mf_subfield src;
-    uint64_t ofp_port;
-
-    nxm_decode(&src, naor->src, naor->ofs_nbits);
-    ofp_port = mf_get_subfield(&src, &ctx->flow);
-
-    if (ofp_port <= UINT16_MAX) {
-        xlate_output_action__(ctx, ofp_port, ntohs(naor->max_len));
+    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_output_action(struct action_xlate_ctx *ctx,
-                    const struct ofp_action_output *oao)
-{
-    xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
-}
-
 static void
 xlate_enqueue_action(struct action_xlate_ctx *ctx,
-                     const struct ofp_action_enqueue *oae)
+                     const struct ofpact_enqueue *enqueue)
 {
-    uint16_t ofp_port;
+    uint16_t ofp_port = enqueue->port;
+    uint32_t queue_id = enqueue->queue;
     uint32_t flow_priority, priority;
     int error;
 
-    error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
-                                   &priority);
+    /* Translate queue to priority. */
+    error = dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &priority);
     if (error) {
         /* Fall back to ordinary output action. */
-        xlate_output_action__(ctx, ntohs(oae->port), 0);
+        xlate_output_action(ctx, enqueue->port, 0, false);
         return;
     }
 
-    /* Figure out datapath output port. */
-    ofp_port = ntohs(oae->port);
+    /* Check output port. */
     if (ofp_port == OFPP_IN_PORT) {
         ofp_port = ctx->flow.in_port;
     } else if (ofp_port == ctx->flow.in_port) {
@@ -4947,21 +5303,16 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx,
 }
 
 static void
-xlate_set_queue_action(struct action_xlate_ctx *ctx,
-                       const struct nx_action_set_queue *nasq)
+xlate_set_queue_action(struct action_xlate_ctx *ctx, uint32_t queue_id)
 {
-    uint32_t priority;
-    int error;
+    uint32_t skb_priority;
 
-    error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id),
-                                   &priority);
-    if (error) {
-        /* Couldn't translate queue to a priority, so ignore.  A warning
+    if (!dpif_queue_to_priority(ctx->ofproto->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. */
-        return;
     }
-
-    ctx->flow.skb_priority = priority;
 }
 
 struct xlate_reg_state {
@@ -4971,9 +5322,9 @@ struct xlate_reg_state {
 
 static void
 xlate_autopath(struct action_xlate_ctx *ctx,
-               const struct nx_action_autopath *naa)
+               const struct ofpact_autopath *ap)
 {
-    uint16_t ofp_port = ntohl(naa->id);
+    uint16_t ofp_port = ap->port;
     struct ofport_dpif *port = get_ofp_port(ctx->ofproto, ofp_port);
 
     if (!port || !port->bundle) {
@@ -4986,7 +5337,7 @@ xlate_autopath(struct action_xlate_ctx *ctx,
             ofp_port = slave->up.ofp_port;
         }
     }
-    autopath_execute(naa, &ctx->flow, ofp_port);
+    nxm_reg_load(&ap->dst, ofp_port, &ctx->flow);
 }
 
 static bool
@@ -5011,15 +5362,32 @@ slave_enabled_cb(uint16_t ofp_port, void *ofproto_)
     }
 }
 
+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 nx_action_learn *learn)
+                   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;
 
-    learn_execute(learn, &ctx->flow, &fm);
+    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)) {
@@ -5027,7 +5395,7 @@ xlate_learn_action(struct action_xlate_ctx *ctx,
                   ofperr_get_name(error));
     }
 
-    free(fm.actions);
+    ofpbuf_uninit(&ofpacts);
 }
 
 /* Reduces '*timeout' to no more than 'max'.  A value of zero in either case
@@ -5042,13 +5410,13 @@ reduce_timeout(uint16_t max, uint16_t *timeout)
 
 static void
 xlate_fin_timeout(struct action_xlate_ctx *ctx,
-                  const struct nx_action_fin_timeout *naft)
+                  const struct ofpact_fin_timeout *oft)
 {
     if (ctx->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
         struct rule_dpif *rule = ctx->rule;
 
-        reduce_timeout(ntohs(naft->fin_idle_timeout), &rule->up.idle_timeout);
-        reduce_timeout(ntohs(naft->fin_hard_timeout), &rule->up.hard_timeout);
+        reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
+        reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
     }
 }
 
@@ -5074,13 +5442,12 @@ may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
 }
 
 static void
-do_xlate_actions(const union ofp_action *in, size_t n_in,
+do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
                  struct action_xlate_ctx *ctx)
 {
     const struct ofport_dpif *port;
-    const union ofp_action *ia;
     bool was_evictable = true;
-    size_t left;
+    const struct ofpact *a;
 
     port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
     if (port && !may_receive(port, ctx)) {
@@ -5093,181 +5460,146 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
         was_evictable = ctx->rule->up.evictable;
         ctx->rule->up.evictable = false;
     }
-    OFPUTIL_ACTION_FOR_EACH_UNSAFE (ia, left, in, n_in) {
-        const struct ofp_action_dl_addr *oada;
-        const struct nx_action_resubmit *nar;
-        const struct nx_action_set_tunnel *nast;
-        const struct nx_action_set_queue *nasq;
-        const struct nx_action_multipath *nam;
-        const struct nx_action_autopath *naa;
-        const struct nx_action_bundle *nab;
-        const struct nx_action_output_reg *naor;
-        const struct nx_action_controller *nac;
-        enum ofputil_action_code code;
-        ovs_be64 tun_id;
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        struct ofpact_controller *controller;
 
         if (ctx->exit) {
             break;
         }
 
-        code = ofputil_decode_action_unsafe(ia);
-        switch (code) {
-        case OFPUTIL_OFPAT10_OUTPUT:
-            xlate_output_action(ctx, &ia->output);
+        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 OFPUTIL_OFPAT10_SET_VLAN_VID:
+        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 |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
+            ctx->flow.vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
+                                   | htons(VLAN_CFI));
             break;
 
-        case OFPUTIL_OFPAT10_SET_VLAN_PCP:
+        case OFPACT_SET_VLAN_PCP:
             ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
-            ctx->flow.vlan_tci |= htons(
-                (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
+            ctx->flow.vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
+                                         << VLAN_PCP_SHIFT)
+                                        | VLAN_CFI);
             break;
 
-        case OFPUTIL_OFPAT10_STRIP_VLAN:
+        case OFPACT_STRIP_VLAN:
             ctx->flow.vlan_tci = htons(0);
             break;
 
-        case OFPUTIL_OFPAT10_SET_DL_SRC:
-            oada = ((struct ofp_action_dl_addr *) ia);
-            memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
+        case OFPACT_SET_ETH_SRC:
+            memcpy(ctx->flow.dl_src, ofpact_get_SET_ETH_SRC(a)->mac,
+                   ETH_ADDR_LEN);
             break;
 
-        case OFPUTIL_OFPAT10_SET_DL_DST:
-            oada = ((struct ofp_action_dl_addr *) ia);
-            memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
+        case OFPACT_SET_ETH_DST:
+            memcpy(ctx->flow.dl_dst, ofpact_get_SET_ETH_DST(a)->mac,
+                   ETH_ADDR_LEN);
             break;
 
-        case OFPUTIL_OFPAT10_SET_NW_SRC:
-            ctx->flow.nw_src = ia->nw_addr.nw_addr;
+        case OFPACT_SET_IPV4_SRC:
+            ctx->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
             break;
 
-        case OFPUTIL_OFPAT10_SET_NW_DST:
-            ctx->flow.nw_dst = ia->nw_addr.nw_addr;
+        case OFPACT_SET_IPV4_DST:
+            ctx->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
             break;
 
-        case OFPUTIL_OFPAT10_SET_NW_TOS:
+        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 |= ia->nw_tos.nw_tos & IP_DSCP_MASK;
+                ctx->flow.nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp;
             }
             break;
 
-        case OFPUTIL_OFPAT10_SET_TP_SRC:
-            ctx->flow.tp_src = ia->tp_port.tp_port;
-            break;
-
-        case OFPUTIL_OFPAT10_SET_TP_DST:
-            ctx->flow.tp_dst = ia->tp_port.tp_port;
+        case OFPACT_SET_L4_SRC_PORT:
+            ctx->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
             break;
 
-        case OFPUTIL_OFPAT10_ENQUEUE:
-            xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
+        case OFPACT_SET_L4_DST_PORT:
+            ctx->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
             break;
 
-        case OFPUTIL_NXAST_RESUBMIT:
-            nar = (const struct nx_action_resubmit *) ia;
-            xlate_table_action(ctx, ntohs(nar->in_port), ctx->table_id);
+        case OFPACT_RESUBMIT:
+            xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
             break;
 
-        case OFPUTIL_NXAST_RESUBMIT_TABLE:
-            xlate_resubmit_table(ctx, (const struct nx_action_resubmit *) ia);
+        case OFPACT_SET_TUNNEL:
+            ctx->flow.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
             break;
 
-        case OFPUTIL_NXAST_SET_TUNNEL:
-            nast = (const struct nx_action_set_tunnel *) ia;
-            tun_id = htonll(ntohl(nast->tun_id));
-            ctx->flow.tun_id = tun_id;
+        case OFPACT_SET_QUEUE:
+            xlate_set_queue_action(ctx, ofpact_get_SET_QUEUE(a)->queue_id);
             break;
 
-        case OFPUTIL_NXAST_SET_QUEUE:
-            nasq = (const struct nx_action_set_queue *) ia;
-            xlate_set_queue_action(ctx, nasq);
-            break;
-
-        case OFPUTIL_NXAST_POP_QUEUE:
+        case OFPACT_POP_QUEUE:
             ctx->flow.skb_priority = ctx->orig_skb_priority;
             break;
 
-        case OFPUTIL_NXAST_REG_MOVE:
-            nxm_execute_reg_move((const struct nx_action_reg_move *) ia,
-                                 &ctx->flow);
+        case OFPACT_REG_MOVE:
+            nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->flow);
             break;
 
-        case OFPUTIL_NXAST_REG_LOAD:
-            nxm_execute_reg_load((const struct nx_action_reg_load *) ia,
-                                 &ctx->flow);
+        case OFPACT_REG_LOAD:
+            nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
             break;
 
-        case OFPUTIL_NXAST_NOTE:
-            /* Nothing to do. */
-            break;
-
-        case OFPUTIL_NXAST_SET_TUNNEL64:
-            tun_id = ((const struct nx_action_set_tunnel64 *) ia)->tun_id;
-            ctx->flow.tun_id = tun_id;
+        case OFPACT_DEC_TTL:
+            if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
+                goto out;
+            }
             break;
 
-        case OFPUTIL_NXAST_MULTIPATH:
-            nam = (const struct nx_action_multipath *) ia;
-            multipath_execute(nam, &ctx->flow);
+        case OFPACT_NOTE:
+            /* Nothing to do. */
             break;
 
-        case OFPUTIL_NXAST_AUTOPATH:
-            naa = (const struct nx_action_autopath *) ia;
-            xlate_autopath(ctx, naa);
+        case OFPACT_MULTIPATH:
+            multipath_execute(ofpact_get_MULTIPATH(a), &ctx->flow);
             break;
 
-        case OFPUTIL_NXAST_BUNDLE:
-            ctx->ofproto->has_bundle_action = true;
-            nab = (const struct nx_action_bundle *) ia;
-            xlate_output_action__(ctx, bundle_execute(nab, &ctx->flow,
-                                                      slave_enabled_cb,
-                                                      ctx->ofproto), 0);
+        case OFPACT_AUTOPATH:
+            xlate_autopath(ctx, ofpact_get_AUTOPATH(a));
             break;
 
-        case OFPUTIL_NXAST_BUNDLE_LOAD:
+        case OFPACT_BUNDLE:
             ctx->ofproto->has_bundle_action = true;
-            nab = (const struct nx_action_bundle *) ia;
-            bundle_execute_load(nab, &ctx->flow, slave_enabled_cb,
-                                ctx->ofproto);
+            xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
             break;
 
-        case OFPUTIL_NXAST_OUTPUT_REG:
-            naor = (const struct nx_action_output_reg *) ia;
-            xlate_output_reg_action(ctx, naor);
+        case OFPACT_OUTPUT_REG:
+            xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a));
             break;
 
-        case OFPUTIL_NXAST_LEARN:
+        case OFPACT_LEARN:
             ctx->has_learn = true;
             if (ctx->may_learn) {
-                xlate_learn_action(ctx, (const struct nx_action_learn *) ia);
+                xlate_learn_action(ctx, ofpact_get_LEARN(a));
             }
             break;
 
-        case OFPUTIL_NXAST_DEC_TTL:
-            if (compose_dec_ttl(ctx)) {
-                goto out;
-            }
-            break;
-
-        case OFPUTIL_NXAST_EXIT:
+        case OFPACT_EXIT:
             ctx->exit = true;
             break;
 
-        case OFPUTIL_NXAST_FIN_TIMEOUT:
+        case OFPACT_FIN_TIMEOUT:
             ctx->has_fin_timeout = true;
-            xlate_fin_timeout(ctx, (const struct nx_action_fin_timeout *) ia);
-            break;
-
-        case OFPUTIL_NXAST_CONTROLLER:
-            nac = (const struct nx_action_controller *) ia;
-            execute_controller_action(ctx, ntohs(nac->max_len), nac->reason,
-                                      ntohs(nac->controller_id));
+            xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a));
             break;
         }
     }
@@ -5300,14 +5632,15 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
     ctx->may_learn = packet != NULL;
     ctx->tcp_flags = tcp_flags;
     ctx->resubmit_hook = NULL;
+    ctx->report_hook = NULL;
     ctx->resubmit_stats = NULL;
 }
 
-/* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions in
- * 'odp_actions', using 'ctx'. */
+/* 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 union ofp_action *in, size_t n_in,
+              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
@@ -5315,6 +5648,8 @@ xlate_actions(struct action_xlate_ctx *ctx,
      * tracing purposes. */
     static bool hit_resubmit_limit;
 
+    enum slow_path_reason special;
+
     COVERAGE_INC(ofproto_dpif_xlate);
 
     ofpbuf_clear(odp_actions);
@@ -5322,7 +5657,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
 
     ctx->odp_actions = odp_actions;
     ctx->tags = 0;
-    ctx->may_set_up_flow = true;
+    ctx->slow = 0;
     ctx->has_learn = false;
     ctx->has_normal = false;
     ctx->has_fin_timeout = false;
@@ -5367,14 +5702,15 @@ xlate_actions(struct action_xlate_ctx *ctx,
         }
     }
 
-    if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) {
-        ctx->may_set_up_flow = false;
+    special = process_special(ctx->ofproto, &ctx->flow, ctx->packet);
+    if (special) {
+        ctx->slow |= special;
     } else {
         static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
         ovs_be16 initial_tci = ctx->base_flow.vlan_tci;
 
         add_sflow_action(ctx);
-        do_xlate_actions(in, n_in, ctx);
+        do_xlate_actions(ofpacts, ofpacts_len, ctx);
 
         if (ctx->max_resubmit_trigger && !ctx->resubmit_hook) {
             if (!hit_resubmit_limit) {
@@ -5395,7 +5731,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
         if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow,
                                      ctx->odp_actions->data,
                                      ctx->odp_actions->size)) {
-            ctx->may_set_up_flow = false;
+            ctx->slow |= SLOW_IN_BAND;
             if (ctx->packet
                 && connmgr_msg_in_hook(ctx->ofproto->up.connmgr, &ctx->flow,
                                        ctx->packet)) {
@@ -5409,19 +5745,28 @@ xlate_actions(struct action_xlate_ctx *ctx,
     }
 }
 
-/* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions,
- * using 'ctx', and discards the datapath actions. */
+/* 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 union ofp_action *in, size_t n_in)
+                               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, in, n_in, &odp_actions);
+    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. */
 
@@ -5604,47 +5949,6 @@ vlan_is_mirrored(const struct ofmirror *m, int vlan)
     return !m->vlans || bitmap_is_set(m->vlans, vlan);
 }
 
-/* Returns true if a packet with Ethernet destination MAC 'dst' may be mirrored
- * to a VLAN.  In general most packets may be mirrored but we want to drop
- * protocols that may confuse switches. */
-static bool
-eth_dst_may_rspan(const uint8_t dst[ETH_ADDR_LEN])
-{
-    /* If you change this function's behavior, please update corresponding
-     * documentation in vswitch.xml at the same time. */
-    if (dst[0] != 0x01) {
-        /* All the currently banned MACs happen to start with 01 currently, so
-         * this is a quick way to eliminate most of the good ones. */
-    } else {
-        if (eth_addr_is_reserved(dst)) {
-            /* Drop STP, IEEE pause frames, and other reserved protocols
-             * (01-80-c2-00-00-0x). */
-            return false;
-        }
-
-        if (dst[0] == 0x01 && dst[1] == 0x00 && dst[2] == 0x0c) {
-            /* Cisco OUI. */
-            if ((dst[3] & 0xfe) == 0xcc &&
-                (dst[4] & 0xfe) == 0xcc &&
-                (dst[5] & 0xfe) == 0xcc) {
-                /* Drop the following protocols plus others following the same
-                   pattern:
-
-                   CDP, VTP, DTP, PAgP  (01-00-0c-cc-cc-cc)
-                   Spanning Tree PVSTP+ (01-00-0c-cc-cc-cd)
-                   STP Uplink Fast      (01-00-0c-cd-cd-cd) */
-                return false;
-            }
-
-            if (!(dst[3] | dst[4] | dst[5])) {
-                /* Drop Inter Switch Link packets (01-00-0c-00-00-00). */
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
 static void
 add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow)
 {
@@ -5719,8 +6023,8 @@ add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow)
         ctx->mirrors |= m->dup_mirrors;
         if (m->out) {
             output_normal(ctx, m->out, vlan);
-        } else if (eth_dst_may_rspan(orig_flow->dl_dst)
-                   && vlan != 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) {
@@ -5817,8 +6121,8 @@ update_learning_table(struct ofproto_dpif *ofproto,
 }
 
 static struct ofbundle *
-lookup_input_bundle(struct ofproto_dpif *ofproto, uint16_t in_port, bool warn,
-                    struct ofport_dpif **in_ofportp)
+lookup_input_bundle(const struct ofproto_dpif *ofproto, uint16_t in_port,
+                    bool warn, struct ofport_dpif **in_ofportp)
 {
     struct ofport_dpif *ofport;
 
@@ -5846,6 +6150,9 @@ lookup_input_bundle(struct ofproto_dpif *ofproto, uint16_t in_port, bool warn,
      *   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);
@@ -5871,14 +6178,17 @@ lookup_input_bundle(struct ofproto_dpif *ofproto, uint16_t in_port, bool warn,
  * so in one special case.
  */
 static bool
-is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
-              struct ofport_dpif *in_port, uint16_t vlan, tag_type *tags)
+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 (eth_addr_is_reserved(flow->dl_dst) && !ofproto->up.forward_bpdu) {
+    if (!ofproto->up.forward_bpdu && eth_addr_is_reserved(flow->dl_dst)) {
+        xlate_report(ctx, "packet has reserved destination MAC, dropping");
         return false;
     }
 
@@ -5886,11 +6196,12 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
         struct mac_entry *mac;
 
         switch (bond_check_admissibility(in_bundle->bond, in_port,
-                                         flow->dl_dst, tags)) {
+                                         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:
@@ -5898,6 +6209,8 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
             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;
@@ -5921,6 +6234,7 @@ xlate_normal(struct action_xlate_ctx *ctx)
     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;
     }
 
@@ -5933,6 +6247,7 @@ xlate_normal(struct action_xlate_ctx *ctx)
                          "VLAN tag received on port %s",
                          ctx->ofproto->up.name, in_bundle->name);
         }
+        xlate_report(ctx, "partial VLAN tag, dropping");
         return;
     }
 
@@ -5944,19 +6259,20 @@ xlate_normal(struct action_xlate_ctx *ctx)
                          "%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->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) {
+    if (in_port && !is_admissible(ctx, in_port, vlan)) {
         return;
     }
 
@@ -5970,11 +6286,15 @@ xlate_normal(struct action_xlate_ctx *ctx)
                               &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)
@@ -6072,7 +6392,7 @@ table_update_taggable(struct ofproto_dpif *ofproto, uint8_t table_id)
     if (table->catchall_table != catchall || table->other_table != other) {
         table->catchall_table = catchall;
         table->other_table = other;
-        ofproto->need_revalidate = true;
+        ofproto->need_revalidate = REV_FLOW_TABLE;
     }
 }
 
@@ -6096,7 +6416,7 @@ rule_invalidate(const struct rule_dpif *rule)
         if (table->other_table && rule->tag) {
             tag_set_add(&ofproto->revalidate_set, rule->tag);
         } else {
-            ofproto->need_revalidate = true;
+            ofproto->need_revalidate = REV_FLOW_TABLE;
         }
     }
 }
@@ -6108,7 +6428,7 @@ set_frag_handling(struct ofproto *ofproto_,
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 
     if (frag_handling != OFPC_FRAG_REASM) {
-        ofproto->need_revalidate = true;
+        ofproto->need_revalidate = REV_RECONFIGURE;
         return true;
     } else {
         return false;
@@ -6118,7 +6438,7 @@ set_frag_handling(struct ofproto *ofproto_,
 static enum ofperr
 packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
            const struct flow *flow,
-           const union ofp_action *ofp_actions, size_t n_ofp_actions)
+           const struct ofpact *ofpacts, size_t ofpacts_len)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     enum ofperr error;
@@ -6127,8 +6447,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
         return OFPERR_NXBRC_BAD_IN_PORT;
     }
 
-    error = validate_actions(ofp_actions, n_ofp_actions, flow,
-                             ofproto->max_ports);
+    error = ofpacts_check(ofpacts, ofpacts_len, flow, ofproto->max_ports);
     if (!error) {
         struct odputil_keybuf keybuf;
         struct dpif_flow_stats stats;
@@ -6142,7 +6461,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
         ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
         odp_flow_key_from_flow(&key, flow);
 
-        dpif_flow_stats_extract(flow, packet, &stats);
+        dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
 
         action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, NULL,
                               packet_get_tcp_flags(packet, flow), packet);
@@ -6150,7 +6469,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
 
         ofpbuf_use_stub(&odp_actions,
                         odp_actions_stub, sizeof odp_actions_stub);
-        xlate_actions(&ctx, ofp_actions, n_ofp_actions, &odp_actions);
+        xlate_actions(&ctx, ofpacts, ofpacts_len, &odp_actions);
         dpif_execute(ofproto->dpif, key.data, key.size,
                      odp_actions.data, odp_actions.size, packet);
         ofpbuf_uninit(&odp_actions);
@@ -6196,11 +6515,10 @@ send_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet)
         struct ofexpired expired;
 
         LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-            if (subfacet->installed) {
+            if (subfacet->path == SF_FAST_PATH) {
                 struct dpif_flow_stats stats;
 
-                subfacet_install(subfacet, subfacet->actions,
-                                 subfacet->actions_len, &stats);
+                subfacet_reinstall(subfacet, &stats);
                 subfacet_update_stats(subfacet, &stats);
             }
         }
@@ -6308,7 +6626,7 @@ trace_format_rule(struct ds *result, uint8_t table_id, int level,
 
     ds_put_char_multiple(result, '\t', level);
     ds_put_cstr(result, "OpenFlow ");
-    ofp_print_actions(result, rule->up.actions, rule->up.n_actions);
+    ofpacts_format(rule->up.ofpacts, rule->up.ofpacts_len, result);
     ds_put_char(result, '\n');
 }
 
@@ -6366,6 +6684,17 @@ trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
     trace_format_rule(result, ctx->table_id, ctx->recurse + 1, rule);
 }
 
+static void
+trace_report(struct action_xlate_ctx *ctx, const char *s)
+{
+    struct trace_ctx *trace = CONTAINER_OF(ctx, struct trace_ctx, ctx);
+    struct ds *result = trace->result;
+
+    ds_put_char_multiple(result, '\t', ctx->recurse);
+    ds_put_cstr(result, s);
+    ds_put_char(result, '\n');
+}
+
 static void
 ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
                       void *aux OVS_UNUSED)
@@ -6393,23 +6722,48 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
         /* ofproto/trace dpname flow [-generate] */
         const char *flow_s = argv[2];
         const char *generate_s = argv[3];
-        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;
-        }
+        /* 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;
+            }
 
-        /* Convert odp_key to flow. */
-        error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data,
-                                              odp_key.size, &flow,
-                                              &initial_tci, NULL);
-        if (error == ODP_FIT_ERROR) {
-            unixctl_command_reply_error(conn, "Invalid flow");
-            goto exit;
+            /* Convert odp_key to flow. */
+            error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data,
+                                                  odp_key.size, &flow,
+                                                  &initial_tci, NULL);
+            if (error == ODP_FIT_ERROR) {
+                unixctl_command_reply_error(conn, "Invalid flow");
+                goto exit;
+            }
+        } 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);
+                goto exit;
+            }
+
+            initial_tci = flow.vlan_tci;
+            vsp_adjust_flow(ofproto, &flow);
         }
 
         /* Generate a packet, if requested. */
@@ -6466,8 +6820,16 @@ 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, 0);
+    rule = rule_dpif_lookup(ofproto, flow);
+
     trace_format_rule(ds, 0, 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");
+    }
+
     if (rule) {
         uint64_t odp_actions_stub[1024 / 8];
         struct ofpbuf odp_actions;
@@ -6483,7 +6845,8 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
         action_xlate_ctx_init(&trace.ctx, ofproto, flow, initial_tci,
                               rule, tcp_flags, packet);
         trace.ctx.resubmit_hook = trace_resubmit;
-        xlate_actions(&trace.ctx, rule->up.actions, rule->up.n_actions,
+        trace.ctx.report_hook = trace_report;
+        xlate_actions(&trace.ctx, rule->up.ofpacts, rule->up.ofpacts_len,
                       &odp_actions);
 
         ds_put_char(ds, '\n');
@@ -6492,12 +6855,49 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
         format_odp_actions(ds, odp_actions.data, odp_actions.size);
         ofpbuf_uninit(&odp_actions);
 
-        if (!trace.ctx.may_set_up_flow) {
-            if (packet) {
-                ds_put_cstr(ds, "\nThis flow is not cachable.");
-            } else {
-                ds_put_cstr(ds, "\nThe datapath actions are incomplete--"
-                            "for complete actions, please supply a packet.");
+        if (trace.ctx.slow) {
+            enum slow_path_reason 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_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.");
             }
         }
     }
@@ -6534,7 +6934,7 @@ ofproto_dpif_self_check__(struct ofproto_dpif *ofproto, struct ds *reply)
         }
     }
     if (errors) {
-        ofproto->need_revalidate = true;
+        ofproto->need_revalidate = REV_INCONSISTENCY;
     }
 
     if (errors) {
@@ -6613,7 +7013,7 @@ set_realdev(struct ofport *ofport_, uint16_t realdev_ofp_port, int vid)
         return 0;
     }
 
-    ofproto->need_revalidate = true;
+    ofproto->need_revalidate = REV_RECONFIGURE;
 
     if (ofport->realdev_ofp_port) {
         vsp_remove(ofport);
@@ -6710,6 +7110,30 @@ vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto,
     return 0;
 }
 
+/* Given 'flow', a flow representing a packet received on 'ofproto', checks
+ * whether 'flow->in_port' represents a Linux VLAN device.  If so, changes
+ * 'flow->in_port' to the "real" device backing the VLAN device, sets
+ * 'flow->vlan_tci' to the VLAN VID, and returns true.  Otherwise (which is
+ * always the case unless VLAN splinters are enabled), returns false without
+ * making any changes. */
+static bool
+vsp_adjust_flow(const struct ofproto_dpif *ofproto, struct flow *flow)
+{
+    uint16_t realdev;
+    int vid;
+
+    realdev = vsp_vlandev_to_realdev(ofproto, flow->in_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->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
+    return true;
+}
+
 static void
 vsp_remove(struct ofport_dpif *port)
 {
@@ -6764,6 +7188,7 @@ const struct ofproto_class ofproto_dpif_class = {
     run,
     run_fast,
     wait,
+    get_memory_usage,
     flush,
     get_features,
     get_tables,
@@ -6798,6 +7223,7 @@ const struct ofproto_class ofproto_dpif_class = {
     set_sflow,
     set_cfm,
     get_cfm_fault,
+    get_cfm_opup,
     get_cfm_remote_mpids,
     get_cfm_health,
     set_stp,
index 7b0e478..15dc347 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,7 +29,9 @@
 #include "shash.h"
 #include "timeval.h"
 
+struct ofpact;
 struct ofputil_flow_mod;
+struct simap;
 
 /* An OpenFlow switch.
  *
@@ -73,6 +75,12 @@ struct ofproto {
     unsigned int n_pending;     /* list_size(&pending). */
     struct hmap deletions;      /* All OFOPERATION_DELETE "ofoperation"s. */
 
+    /* Flow table operation logging. */
+    int n_add, n_delete, n_modify; /* Number of unreported ops of each kind. */
+    long long int first_op, last_op; /* Range of times for unreported ops. */
+    long long int next_op_report;    /* Time to report ops, or LLONG_MAX. */
+    long long int op_backoff;        /* Earliest time to report ops again. */
+
     /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
      *
      * This is deprecated.  It is only for compatibility with broken device
@@ -177,8 +185,13 @@ struct rule {
     struct heap_node evg_node;   /* In eviction_group's "rules" heap. */
     struct eviction_group *eviction_group; /* NULL if not in any group. */
 
-    union ofp_action *actions;   /* OpenFlow actions. */
-    int n_actions;               /* Number of elements in actions[]. */
+    struct ofpact *ofpacts;      /* Sequence of "struct ofpacts". */
+    unsigned int ofpacts_len;    /* Size of 'ofpacts', in bytes. */
+
+    /* Flow monitors. */
+    enum nx_flow_monitor_flags monitor_flags;
+    uint64_t add_seqno;         /* Sequence number when added. */
+    uint64_t modify_seqno;      /* Sequence number when changed. */
 };
 
 static inline struct rule *
@@ -191,9 +204,15 @@ 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);
+
 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 ofproto_rule_is_hidden(const struct rule *);
+
 /* ofproto class structure, to be defined by each ofproto implementation.
  *
  *
@@ -380,6 +399,9 @@ struct ofproto_class {
      *   - Call ofproto_rule_expire() for each OpenFlow flow that has reached
      *     its hard_timeout or idle_timeout, to expire the flow.
      *
+     *     (But rules that are part of a pending operation, e.g. rules for
+     *     which ->pending is true, may not expire.)
+     *
      * Returns 0 if successful, otherwise a positive errno value. */
     int (*run)(struct ofproto *ofproto);
 
@@ -396,6 +418,13 @@ struct ofproto_class {
      * poll-loop.h.  */
     void (*wait)(struct ofproto *ofproto);
 
+    /* Adds some memory usage statistics for the implementation of 'ofproto'
+     * into 'usage', for use with memory_report().
+     *
+     * This function is optional. */
+    void (*get_memory_usage)(const struct ofproto *ofproto,
+                             struct simap *usage);
+
     /* Every "struct rule" in 'ofproto' is about to be deleted, one by one.
      * This function may prepare for that, for example by clearing state in
      * advance.  It should *not* actually delete any "struct rule"s from
@@ -427,7 +456,7 @@ struct ofproto_class {
      *
      *   - 'name' to "table#" where # is the table ID.
      *
-     *   - 'wildcards' to OFPFW_ALL.
+     *   - 'wildcards' to OFPFW10_ALL.
      *
      *   - 'max_entries' to 1,000,000.
      *
@@ -452,10 +481,10 @@ struct ofproto_class {
      *   - 'matched_count' to the number of packets looked up in this flow
      *     table so far that matched one of the flow entries.
      *
-     * Keep in mind that all of the members of struct ofp_table_stats are in
+     * Keep in mind that all of the members of struct ofp10_table_stats are in
      * network byte order.
      */
-    void (*get_tables)(struct ofproto *ofproto, struct ofp_table_stats *ots);
+    void (*get_tables)(struct ofproto *ofproto, struct ofp10_table_stats *ots);
 
 /* ## ---------------- ## */
 /* ## ofport Functions ## */
@@ -767,13 +796,11 @@ struct ofproto_class {
      *     registers, then it is an error if 'rule->cr' does not wildcard all
      *     registers.
      *
-     *   - Validate that 'rule->actions' and 'rule->n_actions' are well-formed
-     *     OpenFlow actions that the datapath can correctly implement.  The
-     *     validate_actions() function (in ofp-util.c) can be useful as a model
-     *     for action validation, but it accepts all of the OpenFlow actions
-     *     that OVS understands.  If your ofproto implementation only
-     *     implements a subset of those, then you should implement your own
-     *     action validation.
+     *   - Validate that 'rule->ofpacts' is a sequence of well-formed actions
+     *     that the datapath can correctly implement.  If your ofproto
+     *     implementation only implements a subset of the actions that Open
+     *     vSwitch understands, then you should implement your own action
+     *     validation.
      *
      *   - If the rule is valid, update the datapath flow table, adding the new
      *     rule or replacing the existing one.
@@ -904,19 +931,37 @@ struct ofproto_class {
                               enum ofp_config_flags frag_handling);
 
     /* Implements the OpenFlow OFPT_PACKET_OUT command.  The datapath should
-     * execute the 'n_actions' in the 'actions' array on 'packet'.
+     * execute the 'ofpacts_len' bytes of "struct ofpacts" in 'ofpacts'.
      *
-     * The caller retains ownership of 'packet', so ->packet_out() should not
-     * modify or free it.
+     * The caller retains ownership of 'packet' and of 'ofpacts', so
+     * ->packet_out() should not modify or free them.
      *
-     * This function must validate that the 'n_actions' elements in 'actions'
-     * are well-formed OpenFlow actions that can be correctly implemented by
-     * the datapath.  If not, then it should return an OpenFlow error code.
+     * This function must validate that it can implement 'ofpacts'.  If not,
+     * then it should return an OpenFlow error code.
      *
      * 'flow' reflects the flow information for 'packet'.  All of the
      * information in 'flow' is extracted from 'packet', except for
-     * flow->in_port, which is taken from the OFPT_PACKET_OUT message.
-     * flow->tun_id and its register values are zeroed.
+     * flow->in_port (see below).  flow->tun_id and its register values are
+     * zeroed.
+     *
+     * flow->in_port comes from the OpenFlow OFPT_PACKET_OUT message.  The
+     * implementation should reject invalid flow->in_port values by returning
+     * OFPERR_NXBRC_BAD_IN_PORT.  For consistency, the implementation should
+     * consider valid for flow->in_port any value that could possibly be seen
+     * in a packet that it passes to connmgr_send_packet_in().  Ideally, even
+     * an implementation that never generates packet-ins (e.g. due to hardware
+     * limitations) should still allow flow->in_port values for every possible
+     * physical port and OFPP_LOCAL.  The only virtual ports (those above
+     * OFPP_MAX) that the caller will ever pass in as flow->in_port, other than
+     * OFPP_LOCAL, are OFPP_NONE and OFPP_CONTROLLER.  The implementation
+     * should allow both of these, treating each of them as packets generated
+     * by the controller as opposed to packets originating from some switch
+     * port.
+     *
+     * (Ordinarily the only effect of flow->in_port is on output actions that
+     * involve the input port, such as actions that output to OFPP_IN_PORT,
+     * OFPP_FLOOD, or OFPP_ALL.  flow->in_port can also affect Nicira extension
+     * "resubmit" actions.)
      *
      * 'packet' is not matched against the OpenFlow flow table, so its
      * statistics should not be included in OpenFlow flow statistics.
@@ -924,8 +969,8 @@ struct ofproto_class {
      * Returns 0 if successful, otherwise an OpenFlow error code. */
     enum ofperr (*packet_out)(struct ofproto *ofproto, struct ofpbuf *packet,
                               const struct flow *flow,
-                              const union ofp_action *actions,
-                              size_t n_actions);
+                              const struct ofpact *ofpacts,
+                              size_t ofpacts_len);
 
 /* ## ------------------------- ## */
 /* ## OFPP_NORMAL configuration ## */
@@ -970,6 +1015,15 @@ struct ofproto_class {
      * not support CFM. */
     int (*get_cfm_fault)(const struct ofport *ofport);
 
+    /* Check the operational status reported by the remote CFM endpoint of
+     * 'ofp_port'  Returns 1 if operationally up, 0 if operationally down, and
+     * -1 if CFM is not enabled on 'ofp_port' or does not support operational
+     * status.
+     *
+     * This function may be a null pointer if the ofproto implementation does
+     * not support CFM. */
+    int (*get_cfm_opup)(const struct ofport *ofport);
+
     /* Gets the MPIDs of the remote maintenance points broadcasting to
      * 'ofport'.  Populates 'rmps' with a provider owned array of MPIDs, and
      * 'n_rmps' with the number of MPIDs in 'rmps'. Returns a number less than
@@ -1162,7 +1216,7 @@ BUILD_ASSERT_DECL(OFPROTO_POSTPONE < OFPERR_OFS);
 
 int ofproto_flow_mod(struct ofproto *, const struct ofputil_flow_mod *);
 void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
-                      const union ofp_action *, size_t n_actions);
+                      const struct ofpact *ofpacts, size_t ofpacts_len);
 bool ofproto_delete_flow(struct ofproto *, const struct cls_rule *);
 void ofproto_flush_flows(struct ofproto *);
 
index ad9e02d..3e36fe6 100644 (file)
@@ -7,7 +7,7 @@ Lists the names of the running ofproto instances.  These are the names
 that may be used on \fBofproto/trace\fR.
 .
 .IP "\fBofproto/trace \fIswitch priority tun_id in_port packet\fR"
-.IQ "\fBofproto/trace \fIswitch odp_flow \fB\-generate\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
@@ -31,32 +31,36 @@ utilities provide easier ways.
 .IP
 The second form specifies the packet's contents implicitly:
 .RS
-.IP "\fIodp_flow\fR"
-A flow in the form printed by \fBovs\-dpctl\fR(8)'s \fBdump\-flows\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.  \fB\*(PN\fR generates an arbitrary packet
-that has the specified \fIodp_flow\fR.
+that has the specified \fIflow\fR.
 .RE
 .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.
 .
-.IP "\fBofproto/trace \fIswitch odp_flow\fR"
+.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 "\fIodp_flow\fR"
-A flow in the form printed by \fBovs\-dpctl\fR(8)'s \fBdump\-flows\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.
 .RE
 .IP
 \fB\*(PN\fR will respond with extensive information on how a packet
-in \fIodp_flow\fR would be handled if it were received by
+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.
 .IP
index cb46d26..5c9ab9d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  * Copyright (c) 2010 Jean Tourrilhes - HP-Labs.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,7 +32,9 @@
 #include "meta-flow.h"
 #include "netdev.h"
 #include "nx-match.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
+#include "ofp-msgs.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
@@ -45,6 +47,7 @@
 #include "poll-loop.h"
 #include "random.h"
 #include "shash.h"
+#include "simap.h"
 #include "sset.h"
 #include "timeval.h"
 #include "unaligned.h"
@@ -85,6 +88,7 @@ struct ofopgroup {
     struct ofproto *ofproto;    /* Owning ofproto. */
     struct list ofproto_node;   /* In ofproto's "pending" list. */
     struct list ops;            /* List of "struct ofoperation"s. */
+    int n_running;              /* Number of ops still pending. */
 
     /* Data needed to send OpenFlow reply on failure or to send a buffered
      * packet on success.
@@ -99,7 +103,6 @@ struct ofopgroup {
     struct ofconn *ofconn;      /* ofconn for reply (but see note above). */
     struct ofp_header *request; /* Original request (truncated at 64 bytes). */
     uint32_t buffer_id;         /* Buffer id from original request. */
-    int error;                  /* 0 if no error yet, otherwise error code. */
 };
 
 static struct ofopgroup *ofopgroup_create_unattached(struct ofproto *);
@@ -107,7 +110,7 @@ static struct ofopgroup *ofopgroup_create(struct ofproto *, struct ofconn *,
                                           const struct ofp_header *,
                                           uint32_t buffer_id);
 static void ofopgroup_submit(struct ofopgroup *);
-static void ofopgroup_destroy(struct ofopgroup *);
+static void ofopgroup_complete(struct ofopgroup *);
 
 /* A single flow table operation. */
 struct ofoperation {
@@ -116,15 +119,25 @@ struct ofoperation {
     struct hmap_node hmap_node; /* In ofproto's "deletions" hmap. */
     struct rule *rule;          /* Rule being operated upon. */
     enum ofoperation_type type; /* Type of operation. */
-    int status;                 /* -1 if pending, otherwise 0 or error code. */
-    struct rule *victim;        /* OFOPERATION_ADDING: Replaced rule. */
-    union ofp_action *actions;  /* OFOPERATION_MODIFYING: Replaced actions. */
-    int n_actions;              /* OFOPERATION_MODIFYING: # of old actions. */
+
+    /* OFOPERATION_ADD. */
+    struct rule *victim;        /* Rule being replaced, if any.. */
+
+    /* OFOPERATION_MODIFY: The old actions, if the actions are changing. */
+    struct ofpact *ofpacts;
+    size_t ofpacts_len;
+
+    /* OFOPERATION_DELETE. */
+    enum ofp_flow_removed_reason reason; /* Reason flow was removed. */
+
     ovs_be64 flow_cookie;       /* Rule's old flow cookie. */
+    enum ofperr error;          /* 0 if no error. */
 };
 
-static void ofoperation_create(struct ofopgroup *, struct rule *,
-                               enum ofoperation_type);
+static struct ofoperation *ofoperation_create(struct ofopgroup *,
+                                              struct rule *,
+                                              enum ofoperation_type,
+                                              enum ofp_flow_removed_reason);
 static void ofoperation_destroy(struct ofoperation *);
 
 /* oftable. */
@@ -180,7 +193,6 @@ static void reinit_ports(struct ofproto *);
 static void ofproto_rule_destroy__(struct rule *);
 static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
 static bool rule_is_modifiable(const struct rule *);
-static bool rule_is_hidden(const struct rule *);
 
 /* OpenFlow. */
 static enum ofperr add_flow(struct ofproto *, struct ofconn *,
@@ -385,6 +397,10 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     list_init(&ofproto->pending);
     ofproto->n_pending = 0;
     hmap_init(&ofproto->deletions);
+    ofproto->n_add = ofproto->n_delete = ofproto->n_modify = 0;
+    ofproto->first_op = ofproto->last_op = LLONG_MIN;
+    ofproto->next_op_report = LLONG_MAX;
+    ofproto->op_backoff = LLONG_MIN;
     ofproto->vlan_bitmap = NULL;
     ofproto->vlans_changed = false;
     ofproto->min_mtu = INT_MAX;
@@ -400,7 +416,6 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     assert(ofproto->n_tables);
 
     ofproto->datapath_id = pick_datapath_id(ofproto);
-    VLOG_INFO("using datapath ID %016"PRIx64, ofproto->datapath_id);
     init_ports(ofproto);
 
     *ofprotop = ofproto;
@@ -422,14 +437,18 @@ ofproto_init_tables(struct ofproto *ofproto, int n_tables)
     }
 }
 
+uint64_t
+ofproto_get_datapath_id(const struct ofproto *ofproto)
+{
+    return ofproto->datapath_id;
+}
+
 void
 ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
 {
     uint64_t old_dpid = p->datapath_id;
     p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
     if (p->datapath_id != old_dpid) {
-        VLOG_INFO("datapath ID changed to %016"PRIx64, p->datapath_id);
-
         /* Force all active connections to reconnect, since there is no way to
          * notify a controller that the datapath ID has changed. */
         ofproto_reconnect_controllers(p);
@@ -524,41 +543,40 @@ ofproto_set_desc(struct ofproto *p,
 
     if (mfr_desc) {
         if (strlen(mfr_desc) >= sizeof ods->mfr_desc) {
-            VLOG_WARN("truncating mfr_desc, must be less than %zu characters",
-                    sizeof ods->mfr_desc);
+            VLOG_WARN("%s: truncating mfr_desc, must be less than %zu bytes",
+                      p->name, sizeof ods->mfr_desc);
         }
         free(p->mfr_desc);
         p->mfr_desc = xstrdup(mfr_desc);
     }
     if (hw_desc) {
         if (strlen(hw_desc) >= sizeof ods->hw_desc) {
-            VLOG_WARN("truncating hw_desc, must be less than %zu characters",
-                    sizeof ods->hw_desc);
+            VLOG_WARN("%s: truncating hw_desc, must be less than %zu bytes",
+                      p->name, sizeof ods->hw_desc);
         }
         free(p->hw_desc);
         p->hw_desc = xstrdup(hw_desc);
     }
     if (sw_desc) {
         if (strlen(sw_desc) >= sizeof ods->sw_desc) {
-            VLOG_WARN("truncating sw_desc, must be less than %zu characters",
-                    sizeof ods->sw_desc);
+            VLOG_WARN("%s: truncating sw_desc, must be less than %zu bytes",
+                      p->name, sizeof ods->sw_desc);
         }
         free(p->sw_desc);
         p->sw_desc = xstrdup(sw_desc);
     }
     if (serial_desc) {
         if (strlen(serial_desc) >= sizeof ods->serial_num) {
-            VLOG_WARN("truncating serial_desc, must be less than %zu "
-                    "characters",
-                    sizeof ods->serial_num);
+            VLOG_WARN("%s: truncating serial_desc, must be less than %zu "
+                      "bytes", p->name, sizeof ods->serial_num);
         }
         free(p->serial_desc);
         p->serial_desc = xstrdup(serial_desc);
     }
     if (dp_desc) {
         if (strlen(dp_desc) >= sizeof ods->dp_desc) {
-            VLOG_WARN("truncating dp_desc, must be less than %zu characters",
-                    sizeof ods->dp_desc);
+            VLOG_WARN("%s: truncating dp_desc, must be less than %zu bytes",
+                      p->name, sizeof ods->dp_desc);
         }
         free(p->dp_desc);
         p->dp_desc = xstrdup(dp_desc);
@@ -664,8 +682,8 @@ ofproto_port_get_stp_status(struct ofproto *ofproto, uint16_t ofp_port,
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
     if (!ofport) {
-        VLOG_WARN("%s: cannot get STP status on nonexistent port %"PRIu16,
-                  ofproto->name, ofp_port);
+        VLOG_WARN_RL(&rl, "%s: cannot get STP status on nonexistent "
+                     "port %"PRIu16, ofproto->name, ofp_port);
         return ENODEV;
     }
 
@@ -938,7 +956,8 @@ ofproto_flush__(struct ofproto *ofproto)
         cls_cursor_init(&cursor, &table->cls, NULL);
         CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
             if (!rule->pending) {
-                ofoperation_create(group, rule, OFOPERATION_DELETE);
+                ofoperation_create(group, rule, OFOPERATION_DELETE,
+                                   OFPRR_DELETE);
                 oftable_remove_rule(rule);
                 ofproto->ofproto_class->rule_destruct(rule);
             }
@@ -1090,6 +1109,43 @@ ofproto_run(struct ofproto *p)
         NOT_REACHED();
     }
 
+    if (time_msec() >= p->next_op_report) {
+        long long int ago = (time_msec() - p->first_op) / 1000;
+        long long int interval = (p->last_op - p->first_op) / 1000;
+        struct ds s;
+
+        ds_init(&s);
+        ds_put_format(&s, "%d flow_mods ",
+                      p->n_add + p->n_delete + p->n_modify);
+        if (interval == ago) {
+            ds_put_format(&s, "in the last %lld s", ago);
+        } else if (interval) {
+            ds_put_format(&s, "in the %lld s starting %lld s ago",
+                          interval, ago);
+        } else {
+            ds_put_format(&s, "%lld s ago", ago);
+        }
+
+        ds_put_cstr(&s, " (");
+        if (p->n_add) {
+            ds_put_format(&s, "%d adds, ", p->n_add);
+        }
+        if (p->n_delete) {
+            ds_put_format(&s, "%d deletes, ", p->n_delete);
+        }
+        if (p->n_modify) {
+            ds_put_format(&s, "%d modifications, ", p->n_modify);
+        }
+        s.length -= 2;
+        ds_put_char(&s, ')');
+
+        VLOG_INFO("%s: %s", p->name, ds_cstr(&s));
+        ds_destroy(&s);
+
+        p->n_add = p->n_delete = p->n_modify = 0;
+        p->next_op_report = LLONG_MAX;
+    }
+
     return error;
 }
 
@@ -1149,6 +1205,31 @@ ofproto_is_alive(const struct ofproto *p)
     return connmgr_has_controllers(p->connmgr);
 }
 
+/* Adds some memory usage statistics for 'ofproto' into 'usage', for use with
+ * memory_report(). */
+void
+ofproto_get_memory_usage(const struct ofproto *ofproto, struct simap *usage)
+{
+    const struct oftable *table;
+    unsigned int n_rules;
+
+    simap_increase(usage, "ports", hmap_count(&ofproto->ports));
+    simap_increase(usage, "ops",
+                   ofproto->n_pending + hmap_count(&ofproto->deletions));
+
+    n_rules = 0;
+    OFPROTO_FOR_EACH_TABLE (table, ofproto) {
+        n_rules += classifier_count(&table->cls);
+    }
+    simap_increase(usage, "rules", n_rules);
+
+    if (ofproto->ofproto_class->get_memory_usage) {
+        ofproto->ofproto_class->get_memory_usage(ofproto, usage);
+    }
+
+    connmgr_get_memory_usage(ofproto->connmgr, usage);
+}
+
 void
 ofproto_get_ofproto_controller_info(const struct ofproto *ofproto,
                                     struct shash *info)
@@ -1312,27 +1393,28 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
  * (0...65535, inclusive) then the flow will be visible to OpenFlow
  * controllers; otherwise, it will be hidden.
  *
- * The caller retains ownership of 'cls_rule' and 'actions'.
+ * The caller retains ownership of 'cls_rule' and 'ofpacts'.
  *
  * This is a helper function for in-band control and fail-open. */
 void
 ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule,
-                 const union ofp_action *actions, size_t n_actions)
+                 const struct ofpact *ofpacts, size_t ofpacts_len)
 {
     const struct rule *rule;
 
     rule = rule_from_cls_rule(classifier_find_rule_exactly(
                                     &ofproto->tables[0].cls, cls_rule));
-    if (!rule || !ofputil_actions_equal(rule->actions, rule->n_actions,
-                                        actions, n_actions)) {
+    if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
+                                ofpacts, ofpacts_len)) {
         struct ofputil_flow_mod fm;
 
         memset(&fm, 0, sizeof fm);
         fm.cr = *cls_rule;
         fm.buffer_id = UINT32_MAX;
-        fm.actions = (union ofp_action *) actions;
-        fm.n_actions = n_actions;
+        fm.ofpacts = xmemdup(ofpacts, ofpacts_len);
+        fm.ofpacts_len = ofpacts_len;
         add_flow(ofproto, NULL, &fm, NULL);
+        free(fm.ofpacts);
     }
 }
 
@@ -1368,7 +1450,7 @@ ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target)
     } else {
         /* Initiate deletion -> success. */
         struct ofopgroup *group = ofopgroup_create_unattached(ofproto);
-        ofoperation_create(group, rule, OFOPERATION_DELETE);
+        ofoperation_create(group, rule, OFOPERATION_DELETE, OFPRR_DELETE);
         oftable_remove_rule(rule);
         ofproto->ofproto_class->rule_destruct(rule);
         ofopgroup_submit(group);
@@ -1412,10 +1494,12 @@ reinit_ports(struct ofproto *p)
     sset_destroy(&devnames);
 }
 
-/* Opens and returns a netdev for 'ofproto_port', or a null pointer if the
- * netdev cannot be opened.  On success, also fills in 'opp'.  */
+/* Opens and returns a netdev for 'ofproto_port' in 'ofproto', or a null
+ * pointer if the netdev cannot be opened.  On success, also fills in
+ * 'opp'.  */
 static struct netdev *
-ofport_open(const struct ofproto_port *ofproto_port,
+ofport_open(const struct ofproto *ofproto,
+            const struct ofproto_port *ofproto_port,
             struct ofputil_phy_port *pp)
 {
     enum netdev_flags flags;
@@ -1424,8 +1508,9 @@ ofport_open(const struct ofproto_port *ofproto_port,
 
     error = netdev_open(ofproto_port->name, ofproto_port->type, &netdev);
     if (error) {
-        VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s "
+        VLOG_WARN_RL(&rl, "%s: ignoring port %s (%"PRIu16") because netdev %s "
                      "cannot be opened (%s)",
+                     ofproto->name,
                      ofproto_port->name, ofproto_port->ofp_port,
                      ofproto_port->name, strerror(error));
         return NULL;
@@ -1646,7 +1731,7 @@ update_port(struct ofproto *ofproto, const char *name)
 
     /* Fetch 'name''s location and properties from the datapath. */
     netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port)
-              ? ofport_open(&ofproto_port, &pp)
+              ? ofport_open(ofproto, &ofproto_port, &pp)
               : NULL);
     if (netdev) {
         port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
@@ -1697,16 +1782,16 @@ init_ports(struct ofproto *p)
     OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
         uint16_t ofp_port = ofproto_port.ofp_port;
         if (ofproto_get_port(p, ofp_port)) {
-            VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
-                         ofp_port);
+            VLOG_WARN_RL(&rl, "%s: ignoring duplicate port %"PRIu16" "
+                         "in datapath", p->name, ofp_port);
         } else if (shash_find(&p->port_by_name, ofproto_port.name)) {
-            VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
-                         ofproto_port.name);
+            VLOG_WARN_RL(&rl, "%s: ignoring duplicate device %s in datapath",
+                         p->name, ofproto_port.name);
         } else {
             struct ofputil_phy_port pp;
             struct netdev *netdev;
 
-            netdev = ofport_open(&ofproto_port, &pp);
+            netdev = ofport_open(p, &ofproto_port, &pp);
             if (netdev) {
                 ofport_install(p, netdev, &pp);
             }
@@ -1791,7 +1876,7 @@ static void
 ofproto_rule_destroy__(struct rule *rule)
 {
     if (rule) {
-        free(rule->actions);
+        free(rule->ofpacts);
         rule->ofproto->ofproto_class->rule_dealloc(rule);
     }
 }
@@ -1813,23 +1898,35 @@ ofproto_rule_destroy(struct rule *rule)
 }
 
 /* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
- * that outputs to 'out_port' (output to OFPP_FLOOD and OFPP_ALL doesn't
- * count). */
-static bool
-rule_has_out_port(const struct rule *rule, uint16_t out_port)
+ * 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)
 {
-    const union ofp_action *oa;
-    size_t left;
+    return (port == OFPP_NONE
+            || ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port));
+}
 
-    if (out_port == OFPP_NONE) {
+/* 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)
+{
+    if (ofproto_rule_has_out_port(op->rule, out_port)) {
         return true;
     }
-    OFPUTIL_ACTION_FOR_EACH_UNSAFE (oa, left, rule->actions, rule->n_actions) {
-        if (action_outputs_to_port(oa, htons(out_port))) {
-            return true;
-        }
+
+    switch (op->type) {
+    case OFOPERATION_ADD:
+        return op->victim && ofproto_rule_has_out_port(op->victim, out_port);
+
+    case OFOPERATION_DELETE:
+        return false;
+
+    case OFOPERATION_MODIFY:
+        return ofpacts_output_to_port(op->ofpacts, op->ofpacts_len, out_port);
     }
-    return false;
+
+    NOT_REACHED();
 }
 
 /* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
@@ -1856,8 +1953,8 @@ rule_execute(struct rule *rule, uint16_t in_port, struct ofpbuf *packet)
  * Rules with priority higher than UINT16_MAX are set up by ofproto itself
  * (e.g. by in-band control) and are intentionally hidden from the
  * controller. */
-static bool
-rule_is_hidden(const struct rule *rule)
+bool
+ofproto_rule_is_hidden(const struct rule *rule)
 {
     return rule->cr.priority > UINT16_MAX;
 }
@@ -1922,7 +2019,8 @@ handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
     struct ofpbuf *buf;
 
     /* Send reply. */
-    osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
+    buf = ofpraw_alloc_reply(OFPRAW_OFPT_GET_CONFIG_REPLY, oh, 0);
+    osc = ofpbuf_put_uninit(buf, sizeof *osc);
     flags = ofproto->frag_handling;
     if (ofconn_get_invalid_ttl_to_controller(ofconn)) {
         flags |= OFPC_INVALID_TTL_TO_CONTROLLER;
@@ -1935,8 +2033,9 @@ handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
 }
 
 static enum ofperr
-handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
+handle_set_config(struct ofconn *ofconn, const struct ofp_header *oh)
 {
+    const struct ofp_switch_config *osc = ofpmsg_body(oh);
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     uint16_t flags = ntohs(osc->flags);
 
@@ -1981,11 +2080,13 @@ reject_slave_controller(struct ofconn *ofconn)
 }
 
 static enum ofperr
-handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
+handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *p = ofconn_get_ofproto(ofconn);
     struct ofputil_packet_out po;
     struct ofpbuf *payload;
+    uint64_t ofpacts_stub[1024 / 8];
+    struct ofpbuf ofpacts;
     struct flow flow;
     enum ofperr error;
 
@@ -1993,20 +2094,21 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
 
     error = reject_slave_controller(ofconn);
     if (error) {
-        return error;
+        goto exit;
     }
 
     /* Decode message. */
-    error = ofputil_decode_packet_out(&po, opo);
+    ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+    error = ofputil_decode_packet_out(&po, oh, &ofpacts);
     if (error) {
-        return error;
+        goto exit_free_ofpacts;
     }
 
     /* Get payload. */
     if (po.buffer_id != UINT32_MAX) {
         error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL);
         if (error || !payload) {
-            return error;
+            goto exit_free_ofpacts;
         }
     } else {
         payload = xmalloc(sizeof *payload);
@@ -2016,9 +2118,12 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
     /* Send out packet. */
     flow_extract(payload, 0, 0, po.in_port, &flow);
     error = p->ofproto_class->packet_out(p, payload, &flow,
-                                         po.actions, po.n_actions);
+                                         po.ofpacts, po.ofpacts_len);
     ofpbuf_delete(payload);
 
+exit_free_ofpacts:
+    ofpbuf_uninit(&ofpacts);
+exit:
     return error;
 }
 
@@ -2080,13 +2185,14 @@ handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 
 static enum ofperr
 handle_desc_stats_request(struct ofconn *ofconn,
-                          const struct ofp_stats_msg *request)
+                          const struct ofp_header *request)
 {
     struct ofproto *p = ofconn_get_ofproto(ofconn);
     struct ofp_desc_stats *ods;
     struct ofpbuf *msg;
 
-    ods = ofputil_make_stats_reply(sizeof *ods, request, &msg);
+    msg = ofpraw_alloc_stats_reply(request, 0);
+    ods = ofpbuf_put_zeros(msg, sizeof *ods);
     ovs_strlcpy(ods->mfr_desc, p->mfr_desc, sizeof ods->mfr_desc);
     ovs_strlcpy(ods->hw_desc, p->hw_desc, sizeof ods->hw_desc);
     ovs_strlcpy(ods->sw_desc, p->sw_desc, sizeof ods->sw_desc);
@@ -2099,20 +2205,19 @@ handle_desc_stats_request(struct ofconn *ofconn,
 
 static enum ofperr
 handle_table_stats_request(struct ofconn *ofconn,
-                           const struct ofp_stats_msg *request)
+                           const struct ofp_header *request)
 {
     struct ofproto *p = ofconn_get_ofproto(ofconn);
-    struct ofp_table_stats *ots;
+    struct ofp10_table_stats *ots;
     struct ofpbuf *msg;
     size_t i;
 
-    ofputil_make_stats_reply(sizeof(struct ofp_stats_msg), request, &msg);
-
+    msg = ofpraw_alloc_stats_reply(request, sizeof *ots * p->n_tables);
     ots = ofpbuf_put_zeros(msg, sizeof *ots * p->n_tables);
     for (i = 0; i < p->n_tables; i++) {
         ots[i].table_id = i;
         sprintf(ots[i].name, "table%zu", i);
-        ots[i].wildcards = htonl(OFPFW_ALL);
+        ots[i].wildcards = htonl(OFPFW10_ALL);
         ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
         ots[i].active_count = htonl(classifier_count(&p->tables[i].cls));
     }
@@ -2139,14 +2244,14 @@ static void
 append_port_stat(struct ofport *port, struct list *replies)
 {
     struct netdev_stats stats;
-    struct ofp_port_stats *ops;
+    struct ofp10_port_stats *ops;
 
     /* Intentionally ignore return value, since errors will set
      * 'stats' to all-1s, which is correct for OpenFlow, and
      * netdev_get_stats() will log errors. */
     ofproto_port_get_stats(port, &stats);
 
-    ops = ofputil_append_stats_reply(sizeof *ops, replies);
+    ops = ofpmp_append(replies, sizeof *ops);
     ops->port_no = htons(port->pp.port_no);
     memset(ops->pad, 0, sizeof ops->pad);
     put_32aligned_be64(&ops->rx_packets, htonll(stats.rx_packets));
@@ -2165,13 +2270,14 @@ append_port_stat(struct ofport *port, struct list *replies)
 
 static enum ofperr
 handle_port_stats_request(struct ofconn *ofconn,
-                          const struct ofp_port_stats_request *psr)
+                          const struct ofp_header *request)
 {
     struct ofproto *p = ofconn_get_ofproto(ofconn);
+    const struct ofp10_port_stats_request *psr = ofpmsg_body(request);
     struct ofport *port;
     struct list replies;
 
-    ofputil_start_stats_reply(&psr->osm, &replies);
+    ofpmp_init(&replies, request);
     if (psr->port_no != htons(OFPP_NONE)) {
         port = ofproto_get_port(p, ntohs(psr->port_no));
         if (port) {
@@ -2187,6 +2293,26 @@ handle_port_stats_request(struct ofconn *ofconn,
     return 0;
 }
 
+static enum ofperr
+handle_port_desc_stats_request(struct ofconn *ofconn,
+                               const struct ofp_header *request)
+{
+    struct ofproto *p = ofconn_get_ofproto(ofconn);
+    enum ofp_version version;
+    struct ofport *port;
+    struct list replies;
+
+    ofpmp_init(&replies, request);
+
+    version = ofputil_protocol_to_ofp_version(ofconn_get_protocol(ofconn));
+    HMAP_FOR_EACH (port, hmap_node, &p->ports) {
+        ofputil_append_port_desc_stats_reply(version, &port->pp, &replies);
+    }
+
+    ofconn_send_replies(ofconn, &replies);
+    return 0;
+}
+
 static void
 calc_flow_duration__(long long int start, long long int now,
                      uint32_t *sec, uint32_t *nsec)
@@ -2208,7 +2334,7 @@ check_table_id(const struct ofproto *ofproto, uint8_t table_id)
 }
 
 static struct oftable *
-next_visible_table(struct ofproto *ofproto, uint8_t table_id)
+next_visible_table(const struct ofproto *ofproto, uint8_t table_id)
 {
     struct oftable *table;
 
@@ -2224,7 +2350,7 @@ next_visible_table(struct ofproto *ofproto, uint8_t table_id)
 }
 
 static struct oftable *
-first_matching_table(struct ofproto *ofproto, uint8_t table_id)
+first_matching_table(const struct ofproto *ofproto, uint8_t table_id)
 {
     if (table_id == 0xff) {
         return next_visible_table(ofproto, 0);
@@ -2236,8 +2362,8 @@ first_matching_table(struct ofproto *ofproto, uint8_t table_id)
 }
 
 static struct oftable *
-next_matching_table(struct ofproto *ofproto,
-                    struct oftable *table, uint8_t table_id)
+next_matching_table(const struct ofproto *ofproto,
+                    const struct oftable *table, uint8_t table_id)
 {
     return (table_id == 0xff
             ? next_visible_table(ofproto, (table - ofproto->tables) + 1)
@@ -2299,7 +2425,8 @@ collect_rules_loose(struct ofproto *ofproto, uint8_t table_id,
             if (rule->pending) {
                 return OFPROTO_POSTPONE;
             }
-            if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)
+            if (!ofproto_rule_is_hidden(rule)
+                && ofproto_rule_has_out_port(rule, out_port)
                     && !((rule->flow_cookie ^ cookie) & cookie_mask)) {
                 list_push_back(rules, &rule->ofproto_node);
             }
@@ -2343,7 +2470,8 @@ collect_rules_strict(struct ofproto *ofproto, uint8_t table_id,
             if (rule->pending) {
                 return OFPROTO_POSTPONE;
             }
-            if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)
+            if (!ofproto_rule_is_hidden(rule)
+                && ofproto_rule_has_out_port(rule, out_port)
                     && !((rule->flow_cookie ^ cookie) & cookie_mask)) {
                 list_push_back(rules, &rule->ofproto_node);
             }
@@ -2364,7 +2492,7 @@ age_secs(long long int age_ms)
 
 static enum ofperr
 handle_flow_stats_request(struct ofconn *ofconn,
-                          const struct ofp_stats_msg *osm)
+                          const struct ofp_header *request)
 {
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     struct ofputil_flow_stats_request fsr;
@@ -2373,7 +2501,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
     struct rule *rule;
     enum ofperr error;
 
-    error = ofputil_decode_flow_stats_request(&fsr, &osm->header);
+    error = ofputil_decode_flow_stats_request(&fsr, request);
     if (error) {
         return error;
     }
@@ -2385,7 +2513,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
         return error;
     }
 
-    ofputil_start_stats_reply(osm, &replies);
+    ofpmp_init(&replies, request);
     LIST_FOR_EACH (rule, ofproto_node, &rules) {
         long long int now = time_msec();
         struct ofputil_flow_stats fs;
@@ -2401,8 +2529,8 @@ handle_flow_stats_request(struct ofconn *ofconn,
         fs.hard_age = age_secs(now - rule->modified);
         ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count,
                                                &fs.byte_count);
-        fs.actions = rule->actions;
-        fs.n_actions = rule->n_actions;
+        fs.ofpacts = rule->ofpacts;
+        fs.ofpacts_len = rule->ofpacts_len;
         ofputil_append_flow_stats_reply(&fs, &replies);
     }
     ofconn_send_replies(ofconn, &replies);
@@ -2428,8 +2556,8 @@ flow_stats_ds(struct rule *rule, struct ds *results)
     ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
     cls_rule_format(&rule->cr, results);
     ds_put_char(results, ',');
-    if (rule->n_actions > 0) {
-        ofp_print_actions(results, rule->actions, rule->n_actions);
+    if (rule->ofpacts_len > 0) {
+        ofpacts_format(rule->ofpacts, rule->ofpacts_len, results);
     } else {
         ds_put_cstr(results, "drop");
     }
@@ -2466,7 +2594,7 @@ ofproto_get_netflow_ids(const struct ofproto *ofproto,
 /* Checks the fault status of CFM for 'ofp_port' within 'ofproto'.  Returns a
  * bitmask of 'cfm_fault_reason's to indicate a CFM fault (generally
  * indicating a connectivity problem).  Returns zero if CFM is not faulted,
- * and -1 if CFM is not enabled on 'port'. */
+ * and -1 if CFM is not enabled on 'ofp_port'. */
 int
 ofproto_port_get_cfm_fault(const struct ofproto *ofproto, uint16_t ofp_port)
 {
@@ -2476,6 +2604,19 @@ ofproto_port_get_cfm_fault(const struct ofproto *ofproto, uint16_t ofp_port)
             : -1);
 }
 
+/* Checks the operational status reported by the remote CFM endpoint of
+ * 'ofp_port'  Returns 1 if operationally up, 0 if operationally down, and -1
+ * if CFM is not enabled on 'ofp_port' or does not support operational status.
+ */
+int
+ofproto_port_get_cfm_opup(const struct ofproto *ofproto, uint16_t ofp_port)
+{
+    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+    return (ofport && ofproto->ofproto_class->get_cfm_opup
+            ? ofproto->ofproto_class->get_cfm_opup(ofport)
+            : -1);
+}
+
 /* Gets the MPIDs of the remote maintenance points broadcasting to 'ofp_port'
  * within 'ofproto'.  Populates 'rmps' with an array of MPIDs owned by
  * 'ofproto', and 'n_rmps' with the number of MPIDs in 'rmps'.  Returns a
@@ -2510,7 +2651,7 @@ ofproto_port_get_cfm_health(const struct ofproto *ofproto, uint16_t ofp_port)
 
 static enum ofperr
 handle_aggregate_stats_request(struct ofconn *ofconn,
-                               const struct ofp_stats_msg *osm)
+                               const struct ofp_header *oh)
 {
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     struct ofputil_flow_stats_request request;
@@ -2521,7 +2662,7 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
     struct rule *rule;
     enum ofperr error;
 
-    error = ofputil_decode_flow_stats_request(&request, &osm->header);
+    error = ofputil_decode_flow_stats_request(&request, oh);
     if (error) {
         return error;
     }
@@ -2563,7 +2704,7 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
         stats.byte_count = UINT64_MAX;
     }
 
-    reply = ofputil_encode_aggregate_stats_reply(&stats, osm);
+    reply = ofputil_encode_aggregate_stats_reply(&stats, oh);
     ofconn_send_reply(ofconn, reply);
 
     return 0;
@@ -2578,9 +2719,9 @@ static void
 put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id,
                 const struct netdev_queue_stats *stats)
 {
-    struct ofp_queue_stats *reply;
+    struct ofp10_queue_stats *reply;
 
-    reply = ofputil_append_stats_reply(sizeof *reply, &cbdata->replies);
+    reply = ofpmp_append(&cbdata->replies, sizeof *reply);
     reply->port_no = htons(cbdata->ofport->pp.port_no);
     memset(reply->pad, 0, sizeof reply->pad);
     reply->queue_id = htonl(queue_id);
@@ -2599,7 +2740,7 @@ handle_queue_stats_dump_cb(uint32_t queue_id,
     put_queue_stats(cbdata, queue_id, stats);
 }
 
-static void
+static enum ofperr
 handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id,
                             struct queue_stats_cbdata *cbdata)
 {
@@ -2612,42 +2753,51 @@ handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id,
 
         if (!netdev_get_queue_stats(port->netdev, queue_id, &stats)) {
             put_queue_stats(cbdata, queue_id, &stats);
+        } else {
+            return OFPERR_OFPQOFC_BAD_QUEUE;
         }
     }
+    return 0;
 }
 
 static enum ofperr
 handle_queue_stats_request(struct ofconn *ofconn,
-                           const struct ofp_queue_stats_request *qsr)
+                           const struct ofp_header *rq)
 {
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    const struct ofp10_queue_stats_request *qsr = ofpmsg_body(rq);
     struct queue_stats_cbdata cbdata;
-    struct ofport *port;
     unsigned int port_no;
+    struct ofport *port;
     uint32_t queue_id;
+    enum ofperr error;
 
     COVERAGE_INC(ofproto_queue_req);
 
-    ofputil_start_stats_reply(&qsr->osm, &cbdata.replies);
+    ofpmp_init(&cbdata.replies, rq);
 
     port_no = ntohs(qsr->port_no);
     queue_id = ntohl(qsr->queue_id);
     if (port_no == OFPP_ALL) {
+        error = OFPERR_OFPQOFC_BAD_QUEUE;
         HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
-            handle_queue_stats_for_port(port, queue_id, &cbdata);
+            if (!handle_queue_stats_for_port(port, queue_id, &cbdata)) {
+                error = 0;
+            }
         }
-    } else if (port_no < OFPP_MAX) {
+    } else {
         port = ofproto_get_port(ofproto, port_no);
-        if (port) {
-            handle_queue_stats_for_port(port, queue_id, &cbdata);
-        }
+        error = (port
+                 ? handle_queue_stats_for_port(port, queue_id, &cbdata)
+                 : OFPERR_OFPQOFC_BAD_PORT);
+    }
+    if (!error) {
+        ofconn_send_replies(ofconn, &cbdata.replies);
     } else {
         ofpbuf_list_delete(&cbdata.replies);
-        return OFPERR_OFPQOFC_BAD_PORT;
     }
-    ofconn_send_replies(ofconn, &cbdata.replies);
 
-    return 0;
+    return error;
 }
 
 static bool
@@ -2678,6 +2828,9 @@ is_flow_deletion_pending(const struct ofproto *ofproto,
  * error code on failure, or OFPROTO_POSTPONE if the operation cannot be
  * initiated now but may be retried later.
  *
+ * Upon successful return, takes ownership of 'fm->ofpacts'.  On failure,
+ * ownership remains with the caller.
+ *
  * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
  * if any. */
 static enum ofperr
@@ -2740,16 +2893,19 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     rule->ofproto = ofproto;
     rule->cr = fm->cr;
     rule->pending = NULL;
-    rule->flow_cookie = fm->cookie;
+    rule->flow_cookie = fm->new_cookie;
     rule->created = rule->modified = rule->used = time_msec();
     rule->idle_timeout = fm->idle_timeout;
     rule->hard_timeout = fm->hard_timeout;
     rule->table_id = table - ofproto->tables;
     rule->send_flow_removed = (fm->flags & OFPFF_SEND_FLOW_REM) != 0;
-    rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
-    rule->n_actions = fm->n_actions;
+    rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
+    rule->ofpacts_len = fm->ofpacts_len;
     rule->evictable = true;
     rule->eviction_group = NULL;
+    rule->monitor_flags = 0;
+    rule->add_seqno = 0;
+    rule->modify_seqno = 0;
 
     /* Insert new rule. */
     victim = oftable_replace_rule(rule);
@@ -2758,6 +2914,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     } else if (victim && victim->pending) {
         error = OFPROTO_POSTPONE;
     } else {
+        struct ofoperation *op;
         struct rule *evict;
 
         if (classifier_count(&table->cls) > table->max_flows) {
@@ -2780,11 +2937,12 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
         }
 
         group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
-        ofoperation_create(group, rule, OFOPERATION_ADD);
-        rule->pending->victim = victim;
+        op = ofoperation_create(group, rule, OFOPERATION_ADD, 0);
+        op->victim = victim;
 
         error = ofproto->ofproto_class->rule_construct(rule);
         if (error) {
+            op->group->n_running--;
             ofoperation_destroy(rule->pending);
         } else if (evict) {
             delete_flow__(evict, group);
@@ -2822,6 +2980,10 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
     group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
     error = OFPERR_OFPBRC_EPERM;
     LIST_FOR_EACH (rule, ofproto_node, rules) {
+        struct ofoperation *op;
+        bool actions_changed;
+        ovs_be64 new_cookie;
+
         if (rule_is_modifiable(rule)) {
             /* At least one rule is modifiable, don't report EPERM error. */
             error = 0;
@@ -2829,18 +2991,27 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
             continue;
         }
 
-        if (!ofputil_actions_equal(fm->actions, fm->n_actions,
-                                   rule->actions, rule->n_actions)) {
-            ofoperation_create(group, rule, OFOPERATION_MODIFY);
-            rule->pending->actions = rule->actions;
-            rule->pending->n_actions = rule->n_actions;
-            rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
-            rule->n_actions = fm->n_actions;
-            ofproto->ofproto_class->rule_modify_actions(rule);
+        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);
+        if (!actions_changed && new_cookie == rule->flow_cookie) {
+            /* No change at all. */
+            continue;
+        }
+
+        op = ofoperation_create(group, rule, OFOPERATION_MODIFY, 0);
+        rule->flow_cookie = new_cookie;
+        if (actions_changed) {
+            op->ofpacts = rule->ofpacts;
+            op->ofpacts_len = rule->ofpacts_len;
+            rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
+            rule->ofpacts_len = fm->ofpacts_len;
+            rule->ofproto->ofproto_class->rule_modify_actions(rule);
         } else {
-            rule->modified = time_msec();
+            ofoperation_complete(op, 0);
         }
-        rule->flow_cookie = fm->cookie;
     }
     ofopgroup_submit(group);
 
@@ -2863,9 +3034,13 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
     error = collect_rules_loose(ofproto, fm->table_id, &fm->cr,
                                 fm->cookie, fm->cookie_mask,
                                 OFPP_NONE, &rules);
-    return (error ? error
-            : list_is_empty(&rules) ? add_flow(ofproto, ofconn, fm, request)
-            : modify_flows__(ofproto, ofconn, fm, request, &rules));
+    if (error) {
+        return error;
+    } else if (list_is_empty(&rules)) {
+        return fm->cookie_mask ? 0 : add_flow(ofproto, ofconn, fm, request);
+    } else {
+        return modify_flows__(ofproto, ofconn, fm, request, &rules);
+    }
 }
 
 /* Implements OFPFC_MODIFY_STRICT.  Returns 0 on success or an OpenFlow error
@@ -2884,11 +3059,16 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
     error = collect_rules_strict(ofproto, fm->table_id, &fm->cr,
                                  fm->cookie, fm->cookie_mask,
                                  OFPP_NONE, &rules);
-    return (error ? error
-            : list_is_empty(&rules) ? add_flow(ofproto, ofconn, fm, request)
-            : list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn,
-                                                         fm, request, &rules)
-            : 0);
+
+    if (error) {
+        return error;
+    } else if (list_is_empty(&rules)) {
+        return fm->cookie_mask ? 0 : add_flow(ofproto, ofconn, fm, request);
+    } else {
+        return list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn,
+                                                          fm, request, &rules)
+                                         : 0;
+    }
 }
 \f
 /* OFPFC_DELETE implementation. */
@@ -2900,7 +3080,7 @@ delete_flow__(struct rule *rule, struct ofopgroup *group)
 
     ofproto_rule_send_removed(rule, OFPRR_DELETE);
 
-    ofoperation_create(group, rule, OFOPERATION_DELETE);
+    ofoperation_create(group, rule, OFOPERATION_DELETE, OFPRR_DELETE);
     oftable_remove_rule(rule);
     ofproto->ofproto_class->rule_destruct(rule);
 }
@@ -2965,7 +3145,7 @@ ofproto_rule_send_removed(struct rule *rule, uint8_t reason)
 {
     struct ofputil_flow_removed fr;
 
-    if (rule_is_hidden(rule) || !rule->send_flow_removed) {
+    if (ofproto_rule_is_hidden(rule) || !rule->send_flow_removed) {
         return;
     }
 
@@ -2975,6 +3155,7 @@ ofproto_rule_send_removed(struct rule *rule, uint8_t reason)
     calc_flow_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,
                                                  &fr.byte_count);
 
@@ -2999,6 +3180,9 @@ ofproto_rule_update_used(struct rule *rule, long long int used)
  * OFPRR_HARD_TIMEOUT or OFPRR_IDLE_TIMEOUT), and then removes 'rule' from its
  * ofproto.
  *
+ * 'rule' must not have a pending operation (that is, 'rule->pending' must be
+ * NULL).
+ *
  * ofproto implementation ->run() functions should use this function to expire
  * OpenFlow flows. */
 void
@@ -3012,7 +3196,7 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason)
     ofproto_rule_send_removed(rule, reason);
 
     group = ofopgroup_create_unattached(ofproto);
-    ofoperation_create(group, rule, OFOPERATION_DELETE);
+    ofoperation_create(group, rule, OFOPERATION_DELETE, reason);
     oftable_remove_rule(rule);
     ofproto->ofproto_class->rule_destruct(rule);
     ofopgroup_submit(group);
@@ -3021,28 +3205,69 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason)
 static enum ofperr
 handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 {
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     struct ofputil_flow_mod fm;
+    uint64_t ofpacts_stub[1024 / 8];
+    struct ofpbuf ofpacts;
     enum ofperr error;
+    long long int now;
 
     error = reject_slave_controller(ofconn);
     if (error) {
-        return error;
+        goto exit;
     }
 
-    error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn));
+    ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+    error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
+                                    &ofpacts);
     if (error) {
-        return error;
+        goto exit_free_ofpacts;
     }
 
-    /* We do not support the emergency flow cache.  It will hopefully get
-     * dropped from OpenFlow in the near future. */
-    if (fm.flags & OFPFF_EMERG) {
-        /* There isn't a good fit for an error code, so just state that the
-         * flow table is full. */
-        return OFPERR_OFPFMFC_ALL_TABLES_FULL;
+    if (fm.flags & OFPFF10_EMERG) {
+    /* We do not support the OpenFlow 1.0 emergency flow cache, which is not
+     * required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. */
+        /* We do not support the emergency flow cache.  It will hopefully get
+         * dropped from OpenFlow in the near future.  There is no good error
+         * code, so just state that the flow table is full. */
+        error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
+    } else {
+        error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
     }
+    if (error) {
+        goto exit_free_ofpacts;
+    }
+
+    /* Record the operation for logging a summary report. */
+    switch (fm.command) {
+    case OFPFC_ADD:
+        ofproto->n_add++;
+        break;
 
-    return handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
+    case OFPFC_MODIFY:
+    case OFPFC_MODIFY_STRICT:
+        ofproto->n_modify++;
+        break;
+
+    case OFPFC_DELETE:
+    case OFPFC_DELETE_STRICT:
+        ofproto->n_delete++;
+        break;
+    }
+
+    now = time_msec();
+    if (ofproto->next_op_report == LLONG_MAX) {
+        ofproto->first_op = now;
+        ofproto->next_op_report = MAX(now + 10 * 1000,
+                                      ofproto->op_backoff);
+        ofproto->op_backoff = ofproto->next_op_report + 60 * 1000;
+    }
+    ofproto->last_op = now;
+
+exit_free_ofpacts:
+    ofpbuf_uninit(&ofpacts);
+exit:
+    return error;
 }
 
 static enum ofperr
@@ -3073,8 +3298,9 @@ handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
 
     default:
         if (fm->command > 0xff) {
-            VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but "
-                         "flow_mod_table_id extension is not enabled");
+            VLOG_WARN_RL(&rl, "%s: flow_mod has explicit table_id but "
+                         "flow_mod_table_id extension is not enabled",
+                         ofproto->name);
         }
         return OFPERR_OFPFMFC_BAD_COMMAND;
     }
@@ -3083,7 +3309,7 @@ handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
 static enum ofperr
 handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    struct nx_role_request *nrr = (struct nx_role_request *) oh;
+    const struct nx_role_request *nrr = ofpmsg_body(oh);
     struct nx_role_request *reply;
     struct ofpbuf *buf;
     uint32_t role;
@@ -3101,7 +3327,8 @@ handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
 
     ofconn_set_role(ofconn, role);
 
-    reply = make_nxmsg_xid(sizeof *reply, NXT_ROLE_REPLY, oh->xid, &buf);
+    buf = ofpraw_alloc_reply(OFPRAW_NXT_ROLE_REPLY, oh, 0);
+    reply = ofpbuf_put_zeros(buf, sizeof *reply);
     reply->role = htonl(role);
     ofconn_send_reply(ofconn, buf);
 
@@ -3112,8 +3339,7 @@ static enum ofperr
 handle_nxt_flow_mod_table_id(struct ofconn *ofconn,
                              const struct ofp_header *oh)
 {
-    const struct nx_flow_mod_table_id *msg
-        = (const struct nx_flow_mod_table_id *) oh;
+    const struct nx_flow_mod_table_id *msg = ofpmsg_body(oh);
     enum ofputil_protocol cur, next;
 
     cur = ofconn_get_protocol(ofconn);
@@ -3126,8 +3352,7 @@ handle_nxt_flow_mod_table_id(struct ofconn *ofconn,
 static enum ofperr
 handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    const struct nx_set_flow_format *msg
-        = (const struct nx_set_flow_format *) oh;
+    const struct nx_set_flow_format *msg = ofpmsg_body(oh);
     enum ofputil_protocol cur, next;
     enum ofputil_protocol next_base;
 
@@ -3151,10 +3376,9 @@ static enum ofperr
 handle_nxt_set_packet_in_format(struct ofconn *ofconn,
                                 const struct ofp_header *oh)
 {
-    const struct nx_set_packet_in_format *msg;
+    const struct nx_set_packet_in_format *msg = ofpmsg_body(oh);
     uint32_t format;
 
-    msg = (const struct nx_set_packet_in_format *) oh;
     format = ntohl(msg->format);
     if (format != NXPIF_OPENFLOW10 && format != NXPIF_NXM) {
         return OFPERR_OFPBRC_EPERM;
@@ -3173,7 +3397,7 @@ handle_nxt_set_packet_in_format(struct ofconn *ofconn,
 static enum ofperr
 handle_nxt_set_async_config(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    const struct nx_async_config *msg = (const struct nx_async_config *) oh;
+    const struct nx_async_config *msg = ofpmsg_body(oh);
     uint32_t master[OAM_N_TYPES];
     uint32_t slave[OAM_N_TYPES];
 
@@ -3186,6 +3410,10 @@ handle_nxt_set_async_config(struct ofconn *ofconn, const struct ofp_header *oh)
     slave[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[1]);
 
     ofconn_set_async_config(ofconn, master, slave);
+    if (ofconn_get_type(ofconn) == OFCONN_SERVICE &&
+        !ofconn_get_miss_send_len(ofconn)) {
+        ofconn_set_miss_send_len(ofconn, OFP_DEFAULT_MISS_SEND_LEN);
+    }
 
     return 0;
 }
@@ -3194,9 +3422,8 @@ static enum ofperr
 handle_nxt_set_controller_id(struct ofconn *ofconn,
                              const struct ofp_header *oh)
 {
-    const struct nx_controller_id *nci;
+    const struct nx_controller_id *nci = ofpmsg_body(oh);
 
-    nci = (const struct nx_controller_id *) oh;
     if (!is_all_zeros(nci->zero, sizeof nci->zero)) {
         return OFPERR_NXBRC_MUST_BE_ZERO;
     }
@@ -3208,134 +3435,381 @@ handle_nxt_set_controller_id(struct ofconn *ofconn,
 static enum ofperr
 handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    struct ofp_header *ob;
     struct ofpbuf *buf;
 
     if (ofconn_has_pending_opgroups(ofconn)) {
         return OFPROTO_POSTPONE;
     }
 
-    ob = make_openflow_xid(sizeof *ob, OFPT10_BARRIER_REPLY, oh->xid, &buf);
+    buf = ofpraw_alloc_reply((oh->version == OFP10_VERSION
+                              ? OFPRAW_OFPT10_BARRIER_REPLY
+                              : OFPRAW_OFPT11_BARRIER_REPLY), oh, 0);
     ofconn_send_reply(ofconn, buf);
     return 0;
 }
 
+static void
+ofproto_compose_flow_refresh_update(const struct rule *rule,
+                                    enum nx_flow_monitor_flags flags,
+                                    struct list *msgs)
+{
+    struct ofoperation *op = rule->pending;
+    struct ofputil_flow_update fu;
+
+    if (op && op->type == OFOPERATION_ADD && !op->victim) {
+        /* We'll report the final flow when the operation completes.  Reporting
+         * it now would cause a duplicate report later. */
+        return;
+    }
+
+    fu.event = (flags & (NXFMF_INITIAL | NXFMF_ADD)
+                ? NXFME_ADDED : NXFME_MODIFIED);
+    fu.reason = 0;
+    fu.idle_timeout = rule->idle_timeout;
+    fu.hard_timeout = rule->hard_timeout;
+    fu.table_id = rule->table_id;
+    fu.cookie = rule->flow_cookie;
+    fu.match = CONST_CAST(struct cls_rule *, &rule->cr);
+    if (!(flags & NXFMF_ACTIONS)) {
+        fu.ofpacts = NULL;
+        fu.ofpacts_len = 0;
+    } else if (!op) {
+        fu.ofpacts = rule->ofpacts;
+        fu.ofpacts_len = rule->ofpacts_len;
+    } else {
+        /* An operation is in progress.  Use the previous version of the flow's
+         * actions, so that when the operation commits we report the change. */
+        switch (op->type) {
+        case OFOPERATION_ADD:
+            /* We already verified that there was a victim. */
+            fu.ofpacts = op->victim->ofpacts;
+            fu.ofpacts_len = op->victim->ofpacts_len;
+            break;
+
+        case OFOPERATION_MODIFY:
+            if (op->ofpacts) {
+                fu.ofpacts = op->ofpacts;
+                fu.ofpacts_len = op->ofpacts_len;
+            } else {
+                fu.ofpacts = rule->ofpacts;
+                fu.ofpacts_len = rule->ofpacts_len;
+            }
+            break;
+
+        case OFOPERATION_DELETE:
+            fu.ofpacts = rule->ofpacts;
+            fu.ofpacts_len = rule->ofpacts_len;
+            break;
+
+        default:
+            NOT_REACHED();
+        }
+    }
+
+    if (list_is_empty(msgs)) {
+        ofputil_start_flow_update(msgs);
+    }
+    ofputil_append_flow_update(&fu, msgs);
+}
+
+void
+ofmonitor_compose_refresh_updates(struct list *rules, struct list *msgs)
+{
+    struct rule *rule;
+
+    LIST_FOR_EACH (rule, ofproto_node, rules) {
+        enum nx_flow_monitor_flags flags = rule->monitor_flags;
+        rule->monitor_flags = 0;
+
+        ofproto_compose_flow_refresh_update(rule, flags, msgs);
+    }
+}
+
+static void
+ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m,
+                                       struct rule *rule, uint64_t seqno,
+                                       struct list *rules)
+{
+    enum nx_flow_monitor_flags update;
+
+    if (ofproto_rule_is_hidden(rule)) {
+        return;
+    }
+
+    if (!(rule->pending
+          ? ofoperation_has_out_port(rule->pending, m->out_port)
+          : ofproto_rule_has_out_port(rule, m->out_port))) {
+        return;
+    }
+
+    if (seqno) {
+        if (rule->add_seqno > seqno) {
+            update = NXFMF_ADD | NXFMF_MODIFY;
+        } else if (rule->modify_seqno > seqno) {
+            update = NXFMF_MODIFY;
+        } else {
+            return;
+        }
+
+        if (!(m->flags & update)) {
+            return;
+        }
+    } else {
+        update = NXFMF_INITIAL;
+    }
+
+    if (!rule->monitor_flags) {
+        list_push_back(rules, &rule->ofproto_node);
+    }
+    rule->monitor_flags |= update | (m->flags & NXFMF_ACTIONS);
+}
+
+static void
+ofproto_collect_ofmonitor_refresh_rules(const struct ofmonitor *m,
+                                        uint64_t seqno,
+                                        struct list *rules)
+{
+    const struct ofproto *ofproto = ofconn_get_ofproto(m->ofconn);
+    const struct ofoperation *op;
+    const struct oftable *table;
+
+    FOR_EACH_MATCHING_TABLE (table, m->table_id, ofproto) {
+        struct cls_cursor cursor;
+        struct rule *rule;
+
+        cls_cursor_init(&cursor, &table->cls, &m->match);
+        CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+            assert(!rule->pending); /* XXX */
+            ofproto_collect_ofmonitor_refresh_rule(m, rule, seqno, rules);
+        }
+    }
+
+    HMAP_FOR_EACH (op, hmap_node, &ofproto->deletions) {
+        struct rule *rule = op->rule;
+
+        if (((m->table_id == 0xff
+              ? !(ofproto->tables[rule->table_id].flags & OFTABLE_HIDDEN)
+              : m->table_id == rule->table_id))
+            && cls_rule_is_loose_match(&rule->cr, &m->match)) {
+            ofproto_collect_ofmonitor_refresh_rule(m, rule, seqno, rules);
+        }
+    }
+}
+
+static void
+ofproto_collect_ofmonitor_initial_rules(struct ofmonitor *m,
+                                        struct list *rules)
+{
+    if (m->flags & NXFMF_INITIAL) {
+        ofproto_collect_ofmonitor_refresh_rules(m, 0, rules);
+    }
+}
+
+void
+ofmonitor_collect_resume_rules(struct ofmonitor *m,
+                               uint64_t seqno, struct list *rules)
+{
+    ofproto_collect_ofmonitor_refresh_rules(m, seqno, rules);
+}
+
+static enum ofperr
+handle_flow_monitor_request(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+    struct ofmonitor **monitors;
+    size_t n_monitors, allocated_monitors;
+    struct list replies;
+    enum ofperr error;
+    struct list rules;
+    struct ofpbuf b;
+    size_t i;
+
+    error = 0;
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    monitors = NULL;
+    n_monitors = allocated_monitors = 0;
+    for (;;) {
+        struct ofputil_flow_monitor_request request;
+        struct ofmonitor *m;
+        int retval;
+
+        retval = ofputil_decode_flow_monitor_request(&request, &b);
+        if (retval == EOF) {
+            break;
+        } else if (retval) {
+            error = retval;
+            goto error;
+        }
+
+        if (request.table_id != 0xff
+            && request.table_id >= ofproto->n_tables) {
+            error = OFPERR_OFPBRC_BAD_TABLE_ID;
+            goto error;
+        }
+
+        error = ofmonitor_create(&request, ofconn, &m);
+        if (error) {
+            goto error;
+        }
+
+        if (n_monitors >= allocated_monitors) {
+            monitors = x2nrealloc(monitors, &allocated_monitors,
+                                  sizeof *monitors);
+        }
+        monitors[n_monitors++] = m;
+    }
+
+    list_init(&rules);
+    for (i = 0; i < n_monitors; i++) {
+        ofproto_collect_ofmonitor_initial_rules(monitors[i], &rules);
+    }
+
+    ofpmp_init(&replies, oh);
+    ofmonitor_compose_refresh_updates(&rules, &replies);
+    ofconn_send_replies(ofconn, &replies);
+
+    free(monitors);
+
+    return 0;
+
+error:
+    for (i = 0; i < n_monitors; i++) {
+        ofmonitor_destroy(monitors[i]);
+    }
+    free(monitors);
+    return error;
+}
+
+static enum ofperr
+handle_flow_monitor_cancel(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+    struct ofmonitor *m;
+    uint32_t id;
+
+    id = ofputil_decode_flow_monitor_cancel(oh);
+    m = ofmonitor_lookup(ofconn, id);
+    if (!m) {
+        return OFPERR_NXBRC_FM_BAD_ID;
+    }
+
+    ofmonitor_destroy(m);
+    return 0;
+}
+
 static enum ofperr
 handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 {
     const struct ofp_header *oh = msg->data;
-    const struct ofputil_msg_type *type;
+    enum ofptype type;
     enum ofperr error;
 
-    error = ofputil_decode_msg_type(oh, &type);
+    error = ofptype_decode(&type, oh);
     if (error) {
         return error;
     }
 
-    switch (ofputil_msg_type_code(type)) {
+    switch (type) {
         /* OpenFlow requests. */
-    case OFPUTIL_OFPT_ECHO_REQUEST:
+    case OFPTYPE_ECHO_REQUEST:
         return handle_echo_request(ofconn, oh);
 
-    case OFPUTIL_OFPT_FEATURES_REQUEST:
+    case OFPTYPE_FEATURES_REQUEST:
         return handle_features_request(ofconn, oh);
 
-    case OFPUTIL_OFPT_GET_CONFIG_REQUEST:
+    case OFPTYPE_GET_CONFIG_REQUEST:
         return handle_get_config_request(ofconn, oh);
 
-    case OFPUTIL_OFPT_SET_CONFIG:
-        return handle_set_config(ofconn, msg->data);
+    case OFPTYPE_SET_CONFIG:
+        return handle_set_config(ofconn, oh);
 
-    case OFPUTIL_OFPT_PACKET_OUT:
-        return handle_packet_out(ofconn, msg->data);
+    case OFPTYPE_PACKET_OUT:
+        return handle_packet_out(ofconn, oh);
 
-    case OFPUTIL_OFPT_PORT_MOD:
+    case OFPTYPE_PORT_MOD:
         return handle_port_mod(ofconn, oh);
 
-    case OFPUTIL_OFPT_FLOW_MOD:
+    case OFPTYPE_FLOW_MOD:
         return handle_flow_mod(ofconn, oh);
 
-    case OFPUTIL_OFPT_BARRIER_REQUEST:
+    case OFPTYPE_BARRIER_REQUEST:
         return handle_barrier_request(ofconn, oh);
 
         /* OpenFlow replies. */
-    case OFPUTIL_OFPT_ECHO_REPLY:
+    case OFPTYPE_ECHO_REPLY:
         return 0;
 
         /* Nicira extension requests. */
-    case OFPUTIL_NXT_ROLE_REQUEST:
+    case OFPTYPE_ROLE_REQUEST:
         return handle_role_request(ofconn, oh);
 
-    case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
+    case OFPTYPE_FLOW_MOD_TABLE_ID:
         return handle_nxt_flow_mod_table_id(ofconn, oh);
 
-    case OFPUTIL_NXT_SET_FLOW_FORMAT:
+    case OFPTYPE_SET_FLOW_FORMAT:
         return handle_nxt_set_flow_format(ofconn, oh);
 
-    case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
+    case OFPTYPE_SET_PACKET_IN_FORMAT:
         return handle_nxt_set_packet_in_format(ofconn, oh);
 
-    case OFPUTIL_NXT_SET_CONTROLLER_ID:
+    case OFPTYPE_SET_CONTROLLER_ID:
         return handle_nxt_set_controller_id(ofconn, oh);
 
-    case OFPUTIL_NXT_FLOW_MOD:
-        return handle_flow_mod(ofconn, oh);
-
-    case OFPUTIL_NXT_FLOW_AGE:
+    case OFPTYPE_FLOW_AGE:
         /* Nothing to do. */
         return 0;
 
-    case OFPUTIL_NXT_SET_ASYNC_CONFIG:
+    case OFPTYPE_FLOW_MONITOR_CANCEL:
+        return handle_flow_monitor_cancel(ofconn, oh);
+
+    case OFPTYPE_SET_ASYNC_CONFIG:
         return handle_nxt_set_async_config(ofconn, oh);
 
         /* Statistics requests. */
-    case OFPUTIL_OFPST_DESC_REQUEST:
-        return handle_desc_stats_request(ofconn, msg->data);
-
-    case OFPUTIL_OFPST_FLOW_REQUEST:
-    case OFPUTIL_NXST_FLOW_REQUEST:
-        return handle_flow_stats_request(ofconn, msg->data);
-
-    case OFPUTIL_OFPST_AGGREGATE_REQUEST:
-    case OFPUTIL_NXST_AGGREGATE_REQUEST:
-        return handle_aggregate_stats_request(ofconn, msg->data);
-
-    case OFPUTIL_OFPST_TABLE_REQUEST:
-        return handle_table_stats_request(ofconn, msg->data);
-
-    case OFPUTIL_OFPST_PORT_REQUEST:
-        return handle_port_stats_request(ofconn, msg->data);
-
-    case OFPUTIL_OFPST_QUEUE_REQUEST:
-        return handle_queue_stats_request(ofconn, msg->data);
-
-    case OFPUTIL_MSG_INVALID:
-    case OFPUTIL_OFPT_HELLO:
-    case OFPUTIL_OFPT_ERROR:
-    case OFPUTIL_OFPT_FEATURES_REPLY:
-    case OFPUTIL_OFPT_GET_CONFIG_REPLY:
-    case OFPUTIL_OFPT_PACKET_IN:
-    case OFPUTIL_OFPT_FLOW_REMOVED:
-    case OFPUTIL_OFPT_PORT_STATUS:
-    case OFPUTIL_OFPT_BARRIER_REPLY:
-    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
-    case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
-    case OFPUTIL_OFPST_DESC_REPLY:
-    case OFPUTIL_OFPST_FLOW_REPLY:
-    case OFPUTIL_OFPST_QUEUE_REPLY:
-    case OFPUTIL_OFPST_PORT_REPLY:
-    case OFPUTIL_OFPST_TABLE_REPLY:
-    case OFPUTIL_OFPST_AGGREGATE_REPLY:
-    case OFPUTIL_NXT_ROLE_REPLY:
-    case OFPUTIL_NXT_FLOW_REMOVED:
-    case OFPUTIL_NXT_PACKET_IN:
-    case OFPUTIL_NXST_FLOW_REPLY:
-    case OFPUTIL_NXST_AGGREGATE_REPLY:
+    case OFPTYPE_DESC_STATS_REQUEST:
+        return handle_desc_stats_request(ofconn, oh);
+
+    case OFPTYPE_FLOW_STATS_REQUEST:
+        return handle_flow_stats_request(ofconn, oh);
+
+    case OFPTYPE_AGGREGATE_STATS_REQUEST:
+        return handle_aggregate_stats_request(ofconn, oh);
+
+    case OFPTYPE_TABLE_STATS_REQUEST:
+        return handle_table_stats_request(ofconn, oh);
+
+    case OFPTYPE_PORT_STATS_REQUEST:
+        return handle_port_stats_request(ofconn, oh);
+
+    case OFPTYPE_QUEUE_STATS_REQUEST:
+        return handle_queue_stats_request(ofconn, oh);
+
+    case OFPTYPE_PORT_DESC_STATS_REQUEST:
+        return handle_port_desc_stats_request(ofconn, oh);
+
+    case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+        return handle_flow_monitor_request(ofconn, oh);
+
+    case OFPTYPE_HELLO:
+    case OFPTYPE_ERROR:
+    case OFPTYPE_FEATURES_REPLY:
+    case OFPTYPE_GET_CONFIG_REPLY:
+    case OFPTYPE_PACKET_IN:
+    case OFPTYPE_FLOW_REMOVED:
+    case OFPTYPE_PORT_STATUS:
+    case OFPTYPE_BARRIER_REPLY:
+    case OFPTYPE_DESC_STATS_REPLY:
+    case OFPTYPE_FLOW_STATS_REPLY:
+    case OFPTYPE_QUEUE_STATS_REPLY:
+    case OFPTYPE_PORT_STATS_REPLY:
+    case OFPTYPE_TABLE_STATS_REPLY:
+    case OFPTYPE_AGGREGATE_STATS_REPLY:
+    case OFPTYPE_PORT_DESC_STATS_REPLY:
+    case OFPTYPE_ROLE_REPLY:
+    case OFPTYPE_FLOW_MONITOR_PAUSED:
+    case OFPTYPE_FLOW_MONITOR_RESUMED:
+    case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
     default:
-        return (oh->type == OFPT10_STATS_REQUEST ||
-                oh->type == OFPT10_STATS_REPLY
-                ? OFPERR_OFPBRC_BAD_STAT
-                : OFPERR_OFPBRC_BAD_TYPE);
+        return OFPERR_OFPBRC_BAD_TYPE;
     }
 }
 
@@ -3407,8 +3881,8 @@ ofopgroup_create(struct ofproto *ofproto, struct ofconn *ofconn,
 static void
 ofopgroup_submit(struct ofopgroup *group)
 {
-    if (list_is_empty(&group->ops)) {
-        ofopgroup_destroy(group);
+    if (!group->n_running) {
+        ofopgroup_complete(group);
     } else {
         list_push_back(&group->ofproto->pending, &group->ofproto_node);
         group->ofproto->n_pending++;
@@ -3416,31 +3890,153 @@ ofopgroup_submit(struct ofopgroup *group)
 }
 
 static void
-ofopgroup_destroy(struct ofopgroup *group)
+ofopgroup_complete(struct ofopgroup *group)
 {
-    assert(list_is_empty(&group->ops));
+    struct ofproto *ofproto = group->ofproto;
+
+    struct ofconn *abbrev_ofconn;
+    ovs_be32 abbrev_xid;
+
+    struct ofoperation *op, *next_op;
+    int error;
+
+    assert(!group->n_running);
+
+    error = 0;
+    LIST_FOR_EACH (op, group_node, &group->ops) {
+        if (op->error) {
+            error = op->error;
+            break;
+        }
+    }
+
+    if (!error && group->ofconn && group->buffer_id != UINT32_MAX) {
+        LIST_FOR_EACH (op, group_node, &group->ops) {
+            if (op->type != OFOPERATION_DELETE) {
+                struct ofpbuf *packet;
+                uint16_t in_port;
+
+                error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id,
+                                               &packet, &in_port);
+                if (packet) {
+                    assert(!error);
+                    error = rule_execute(op->rule, in_port, packet);
+                }
+                break;
+            }
+        }
+    }
+
+    if (!error && !list_is_empty(&group->ofconn_node)) {
+        abbrev_ofconn = group->ofconn;
+        abbrev_xid = group->request->xid;
+    } else {
+        abbrev_ofconn = NULL;
+        abbrev_xid = htonl(0);
+    }
+    LIST_FOR_EACH_SAFE (op, next_op, group_node, &group->ops) {
+        struct rule *rule = op->rule;
+
+        if (!op->error && !ofproto_rule_is_hidden(rule)) {
+            /* Check that we can just cast from ofoperation_type to
+             * nx_flow_update_event. */
+            BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_ADD
+                              == NXFME_ADDED);
+            BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_DELETE
+                              == NXFME_DELETED);
+            BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_MODIFY
+                              == NXFME_MODIFIED);
+
+            ofmonitor_report(ofproto->connmgr, rule,
+                             (enum nx_flow_update_event) op->type,
+                             op->reason, abbrev_ofconn, abbrev_xid);
+        }
+
+        rule->pending = NULL;
+
+        switch (op->type) {
+        case OFOPERATION_ADD:
+            if (!op->error) {
+                ofproto_rule_destroy__(op->victim);
+                if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK))
+                    == htons(VLAN_VID_MASK)) {
+                    if (ofproto->vlan_bitmap) {
+                        uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci);
+
+                        if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) {
+                            bitmap_set1(ofproto->vlan_bitmap, vid);
+                            ofproto->vlans_changed = true;
+                        }
+                    } else {
+                        ofproto->vlans_changed = true;
+                    }
+                }
+            } else {
+                oftable_substitute_rule(rule, op->victim);
+                ofproto_rule_destroy__(rule);
+            }
+            break;
+
+        case OFOPERATION_DELETE:
+            assert(!op->error);
+            ofproto_rule_destroy__(rule);
+            op->rule = NULL;
+            break;
+
+        case OFOPERATION_MODIFY:
+            if (!op->error) {
+                rule->modified = time_msec();
+            } else {
+                rule->flow_cookie = op->flow_cookie;
+                if (op->ofpacts) {
+                    free(rule->ofpacts);
+                    rule->ofpacts = op->ofpacts;
+                    rule->ofpacts_len = op->ofpacts_len;
+                    op->ofpacts = NULL;
+                    op->ofpacts_len = 0;
+                }
+            }
+            break;
+
+        default:
+            NOT_REACHED();
+        }
+
+        ofoperation_destroy(op);
+    }
+
+    ofmonitor_flush(ofproto->connmgr);
+
     if (!list_is_empty(&group->ofproto_node)) {
-        assert(group->ofproto->n_pending > 0);
-        group->ofproto->n_pending--;
+        assert(ofproto->n_pending > 0);
+        ofproto->n_pending--;
         list_remove(&group->ofproto_node);
     }
     if (!list_is_empty(&group->ofconn_node)) {
         list_remove(&group->ofconn_node);
-        if (group->error) {
-            ofconn_send_error(group->ofconn, group->request, group->error);
+        if (error) {
+            ofconn_send_error(group->ofconn, group->request, error);
         }
-        connmgr_retry(group->ofproto->connmgr);
+        connmgr_retry(ofproto->connmgr);
     }
     free(group->request);
     free(group);
 }
 
 /* Initiates a new operation on 'rule', of the specified 'type', within
- * 'group'.  Prior to calling, 'rule' must not have any pending operation. */
-static void
+ * 'group'.  Prior to calling, 'rule' must not have any pending operation.
+ *
+ * For a 'type' of OFOPERATION_DELETE, 'reason' should specify the reason that
+ * the flow is being deleted.  For other 'type's, 'reason' is ignored (use 0).
+ *
+ * Returns the newly created ofoperation (which is also available as
+ * rule->pending). */
+static struct ofoperation *
 ofoperation_create(struct ofopgroup *group, struct rule *rule,
-                   enum ofoperation_type type)
+                   enum ofoperation_type type,
+                   enum ofp_flow_removed_reason reason)
 {
+    struct ofproto *ofproto = group->ofproto;
     struct ofoperation *op;
 
     assert(!rule->pending);
@@ -3450,13 +4046,17 @@ ofoperation_create(struct ofopgroup *group, struct rule *rule,
     list_push_back(&group->ops, &op->group_node);
     op->rule = rule;
     op->type = type;
-    op->status = -1;
+    op->reason = reason;
     op->flow_cookie = rule->flow_cookie;
 
+    group->n_running++;
+
     if (type == OFOPERATION_DELETE) {
-        hmap_insert(&op->group->ofproto->deletions, &op->hmap_node,
+        hmap_insert(&ofproto->deletions, &op->hmap_node,
                     cls_rule_hash(&rule->cr, rule->table_id));
     }
+
+    return op;
 }
 
 static void
@@ -3471,12 +4071,8 @@ ofoperation_destroy(struct ofoperation *op)
         hmap_remove(&group->ofproto->deletions, &op->hmap_node);
     }
     list_remove(&op->group_node);
-    free(op->actions);
+    free(op->ofpacts);
     free(op);
-
-    if (list_is_empty(&group->ops) && !list_is_empty(&group->ofproto_node)) {
-        ofopgroup_destroy(group);
-    }
 }
 
 /* Indicates that 'op' completed with status 'error', which is either 0 to
@@ -3512,76 +4108,15 @@ void
 ofoperation_complete(struct ofoperation *op, enum ofperr error)
 {
     struct ofopgroup *group = op->group;
-    struct rule *rule = op->rule;
-    struct ofproto *ofproto = rule->ofproto;
-
-    assert(rule->pending == op);
-    assert(op->status < 0);
-
-    if (!error
-        && !group->error
-        && op->type != OFOPERATION_DELETE
-        && group->ofconn
-        && group->buffer_id != UINT32_MAX
-        && list_is_singleton(&op->group_node)) {
-        struct ofpbuf *packet;
-        uint16_t in_port;
-
-        error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id,
-                                       &packet, &in_port);
-        if (packet) {
-            assert(!error);
-            error = rule_execute(rule, in_port, packet);
-        }
-    }
-    if (!group->error) {
-        group->error = error;
-    }
-
-    switch (op->type) {
-    case OFOPERATION_ADD:
-        if (!error) {
-            ofproto_rule_destroy__(op->victim);
-            if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK))
-                == htons(VLAN_VID_MASK)) {
-                if (ofproto->vlan_bitmap) {
-                    uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci);
-
-                    if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) {
-                        bitmap_set1(ofproto->vlan_bitmap, vid);
-                        ofproto->vlans_changed = true;
-                    }
-                } else {
-                    ofproto->vlans_changed = true;
-                }
-            }
-        } else {
-            oftable_substitute_rule(rule, op->victim);
-            ofproto_rule_destroy__(rule);
-        }
-        break;
-
-    case OFOPERATION_DELETE:
-        assert(!error);
-        ofproto_rule_destroy__(rule);
-        op->rule = NULL;
-        break;
 
-    case OFOPERATION_MODIFY:
-        if (!error) {
-            rule->modified = time_msec();
-        } else {
-            free(rule->actions);
-            rule->actions = op->actions;
-            rule->n_actions = op->n_actions;
-            op->actions = NULL;
-        }
-        break;
+    assert(op->rule->pending == op);
+    assert(group->n_running > 0);
+    assert(!error || op->type != OFOPERATION_DELETE);
 
-    default:
-        NOT_REACHED();
+    op->error = error;
+    if (!--group->n_running && !list_is_empty(&group->ofproto_node)) {
+        ofopgroup_complete(group);
     }
-    ofoperation_destroy(op);
 }
 
 struct rule *
@@ -3605,8 +4140,9 @@ pick_datapath_id(const struct ofproto *ofproto)
         if (!error) {
             return eth_addr_to_uint64(ea);
         }
-        VLOG_WARN("could not get MAC address for %s (%s)",
-                  netdev_get_name(port->netdev), strerror(error));
+        VLOG_WARN("%s: could not get MAC address for %s (%s)",
+                  ofproto->name, netdev_get_name(port->netdev),
+                  strerror(error));
     }
     return ofproto->fallback_dpid;
 }
@@ -3682,7 +4218,8 @@ ofproto_evict(struct ofproto *ofproto)
                 break;
             }
 
-            ofoperation_create(group, rule, OFOPERATION_DELETE);
+            ofoperation_create(group, rule,
+                               OFOPERATION_DELETE, OFPRR_EVICTION);
             oftable_remove_rule(rule);
             ofproto->ofproto_class->rule_destruct(rule);
         }
@@ -3874,6 +4411,7 @@ oftable_init(struct oftable *table)
 {
     memset(table, 0, sizeof *table);
     classifier_init(&table->cls);
+    table->max_flows = UINT_MAX;
 }
 
 /* Destroys 'table', including its classifier and eviction groups.
index c40f5d3..32a00f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ struct netdev;
 struct ofproto;
 struct ofport;
 struct shash;
+struct simap;
 struct netdev_stats;
 
 struct ofproto_controller_info {
@@ -131,7 +132,7 @@ struct ofproto_controller {
     uint8_t dscp;               /* DSCP value for controller connection. */
 };
 
-#define DEFAULT_MFR_DESC "Nicira Networks, Inc."
+#define DEFAULT_MFR_DESC "Nicira, Inc."
 #define DEFAULT_HW_DESC "Open vSwitch"
 #define DEFAULT_SW_DESC VERSION
 #define DEFAULT_SERIAL_DESC "None"
@@ -153,6 +154,8 @@ int ofproto_run_fast(struct ofproto *);
 void ofproto_wait(struct ofproto *);
 bool ofproto_is_alive(const struct ofproto *);
 
+void ofproto_get_memory_usage(const struct ofproto *, struct simap *);
+
 /* A port within an OpenFlow switch.
  *
  * 'name' and 'type' are suitable for passing to netdev_open(). */
@@ -198,6 +201,7 @@ int ofproto_port_query_by_name(const struct ofproto *, const char *devname,
                                struct ofproto_port *);
 
 /* Top-level configuration. */
+uint64_t ofproto_get_datapath_id(const struct ofproto *);
 void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id);
 void ofproto_set_controllers(struct ofproto *,
                              const struct ofproto_controller *, size_t n);
@@ -345,6 +349,7 @@ void ofproto_get_all_flows(struct ofproto *p, struct ds *);
 void ofproto_get_netflow_ids(const struct ofproto *,
                              uint8_t *engine_type, uint8_t *engine_id);
 int ofproto_port_get_cfm_fault(const struct ofproto *, uint16_t ofp_port);
+int ofproto_port_get_cfm_opup(const struct ofproto *, uint16_t ofp_port);
 int ofproto_port_get_cfm_remote_mpids(const struct ofproto *,
                                       uint16_t ofp_port, const uint64_t **rmps,
                                       size_t *n_rmps);
index 6d7eb13..57e8e23 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,7 +28,9 @@
 #include "poll-loop.h"
 #include "random.h"
 #include "rconn.h"
+#include "sat-math.h"
 #include "timeval.h"
+#include "token-bucket.h"
 #include "vconn.h"
 
 struct pinqueue {
@@ -39,24 +41,13 @@ struct pinqueue {
 };
 
 struct pinsched {
-    /* Client-supplied parameters. */
-    int rate_limit;           /* Packets added to bucket per second. */
-    int burst_limit;          /* Maximum token bucket size, in packets. */
+    struct token_bucket token_bucket;
 
     /* One queue per physical port. */
     struct hmap queues;         /* Contains "struct pinqueue"s. */
     int n_queued;               /* Sum over queues[*].n. */
     struct pinqueue *next_txq;  /* Next pinqueue check in round-robin. */
 
-    /* Token bucket.
-     *
-     * It costs 1000 tokens to send a single packet_in message.  A single token
-     * per message would be more straightforward, but this choice lets us avoid
-     * round-off error in refill_bucket()'s calculation of how many tokens to
-     * add to the bucket, since no division step is needed. */
-    long long int last_fill;    /* Time at which we last added tokens. */
-    int tokens;                 /* Current number of tokens. */
-
     /* Transmission queue. */
     int n_txq;                  /* No. of packets waiting in rconn for tx. */
 
@@ -86,6 +77,20 @@ dequeue_packet(struct pinsched *ps, struct pinqueue *q)
     return packet;
 }
 
+static void
+adjust_limits(int *rate_limit, int *burst_limit)
+{
+    if (*rate_limit <= 0) {
+        *rate_limit = 1000;
+    }
+    if (*burst_limit <= 0) {
+        *burst_limit = *rate_limit / 4;
+    }
+    if (*burst_limit < 1) {
+        *burst_limit = 1;
+    }
+}
+
 /* Destroys 'q' and removes it from 'ps''s set of queues.
  * (The caller must ensure that 'q' is empty.) */
 static void
@@ -169,30 +174,13 @@ get_tx_packet(struct pinsched *ps)
     return packet;
 }
 
-/* Add tokens to the bucket based on elapsed time. */
-static void
-refill_bucket(struct pinsched *ps)
-{
-    long long int now = time_msec();
-    long long int tokens = (now - ps->last_fill) * ps->rate_limit + ps->tokens;
-    if (tokens >= 1000) {
-        ps->last_fill = now;
-        ps->tokens = MIN(tokens, ps->burst_limit * 1000);
-    }
-}
-
 /* Attempts to remove enough tokens from 'ps' to transmit a packet.  Returns
  * true if successful, false otherwise.  (In the latter case no tokens are
  * removed.) */
 static bool
 get_token(struct pinsched *ps)
 {
-    if (ps->tokens >= 1000) {
-        ps->tokens -= 1000;
-        return true;
-    } else {
-        return false;
-    }
+    return token_bucket_withdraw(&ps->token_bucket, 1000);
 }
 
 void
@@ -216,7 +204,7 @@ pinsched_send(struct pinsched *ps, uint16_t port_no,
          * otherwise wasted space. */
         ofpbuf_trim(packet);
 
-        if (ps->n_queued >= ps->burst_limit) {
+        if (ps->n_queued * 1000 >= ps->token_bucket.burst) {
             drop_packet(ps);
         }
         q = pinqueue_get(ps, port_no);
@@ -235,7 +223,6 @@ pinsched_run(struct pinsched *ps, pinsched_tx_cb *cb, void *aux)
 
         /* Drain some packets out of the bucket if possible, but limit the
          * number of iterations to allow other code to get work done too. */
-        refill_bucket(ps);
         for (i = 0; ps->n_queued && get_token(ps) && i < 50; i++) {
             cb(get_tx_packet(ps), aux);
         }
@@ -246,14 +233,7 @@ void
 pinsched_wait(struct pinsched *ps)
 {
     if (ps && ps->n_queued) {
-        if (ps->tokens >= 1000) {
-            /* We can transmit more packets as soon as we're called again. */
-            poll_immediate_wake();
-        } else {
-            /* We have to wait for the bucket to re-fill.  We could calculate
-             * the exact amount of time here for increased smoothness. */
-            poll_timer_wait(TIME_UPDATE_INTERVAL / 2);
-        }
+        token_bucket_wait(&ps->token_bucket, 1000);
     }
 }
 
@@ -264,16 +244,18 @@ pinsched_create(int rate_limit, int burst_limit)
     struct pinsched *ps;
 
     ps = xzalloc(sizeof *ps);
+
+    adjust_limits(&rate_limit, &burst_limit);
+    token_bucket_init(&ps->token_bucket,
+                      rate_limit, sat_mul(burst_limit, 1000));
+
     hmap_init(&ps->queues);
     ps->n_queued = 0;
     ps->next_txq = NULL;
-    ps->last_fill = time_msec();
-    ps->tokens = rate_limit * 100;
     ps->n_txq = 0;
     ps->n_normal = 0;
     ps->n_limited = 0;
     ps->n_queue_dropped = 0;
-    pinsched_set_limits(ps, rate_limit, burst_limit);
 
     return ps;
 }
@@ -298,25 +280,25 @@ void
 pinsched_get_limits(const struct pinsched *ps,
                     int *rate_limit, int *burst_limit)
 {
-    *rate_limit = ps->rate_limit;
-    *burst_limit = ps->burst_limit;
+    *rate_limit = ps->token_bucket.rate;
+    *burst_limit = ps->token_bucket.burst / 1000;
 }
 
 void
 pinsched_set_limits(struct pinsched *ps, int rate_limit, int burst_limit)
 {
-    if (rate_limit <= 0) {
-        rate_limit = 1000;
-    }
-    if (burst_limit <= 0) {
-        burst_limit = rate_limit / 4;
-    }
-    burst_limit = MAX(burst_limit, 1);
-    burst_limit = MIN(burst_limit, INT_MAX / 1000);
-
-    ps->rate_limit = rate_limit;
-    ps->burst_limit = burst_limit;
+    adjust_limits(&rate_limit, &burst_limit);
+    token_bucket_set(&ps->token_bucket,
+                     rate_limit, sat_mul(burst_limit, 1000));
     while (ps->n_queued > burst_limit) {
         drop_packet(ps);
     }
 }
+
+/* Returns the number of packets scheduled to be sent eventually by 'ps'.
+ * Returns 0 if 'ps' is null. */
+unsigned int
+pinsched_count_txqlen(const struct pinsched *ps)
+{
+    return ps ? ps->n_txq : 0;
+}
index 642ca30..061cb01 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,4 +32,6 @@ void pinsched_send(struct pinsched *, uint16_t port_no, struct ofpbuf *,
 void pinsched_run(struct pinsched *, pinsched_tx_cb *, void *aux);
 void pinsched_wait(struct pinsched *);
 
+unsigned int pinsched_count_txqlen(const struct pinsched *);
+
 #endif /* pinsched.h */
index 23c01e0..71be34a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -232,3 +232,23 @@ pktbuf_discard(struct pktbuf *pb, uint32_t id)
         p->buffer = NULL;
     }
 }
+
+/* Returns the number of packets buffered in 'pb'.  Returns 0 if 'pb' is
+ * null. */
+unsigned int
+pktbuf_count_packets(const struct pktbuf *pb)
+{
+    int n = 0;
+
+    if (pb) {
+        int i;
+
+        for (i = 0; i < PKTBUF_CNT; i++) {
+            if (pb->packets[i].buffer) {
+                n++;
+            }
+        }
+    }
+
+    return n;
+}
index e29117a..ec99aea 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -36,4 +36,6 @@ enum ofperr pktbuf_retrieve(struct pktbuf *, uint32_t id,
                             struct ofpbuf **bufferp, uint16_t *in_port);
 void pktbuf_discard(struct pktbuf *, uint32_t id);
 
+unsigned int pktbuf_count_packets(const struct pktbuf *);
+
 #endif /* pktbuf.h */
index 576e463..26b7a0b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 429818a..60ba478 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index c57e419..760dd13 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 4716150..ae6159c 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* 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.
index 52de5e8..7b0ec36 100755 (executable)
@@ -1,6 +1,6 @@
 #! /usr/bin/perl
 
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 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.
index 675c1c3..1aff0c5 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 05a4067..43fcb95 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -531,11 +531,14 @@ ovsdb_file_create(struct ovsdb *db, struct ovsdb_log *log,
 {
     long long int now = time_msec();
     struct ovsdb_file *file;
+    char *deref_name;
     char *abs_name;
 
     /* Use the absolute name of the file because ovsdb-server opens its
      * database before daemonize() chdirs to "/". */
-    abs_name = abs_file_name(NULL, file_name);
+    deref_name = follow_symlinks(file_name);
+    abs_name = abs_file_name(NULL, deref_name);
+    free(deref_name);
     if (!abs_name) {
         *filep = NULL;
         return ovsdb_io_error(0, "could not determine current "
@@ -646,7 +649,7 @@ ovsdb_file_compact(struct ovsdb_file *file)
 
     /* Lock temporary file. */
     tmp_name = xasprintf("%s.tmp", file->file_name);
-    retval = lockfile_lock(tmp_name, 0, &tmp_lock);
+    retval = lockfile_lock(tmp_name, &tmp_lock);
     if (retval) {
         error = ovsdb_io_error(retval, "could not get lock on %s", tmp_name);
         goto exit;
index 19d51c0..5d76a73 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 88656b9..bb887d0 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
 #include "reconnect.h"
 #include "row.h"
 #include "server.h"
+#include "simap.h"
 #include "stream.h"
 #include "table.h"
 #include "timeval.h"
@@ -51,6 +52,8 @@ static struct ovsdb_jsonrpc_session *ovsdb_jsonrpc_session_create(
     struct ovsdb_jsonrpc_remote *, struct jsonrpc_session *);
 static void ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *);
 static void ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *);
+static void ovsdb_jsonrpc_session_get_memory_usage_all(
+    const struct ovsdb_jsonrpc_remote *, struct simap *usage);
 static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *);
 static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *);
 static void ovsdb_jsonrpc_session_set_all_options(
@@ -293,6 +296,22 @@ ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr)
         ovsdb_jsonrpc_session_wait_all(remote);
     }
 }
+
+/* Adds some memory usage statistics for 'svr' into 'usage', for use with
+ * memory_report(). */
+void
+ovsdb_jsonrpc_server_get_memory_usage(const struct ovsdb_jsonrpc_server *svr,
+                                      struct simap *usage)
+{
+    struct shash_node *node;
+
+    simap_increase(usage, "sessions", svr->n_sessions);
+    SHASH_FOR_EACH (node, &svr->remotes) {
+        struct ovsdb_jsonrpc_remote *remote = node->data;
+
+        ovsdb_jsonrpc_session_get_memory_usage_all(remote, usage);
+    }
+}
 \f
 /* JSON-RPC database server session. */
 
@@ -315,6 +334,8 @@ struct ovsdb_jsonrpc_session {
 static void ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *);
 static int ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *);
 static void ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *);
+static void ovsdb_jsonrpc_session_get_memory_usage(
+    const struct ovsdb_jsonrpc_session *, struct simap *usage);
 static void ovsdb_jsonrpc_session_set_options(
     struct ovsdb_jsonrpc_session *, const struct ovsdb_jsonrpc_options *);
 static void ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *,
@@ -428,6 +449,27 @@ ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *remote)
     }
 }
 
+static void
+ovsdb_jsonrpc_session_get_memory_usage(const struct ovsdb_jsonrpc_session *s,
+                                       struct simap *usage)
+{
+    simap_increase(usage, "triggers", hmap_count(&s->triggers));
+    simap_increase(usage, "monitors", hmap_count(&s->monitors));
+    simap_increase(usage, "backlog", jsonrpc_session_get_backlog(s->js));
+}
+
+static void
+ovsdb_jsonrpc_session_get_memory_usage_all(
+    const struct ovsdb_jsonrpc_remote *remote,
+    struct simap *usage)
+{
+    struct ovsdb_jsonrpc_session *s;
+
+    LIST_FOR_EACH (s, node, &remote->sessions) {
+        ovsdb_jsonrpc_session_get_memory_usage(s, usage);
+    }
+}
+
 static void
 ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *remote)
 {
index 344ed9d..2dc0c78 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
 
 struct ovsdb;
 struct shash;
+struct simap;
 
 struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(struct ovsdb *);
 void ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *);
@@ -59,4 +60,7 @@ void ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *);
 void ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *);
 void ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *);
 
+void ovsdb_jsonrpc_server_get_memory_usage(const struct ovsdb_jsonrpc_server *,
+                                           struct simap *usage);
+
 #endif /* ovsdb/jsonrpc-server.h */
index f0926c0..b79535a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -80,7 +80,7 @@ ovsdb_log_open(const char *name, enum ovsdb_log_open_mode open_mode,
         locking = open_mode != OVSDB_LOG_READ_ONLY;
     }
     if (locking) {
-        int retval = lockfile_lock(name, 0, &lockfile);
+        int retval = lockfile_lock(name, &lockfile);
         if (retval) {
             error = ovsdb_io_error(retval, "%s: failed to lock lockfile",
                                    name);
@@ -95,7 +95,16 @@ ovsdb_log_open(const char *name, enum ovsdb_log_open_mode open_mode,
     } else if (open_mode == OVSDB_LOG_READ_WRITE) {
         flags = O_RDWR;
     } else if (open_mode == OVSDB_LOG_CREATE) {
-        flags = O_RDWR | O_CREAT | O_EXCL;
+        if (stat(name, &s) == -1 && errno == ENOENT
+            && lstat(name, &s) == 0 && S_ISLNK(s.st_mode)) {
+            /* 'name' is a dangling symlink.  We want to create the file that
+             * the symlink points to, but POSIX says that open() with O_EXCL
+             * must fail with EEXIST if the named file is a symlink.  So, we
+             * have to leave off O_EXCL and accept the race. */
+            flags = O_RDWR | O_CREAT;
+        } else {
+            flags = O_RDWR | O_CREAT | O_EXCL;
+        }
     } else {
         NOT_REACHED();
     }
index f48dc76..aad2543 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 7ae83fe..0dcd16f 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 86de6f2..a6545e4 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 7f9a19b..a813478 100644 (file)
@@ -84,7 +84,7 @@ If \fIdatabase\fR was created before schema versioning was introduced,
 then it will not have a version number and this command will print a
 blank line.
 .
-.IP "\fBlist\-columns\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR]"
+.IP "\fBlist\-tables\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR]"
 Connects to \fIserver\fR, retrieves the schema for \fIdatabase\fR, and
 prints a table listing the name of each table
 within the database.
@@ -104,7 +104,7 @@ operations, and prints the received reply on stdout.
 Connects to \fIserver\fR, retrieves all of the data in \fIdatabase\fR,
 and prints it on stdout as a series of tables.
 .
-.IP "\fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR"
+.IP "\fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]..."
 Connects to \fIserver\fR and monitors the contents of \fItable\fR in
 \fIdatabase\fR.  By default, the initial contents of \fItable\fR are
 printed, followed by each change as it occurs.  If at least one
index 077e3a8..a94d7cb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index dfde735..0b1933b 100755 (executable)
@@ -25,14 +25,14 @@ def constify(cType, const):
     else:
         return cType
 
-def is_string_map(column):
-    return (column.type.key
-            and column.type.value
-            and column.type.key.type == ovs.db.types.StringType
-            and column.type.value.type == ovs.db.types.StringType)
-
 def cMembers(prefix, columnName, column, const):
     type = column.type
+
+    if type.is_smap():
+        return [{'name': columnName,
+                 'type': 'struct smap ',
+                 'comment': ''}]
+
     if type.n_min == 1 and type.n_max == 1:
         singleton = True
         pointer = ''
@@ -77,6 +77,7 @@ def printCIDLHeader(schemaFile):
 #include <stdint.h>
 #include "ovsdb-data.h"
 #include "ovsdb-idl-provider.h"
+#include "smap.h"
 #include "uuid.h"''' % {'prefix': prefix.upper()}
 
     for tableName, table in sorted(schema.tables.iteritems()):
@@ -119,6 +120,7 @@ const struct %(s)s *%(s)s_next(const struct %(s)s *);
              (ROW) ? ((NEXT) = %(s)s_next(ROW), 1) : 0; \\
              (ROW) = (NEXT))
 
+void %(s)s_init(struct %(s)s *);
 void %(s)s_delete(const struct %(s)s *);
 struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
 ''' % {'s': structName, 'S': structName.upper()}
@@ -140,20 +142,15 @@ struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
 
         print
         for columnName, column in sorted(table.columns.iteritems()):
-
             print 'void %(s)s_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName},
-            args = ['%(type)s%(name)s' % member for member
-                    in cMembers(prefix, columnName, column, True)]
+            if column.type.is_smap():
+                args = ['const struct smap *']
+            else:
+                args = ['%(type)s%(name)s' % member for member
+                        in cMembers(prefix, columnName, column, True)]
             print '%s);' % ', '.join(args)
 
         print
-        for columnName, column in sorted(table.columns.iteritems()):
-            if not is_string_map(column):
-                continue
-            print "const char *%(s)s_get_%(c)s_value(const struct %(s)s *," \
-                    " const char *key, const char *default_value);" \
-                    % {'s': structName, 'c': columnName}
-
 
     # Table indexes.
     printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in sorted(schema.tables)] + ["%sN_TABLES" % prefix.upper()])
@@ -203,22 +200,6 @@ enum { sizeof_bool = sizeof(bool) };
 static bool inited;
 ''' % schema.idlHeader
 
-    try:
-        for table in schema.tables.itervalues():
-            for column in table.columns.itervalues():
-                if is_string_map(column):
-                    print """\
-static int
-bsearch_strcmp(const void *a_, const void *b_)
-{
-    char *const *a = a_;
-    char *const *b = b_;
-    return strcmp(*a, *b);
-}"""
-                    raise StopIteration
-    except StopIteration:
-        pass
-
     # Cast functions.
     for tableName, table in sorted(schema.tables.iteritems()):
         structName = "%s%s" % (prefix, tableName.lower())
@@ -244,7 +225,6 @@ static void
 {
     struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName,
                                                 'c': columnName}
-
             type = column.type
             if type.value:
                 keyVar = "row->key_%s" % columnName
@@ -253,7 +233,17 @@ static void
                 keyVar = "row->%s" % columnName
                 valueVar = None
 
-            if (type.n_min == 1 and type.n_max == 1) or type.is_optional_pointer():
+            if type.is_smap():
+                print "    size_t i;"
+                print
+                print "    assert(inited);"
+                print "    smap_init(&row->%s);" % columnName
+                print "    for (i = 0; i < datum->n; i++) {"
+                print "        smap_add(&row->%s," % columnName
+                print "                 datum->keys[i].string,"
+                print "                 datum->values[i].string);"
+                print "    }"
+            elif (type.n_min == 1 and type.n_max == 1) or type.is_optional_pointer():
                 print
                 print "    assert(inited);"
                 print "    if (datum->n >= 1) {"
@@ -336,7 +326,7 @@ static void
         # Unparse functions.
         for columnName, column in sorted(table.columns.iteritems()):
             type = column.type
-            if (type.n_min != 1 or type.n_max != 1) and not type.is_optional_pointer():
+            if type.is_smap() or (type.n_min != 1 or type.n_max != 1) and not type.is_optional_pointer():
                 print '''
 static void
 %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row_)
@@ -344,15 +334,19 @@ static void
     struct %(s)s *row = %(s)s_cast(row_);
 
     assert(inited);''' % {'s': structName, 'c': columnName}
-                if type.value:
-                    keyVar = "row->key_%s" % columnName
-                    valueVar = "row->value_%s" % columnName
+
+                if type.is_smap():
+                    print "    smap_destroy(&row->%s);" % columnName
                 else:
-                    keyVar = "row->%s" % columnName
-                    valueVar = None
-                print "    free(%s);" % keyVar
-                if valueVar:
-                    print "    free(%s);" % valueVar
+                    if type.value:
+                        keyVar = "row->key_%s" % columnName
+                        valueVar = "row->value_%s" % columnName
+                    else:
+                        keyVar = "row->%s" % columnName
+                        valueVar = None
+                    print "    free(%s);" % keyVar
+                    if valueVar:
+                        print "    free(%s);" % valueVar
                 print '}'
             else:
                 print '''
@@ -362,6 +356,25 @@ static void
     /* Nothing to do. */
 }''' % {'s': structName, 'c': columnName}
 
+        # Generic Row Initialization function.
+        print """
+static void
+%(s)s_init__(struct ovsdb_idl_row *row)
+{
+    %(s)s_init(%(s)s_cast(row));
+}""" % {'s': structName}
+
+        # Row Initialization function.
+        print """
+void
+%(s)s_init(struct %(s)s *row)
+{
+    memset(row, 0, sizeof *row); """ % {'s': structName}
+        for columnName, column in sorted(table.columns.iteritems()):
+            if column.type.is_smap():
+                print "    smap_init(&row->%s);" % columnName
+        print "}"
+
         # First, next functions.
         print '''
 const struct %(s)s *
@@ -448,6 +461,44 @@ const struct ovsdb_datum *
         # Set functions.
         for columnName, column in sorted(table.columns.iteritems()):
             type = column.type
+
+            if type.is_smap():
+                print """
+void
+%(s)s_set_%(c)s(const struct %(s)s *row, const struct smap *smap)
+{
+    struct ovsdb_datum datum;
+
+    assert(inited);
+    if (smap) {
+        struct smap_node *node;
+        size_t i;
+
+        datum.n = smap_count(smap);
+        datum.keys = xmalloc(datum.n * sizeof *datum.keys);
+        datum.values = xmalloc(datum.n * sizeof *datum.values);
+
+        i = 0;
+        SMAP_FOR_EACH (node, smap) {
+            datum.keys[i].string = xstrdup(node->key);
+            datum.values[i].string = xstrdup(node->value);
+            i++;
+        }
+        ovsdb_datum_sort_unique(&datum, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
+    } else {
+        ovsdb_datum_init_empty(&datum);
+    }
+    ovsdb_idl_txn_write(&row->header_,
+                        &%(s)s_columns[%(S)s_COL_%(C)s],
+                        &datum);
+}
+""" % {'s': structName,
+       'S': structName.upper(),
+       'c': columnName,
+       'C': columnName.upper()}
+                continue
+
+
             print '\nvoid'
             members = cMembers(prefix, columnName, column, True)
             keyVar = members[0]['name']
@@ -515,25 +566,6 @@ const struct ovsdb_datum *
                    'C': columnName.upper()}
             print "}"
 
-        # String Map Helpers.
-        for columnName, column in sorted(table.columns.iteritems()):
-            if not is_string_map(column):
-                continue
-
-            print """
-const char * %(s)s_get_%(c)s_value(const struct %(s)s *row, const char *search_key, const char *default_value)
-{
-    char **keys = row->key_%(c)s;
-    char **values = row->value_%(c)s;
-    size_t n_keys = row->n_%(c)s;
-    char ** result_key;
-
-    assert(inited);
-    result_key = bsearch(&search_key, keys, n_keys, sizeof *keys,
-                         bsearch_strcmp);
-    return result_key ? values[result_key - keys] : default_value;
-}""" % {'s': structName, 'c': columnName}
-
         # Table columns.
         print "\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (
             structName, structName.upper())
@@ -566,7 +598,7 @@ static void\n%s_columns_init(void)
         print "    {\"%s\", %s," % (tableName, is_root)
         print "     %s_columns, ARRAY_SIZE(%s_columns)," % (
             structName, structName)
-        print "     sizeof(struct %s)}," % structName
+        print "     sizeof(struct %s), %s_init__}," % (structName, structName)
     print "};"
 
     # IDL class.
index dfe9208..1f5be03 100644 (file)
@@ -26,7 +26,7 @@ vSwitch database (OVSDB).  It supports JSON-RPC client connections
 over active or passive TCP/IP or Unix domain sockets.
 .PP
 The OVSDB file may be specified on the command line as \fIdatabase\fR.
-The default is \fB@sysconfdir@/openvswitch/conf.db\fR.  The database
+The default is \fB@DBDIR@/conf.db\fR.  The database
 file must already have been created and initialized using, for
 example, \fBovsdb\-tool create\fR.
 .
@@ -123,6 +123,7 @@ This command might be useful for debugging issues with database
 clients.
 .
 .so lib/vlog-unixctl.man
+.so lib/memory-unixctl.man
 .so lib/coverage-unixctl.man
 .so lib/stress-unixctl.man
 .SH "SEE ALSO"
index d8363a2..1bf10d9 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@
 #include "jsonrpc-server.h"
 #include "leak-checker.h"
 #include "list.h"
+#include "memory.h"
 #include "ovsdb.h"
 #include "ovsdb-data.h"
 #include "ovsdb-types.h"
@@ -39,6 +40,7 @@
 #include "poll-loop.h"
 #include "process.h"
 #include "row.h"
+#include "simap.h"
 #include "stream-ssl.h"
 #include "stream.h"
 #include "stress.h"
@@ -135,6 +137,13 @@ main(int argc, char *argv[])
 
     daemonize_complete();
 
+    if (!run_command) {
+        /* ovsdb-server is usually a long-running process, in which case it
+         * makes plenty of sense to log the version, but --run makes
+         * ovsdb-server more like a command-line tool, so skip it.  */
+        VLOG_INFO("%s (Open vSwitch) %s", program_name, VERSION);
+    }
+
     unixctl_command_register("exit", "", 0, 0, ovsdb_server_exit, &exiting);
     unixctl_command_register("ovsdb-server/compact", "", 0, 0,
                              ovsdb_server_compact, file);
@@ -143,6 +152,17 @@ main(int argc, char *argv[])
 
     exiting = false;
     while (!exiting) {
+        memory_run();
+        if (memory_should_report()) {
+            struct simap usage;
+
+            simap_init(&usage);
+            ovsdb_jsonrpc_server_get_memory_usage(jsonrpc, &usage);
+            ovsdb_get_memory_usage(db, &usage);
+            memory_report(&usage);
+            simap_destroy(&usage);
+        }
+
         reconfigure_from_db(jsonrpc, db, &remotes);
         ovsdb_jsonrpc_server_run(jsonrpc);
         unixctl_server_run(unixctl);
@@ -157,6 +177,7 @@ main(int argc, char *argv[])
             update_remote_status(jsonrpc, &remotes, db);
         }
 
+        memory_wait();
         ovsdb_jsonrpc_server_wait(jsonrpc);
         unixctl_server_wait(unixctl);
         ovsdb_trigger_wait(db, time_msec());
@@ -329,8 +350,8 @@ read_map_string_column(const struct ovsdb_row *row, const char *column_name,
     union ovsdb_atom *atom_key = NULL, *atom_value = NULL;
     size_t i;
 
-    datum = get_datum((struct ovsdb_row *) row, column_name, OVSDB_TYPE_STRING,
-                      OVSDB_TYPE_STRING, UINT_MAX);
+    datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name,
+                      OVSDB_TYPE_STRING, OVSDB_TYPE_STRING, UINT_MAX);
 
     if (!datum) {
         return NULL;
@@ -353,8 +374,8 @@ read_column(const struct ovsdb_row *row, const char *column_name,
 {
     const struct ovsdb_datum *datum;
 
-    datum = get_datum((struct ovsdb_row *) row, column_name, type, OVSDB_TYPE_VOID,
-                      1);
+    datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name, type,
+                      OVSDB_TYPE_VOID, 1);
     return datum && datum->n ? datum->keys : NULL;
 }
 
@@ -787,7 +808,7 @@ parse_options(int argc, char *argv[], char **file_namep,
 
     switch (argc) {
     case 0:
-        *file_namep = xasprintf("%s/openvswitch/conf.db", ovs_sysconfdir());
+        *file_namep = xasprintf("%s/conf.db", ovs_dbdir());
         break;
 
     case 1:
index c440e0d..9d32844 100644 (file)
@@ -148,7 +148,7 @@ record.
 .SS "Other Options"
 .so lib/common.man
 .SH "FILES"
-The default \fIdb\fR is \fB@sysconfdir@/openvswitch/conf.db\fR.  The
+The default \fIdb\fR is \fB@DBDIR@/conf.db\fR.  The
 default \fIschema\fR is \fB@pkgdatadir@/vswitch.ovsschema\fR.  The
 \fBhelp\fR command also displays these defaults.
 .SH "SEE ALSO"
index e83b49a..6b75f49 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -140,7 +140,7 @@ default_db(void)
 {
     static char *db;
     if (!db) {
-        db = xasprintf("%s/conf.db", ovs_sysconfdir());
+        db = xasprintf("%s/conf.db", ovs_dbdir());
     }
     return db;
 }
@@ -207,29 +207,36 @@ do_create(int argc, char *argv[])
 }
 
 static void
-compact_or_convert(const char *src_name, const char *dst_name,
+compact_or_convert(const char *src_name_, const char *dst_name_,
                    const struct ovsdb_schema *new_schema,
                    const char *comment)
 {
+    char *src_name, *dst_name;
     struct lockfile *src_lock;
     struct lockfile *dst_lock;
-    bool in_place = dst_name == NULL;
+    bool in_place = dst_name_ == NULL;
     struct ovsdb *db;
     int retval;
 
+    /* Dereference symlinks for source and destination names.  In the in-place
+     * case this ensures that, if the source name is a symlink, we replace its
+     * target instead of replacing the symlink by a regular file.  In the
+     * non-in-place, this has the same effect for the destination name. */
+    src_name = follow_symlinks(src_name_);
+    dst_name = (in_place
+                ? xasprintf("%s.tmp", src_name)
+                : follow_symlinks(dst_name_));
+
     /* Lock the source, if we will be replacing it. */
     if (in_place) {
-        retval = lockfile_lock(src_name, 0, &src_lock);
+        retval = lockfile_lock(src_name, &src_lock);
         if (retval) {
             ovs_fatal(retval, "%s: failed to lock lockfile", src_name);
         }
     }
 
     /* Get (temporary) destination and lock it. */
-    if (in_place) {
-        dst_name = xasprintf("%s.tmp", src_name);
-    }
-    retval = lockfile_lock(dst_name, 0, &dst_lock);
+    retval = lockfile_lock(dst_name, &dst_lock);
     if (retval) {
         ovs_fatal(retval, "%s: failed to lock lockfile", dst_name);
     }
@@ -253,9 +260,8 @@ compact_or_convert(const char *src_name, const char *dst_name,
 
     lockfile_unlock(dst_lock);
 
-    if (in_place) {
-        free((char *) dst_name);
-    }
+    free(src_name);
+    free(dst_name);
 }
 
 static void
@@ -513,7 +519,7 @@ do_show_log(int argc, char *argv[])
                 time_t t = json_integer(date);
                 char s[128];
 
-                strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S", localtime(&t));
+                strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S", gmtime(&t));
                 printf(" %s", s);
             }
 
index fb020c9..6b53f4a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 #include "ovsdb-error.h"
 #include "ovsdb-parser.h"
 #include "ovsdb-types.h"
+#include "simap.h"
 #include "table.h"
 #include "transaction.h"
 
@@ -384,6 +385,25 @@ ovsdb_destroy(struct ovsdb *db)
     }
 }
 
+/* Adds some memory usage statistics for 'db' into 'usage', for use with
+ * memory_report(). */
+void
+ovsdb_get_memory_usage(const struct ovsdb *db, struct simap *usage)
+{
+    const struct shash_node *node;
+    unsigned int cells = 0;
+
+    SHASH_FOR_EACH (node, &db->tables) {
+        const struct ovsdb_table *table = node->data;
+        unsigned int n_columns = shash_count(&table->schema->columns);
+        unsigned int n_rows = hmap_count(&table->rows);
+
+        cells += n_rows * n_columns;
+    }
+
+    simap_increase(usage, "cells", cells);
+}
+
 struct ovsdb_table *
 ovsdb_get_table(const struct ovsdb *db, const char *name)
 {
index 0d15ef2..6e4ff79 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ struct json;
 struct ovsdb_log;
 struct ovsdb_session;
 struct ovsdb_txn;
+struct simap;
 struct uuid;
 
 /* Database schema. */
@@ -66,6 +67,8 @@ struct ovsdb {
 struct ovsdb *ovsdb_create(struct ovsdb_schema *);
 void ovsdb_destroy(struct ovsdb *);
 
+void ovsdb_get_memory_usage(const struct ovsdb *, struct simap *usage);
+
 struct ovsdb_error *ovsdb_from_json(const struct json *, struct ovsdb **)
     WARN_UNUSED_RESULT;
 struct json *ovsdb_to_json(const struct ovsdb *);
index 4754c67..9520ae7 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
 # Copyright (c) 2010 Citrix Systems, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
index 8bc5757..0beafe7 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
 # Copyright (c) 2010 Citrix Systems, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
index e5a29a9..caf6533 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
 # Copyright (c) 2010 Citrix Systems, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
index 23b3e66..c55a881 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
 # Copyright (c) 2010 Citrix Systems, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
index 774bed2..d1b0692 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
 # Copyright (c) 2010 Citrix Systems, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
index 52eda0a..7f6eccd 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* 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.
index f5cfe2e..5cc9b11 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index e25f857..450c327 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ allocate_row(const struct ovsdb_table *table)
                        + sizeof(struct ovsdb_datum) * n_fields
                        + sizeof(struct hmap_node) * n_indexes);
     struct ovsdb_row *row = xmalloc(row_size);
-    row->table = (struct ovsdb_table *) table;
+    row->table = CONST_CAST(struct ovsdb_table *, table);
     row->txn_row = NULL;
     list_init(&row->src_refs);
     list_init(&row->dst_refs);
@@ -347,7 +347,7 @@ ovsdb_row_hash_destroy(struct ovsdb_row_hash *rh, bool destroy_rows)
     HMAP_FOR_EACH_SAFE (node, next, hmap_node, &rh->rows) {
         hmap_remove(&rh->rows, &node->hmap_node);
         if (destroy_rows) {
-            ovsdb_row_destroy((struct ovsdb_row *) node->row);
+            ovsdb_row_destroy(CONST_CAST(struct ovsdb_row *, node->row));
         }
         free(node);
     }
index 306a56d..45c7fb3 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index e3ba149..7cd4263 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 Nicira Networks
+/* Copyright (c) 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.
index 17e4222..e073850 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, 2012 Nicira Networks
+/* Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index e350fc3..19f4d31 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index a2f2aa8..fab9c5b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 08a8bc1..cc890ad 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -439,8 +439,8 @@ static void
 add_weak_ref(struct ovsdb_txn *txn,
              const struct ovsdb_row *src_, const struct ovsdb_row *dst_)
 {
-    struct ovsdb_row *src = (struct ovsdb_row *) src_;
-    struct ovsdb_row *dst = (struct ovsdb_row *) dst_;
+    struct ovsdb_row *src = CONST_CAST(struct ovsdb_row *, src_);
+    struct ovsdb_row *dst = CONST_CAST(struct ovsdb_row *, dst_);
     struct ovsdb_weak_ref *weak;
 
     if (src == dst) {
@@ -864,7 +864,7 @@ ovsdb_txn_row_create(struct ovsdb_txn *txn, struct ovsdb_table *table,
                      const struct ovsdb_row *old_, struct ovsdb_row *new)
 {
     const struct ovsdb_row *row = old_ ? old_ : new;
-    struct ovsdb_row *old = (struct ovsdb_row *) old_;
+    struct ovsdb_row *old = CONST_CAST(struct ovsdb_row *, old_);
     size_t n_columns = shash_count(&table->schema->columns);
     struct ovsdb_txn_table *txn_table;
     struct ovsdb_txn_row *txn_row;
@@ -895,7 +895,7 @@ ovsdb_txn_row_create(struct ovsdb_txn *txn, struct ovsdb_table *table,
 struct ovsdb_row *
 ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_)
 {
-    struct ovsdb_row *ro_row = (struct ovsdb_row *) ro_row_;
+    struct ovsdb_row *ro_row = CONST_CAST(struct ovsdb_row *, ro_row_);
 
     if (ro_row->txn_row) {
         assert(ro_row == ro_row->txn_row->new);
@@ -931,7 +931,7 @@ ovsdb_txn_row_insert(struct ovsdb_txn *txn, struct ovsdb_row *row)
 void
 ovsdb_txn_row_delete(struct ovsdb_txn *txn, const struct ovsdb_row *row_)
 {
-    struct ovsdb_row *row = (struct ovsdb_row *) row_;
+    struct ovsdb_row *row = CONST_CAST(struct ovsdb_row *, row_);
     struct ovsdb_table *table = row->table;
     struct ovsdb_txn_row *txn_row = row->txn_row;
 
index 1d03ebd..edc9118 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* 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.
index 1322a2f..6ae1f51 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 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.
index 78265e5..1d47f3f 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2011 Nicira Networks
+/* Copyright (c) 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.
index 54ef56f..719da01 100644 (file)
@@ -33,7 +33,7 @@ else
 SSH_KEY_OPTION ?= -i $(SSH_KEY)
 endif
 
-SSH_OPTIONS ?= $(SSH_KEY_OPTION) -l $(SLICE)
+SSH_OPTIONS ?= $(SSH_KEY_OPTION) -l $(SLICE) -t
 SSH = ssh $(SSH_OPTIONS)
 
 ALL_NODE_IDS=$(shell (cut -d- -f1 < links; cut -d- -f2 < links) | sort -u)
@@ -72,6 +72,7 @@ cache/host.%:
        @host $(HOST_$*) | sed -n 's/^.*has address *//p' > $@
 
 cache/db.%:
+<<<<<<< HEAD
        @echo "Starting db server on $(call display,$*)"
        @$(SSH) $(HOST_$*) sudo start_ovsdb-server > $@ \
         || { rm $@; exit 1; }
index 59db000..c0f0db6 100644 (file)
@@ -1,10 +1,9 @@
-run_python = PYTHONPATH=$(top_srcdir)/python:$$PYTHON_PATH $(PYTHON)
-
 ovstest_pyfiles = \
        python/ovstest/__init__.py \
        python/ovstest/args.py \
        python/ovstest/rpcserver.py \
        python/ovstest/tcp.py \
+       python/ovstest/tests.py \
        python/ovstest/udp.py \
        python/ovstest/util.py \
        python/ovstest/vswitch.py
@@ -29,7 +28,9 @@ ovs_pyfiles = \
        python/ovs/socket_util.py \
        python/ovs/stream.py \
        python/ovs/timeval.py \
-       python/ovs/unixctl.py \
+       python/ovs/unixctl/__init__.py \
+       python/ovs/unixctl/client.py \
+       python/ovs/unixctl/server.py \
        python/ovs/util.py \
        python/ovs/version.py \
        python/ovs/vlog.py
@@ -42,11 +43,15 @@ if HAVE_PYTHON
 nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles)
 ovs-install-data-local:
        $(MKDIR_P) python/ovs
-       (echo "import os" && \
-        echo 'PKGDATADIR = os.environ.get("OVS_PKGDATADIR", """$(pkgdatadir)""")' && \
-        echo 'RUNDIR = os.environ.get("OVS_RUNDIR", """@RUNDIR@""")' && \
-        echo 'LOGDIR = os.environ.get("OVS_LOGDIR", """@LOGDIR@""")' && \
-        echo 'BINDIR = os.environ.get("OVS_BINDIR", """$(bindir)""")') \
+       sed \
+               -e '/^##/d' \
+                -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \
+                -e 's,[@]RUNDIR[@],$(RUNDIR),g' \
+                -e 's,[@]LOGDIR[@],$(LOGDIR),g' \
+                -e 's,[@]bindir[@],$(bindir),g' \
+                -e 's,[@]sysconfdir[@],$(sysconfdir),g' \
+                -e 's,[@]DBDIR[@],$(DBDIR),g' \
+               < $(srcdir)/python/ovs/dirs.py.template \
                > python/ovs/dirs.py.tmp
        $(MKDIR_P) $(DESTDIR)$(pkgdatadir)/python/ovs
        $(INSTALL_DATA) python/ovs/dirs.py.tmp $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py
@@ -66,3 +71,17 @@ $(srcdir)/python/ovs/version.py: config.status
        $(ro_shell) > $(@F).tmp
        echo 'VERSION = "$(VERSION)"' >> $(@F).tmp
        if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi
+
+ALL_LOCAL += $(srcdir)/python/ovs/dirs.py
+$(srcdir)/python/ovs/dirs.py: python/ovs/dirs.py.template
+       sed \
+               -e '/^##/d' \
+                -e 's,[@]pkgdatadir[@],/usr/local/share/openvswitch,g' \
+                -e 's,[@]RUNDIR[@],/var/run,g' \
+                -e 's,[@]LOGDIR[@],/usr/local/var/log,g' \
+                -e 's,[@]bindir[@],/usr/local/bin,g' \
+                -e 's,[@]sysconfdir[@],/usr/local/etc,g' \
+                -e 's,[@]DBDIR[@],/usr/local/etc/openvswitch,g' \
+               < $? > $@.tmp
+       mv $@.tmp $@
+EXTRA_DIST += python/ovs/dirs.py python/ovs/dirs.py.template
index 0d119f2..650d250 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 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.
index 3cc4619..55e7a73 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 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.
index c3fd302..d9217e4 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 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.
index 36a7bab..e4b98e8 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
index cb7fc87..2556bec 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 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.
index 12b997d..1b5a771 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 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.
index fc9fc0a..5865acd 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -450,6 +450,11 @@ class Type(object):
     def is_map(self):
         return self.value is not None
 
+    def is_smap(self):
+        return (self.is_map()
+                and self.key.type == StringType
+                and self.value.type == StringType)
+
     def is_optional_pointer(self):
         return (self.is_optional() and not self.value
                 and (self.key.type == StringType or self.key.ref_table_name))
index 5b006cc..b5e68a0 100644 (file)
@@ -1,8 +1,13 @@
-# These are the default directories.  They will be replaced by the
-# configured directories at install time.
-
 import os
-PKGDATADIR = os.environ.get("OVS_PKGDATADIR", "/usr/local/share/openvswitch")
-RUNDIR = os.environ.get("OVS_RUNDIR", "/var/run")
-LOGDIR = os.environ.get("OVS_LOGDIR", "/usr/local/var/log")
-BINDIR = os.environ.get("OVS_BINDIR", "/usr/local/bin")
+PKGDATADIR = os.environ.get("OVS_PKGDATADIR", """/usr/local/share/openvswitch""")
+RUNDIR = os.environ.get("OVS_RUNDIR", """/var/run""")
+LOGDIR = os.environ.get("OVS_LOGDIR", """/usr/local/var/log""")
+BINDIR = os.environ.get("OVS_BINDIR", """/usr/local/bin""")
+
+DBDIR = os.environ.get("OVS_DBDIR")
+if not DBDIR:
+    sysconfdir = os.environ.get("OVS_SYSCONFDIR")
+    if sysconfdir:
+        DBDIR = "%s/openvswitch" % sysconfdir
+    else:
+        DBDIR = """/usr/local/etc/openvswitch"""
diff --git a/python/ovs/dirs.py.template b/python/ovs/dirs.py.template
new file mode 100644 (file)
index 0000000..370c69f
--- /dev/null
@@ -0,0 +1,17 @@
+## The @variables@ in this file are replaced by default directories for
+## use in python/ovs/dirs.py in the source directory and replaced by the
+## configured directories for use in the installed python/ovs/dirs.py.
+##
+import os
+PKGDATADIR = os.environ.get("OVS_PKGDATADIR", """@pkgdatadir@""")
+RUNDIR = os.environ.get("OVS_RUNDIR", """@RUNDIR@""")
+LOGDIR = os.environ.get("OVS_LOGDIR", """@LOGDIR@""")
+BINDIR = os.environ.get("OVS_BINDIR", """@bindir@""")
+
+DBDIR = os.environ.get("OVS_DBDIR")
+if not DBDIR:
+    sysconfdir = os.environ.get("OVS_SYSCONFDIR")
+    if sysconfdir:
+        DBDIR = "%s/openvswitch" % sysconfdir
+    else:
+        DBDIR = """@DBDIR@"""
index dd989a0..e6fe783 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 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.
index fa5fd15..d329ee4 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
index b72af77..a054401 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -210,7 +210,7 @@ class Connection(object):
         if not self.status:
             self.stream.run_wait(poller)
             if len(self.output):
-                self.stream.send_wait()
+                self.stream.send_wait(poller)
 
     def get_status(self):
         return self.status
index 31c9bcc..56fdad0 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 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.
index 8d52d32..e459c58 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010 Nicira Networks
+# Copyright (c) 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.
index 4e6d440..d756131 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 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.
index f1da930..4599573 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
index d00d398..a8e8d92 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2012 Nicira Networks
+# Copyright (c) 2010, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
index e2adaa5..9c10612 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ vlog = ovs.vlog.Vlog("stream")
 
 def stream_or_pstream_needs_probes(name):
     """ 1 if the stream or pstream specified by 'name' needs periodic probes to
-    verify connectivty.  For [p]streams which need probes, it can take a long
+    verify connectivity.  For [p]streams which need probes, it can take a long
     time to notice the connection was dropped.  Returns 0 if probes aren't
     needed, and -1 if 'name' is invalid"""
 
@@ -113,7 +113,7 @@ class Stream(object):
                     break
                 stream.run()
                 poller = ovs.poller.Poller()
-                stream.run_wait()
+                stream.run_wait(poller)
                 stream.connect_wait(poller)
                 poller.block()
             assert error != errno.EINPROGRESS
index 7116079..ba0e54e 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010 Nicira Networks
+# 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.
diff --git a/python/ovs/unixctl/__init__.py b/python/ovs/unixctl/__init__.py
new file mode 100644 (file)
index 0000000..715f2db
--- /dev/null
@@ -0,0 +1,83 @@
+# Copyright (c) 2011, 2012 Nicira, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import types
+
+import ovs.util
+
+commands = {}
+strtypes = types.StringTypes
+
+
+class _UnixctlCommand(object):
+    def __init__(self, usage, min_args, max_args, callback, aux):
+        self.usage = usage
+        self.min_args = min_args
+        self.max_args = max_args
+        self.callback = callback
+        self.aux = aux
+
+
+def _unixctl_help(conn, unused_argv, unused_aux):
+    reply = "The available commands are:\n"
+    command_names = sorted(commands.keys())
+    for name in command_names:
+        reply += "  "
+        usage = commands[name].usage
+        if usage:
+            reply += "%-23s %s" % (name, usage)
+        else:
+            reply += name
+        reply += "\n"
+    conn.reply(reply)
+
+
+def command_register(name, usage, min_args, max_args, callback, aux):
+    """ Registers a command with the given 'name' to be exposed by the
+    UnixctlServer. 'usage' describes the arguments to the command; it is used
+    only for presentation to the user in "help" output.
+
+    'callback' is called when the command is received.  It is passed a
+    UnixctlConnection object, the list of arguments as unicode strings, and
+    'aux'.  Normally 'callback' should reply by calling
+    UnixctlConnection.reply() or UnixctlConnection.reply_error() before it
+    returns, but if the command cannot be handled immediately, then it can
+    defer the reply until later.  A given connection can only process a single
+    request at a time, so a reply must be made eventually to avoid blocking
+    that connection."""
+
+    assert isinstance(name, strtypes)
+    assert isinstance(usage, strtypes)
+    assert isinstance(min_args, int)
+    assert isinstance(max_args, int)
+    assert isinstance(callback, types.FunctionType)
+
+    if name not in commands:
+        commands[name] = _UnixctlCommand(usage, min_args, max_args, callback,
+                                         aux)
+
+def socket_name_from_target(target):
+    assert isinstance(target, strtypes)
+
+    if target.startswith("/"):
+        return 0, target
+
+    pidfile_name = "%s/%s.pid" % (ovs.dirs.RUNDIR, target)
+    pid = ovs.daemon.read_pidfile(pidfile_name)
+    if pid < 0:
+        return -pid, "cannot read pidfile \"%s\"" % pidfile_name
+
+    return 0, "%s/%s.%d.ctl" % (ovs.dirs.RUNDIR, target, pid)
+
+command_register("help", "", 0, 0, _unixctl_help, None)
diff --git a/python/ovs/unixctl/client.py b/python/ovs/unixctl/client.py
new file mode 100644 (file)
index 0000000..2176009
--- /dev/null
@@ -0,0 +1,70 @@
+# Copyright (c) 2011, 2012 Nicira, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+import errno
+import os
+import types
+
+import ovs.jsonrpc
+import ovs.stream
+import ovs.util
+
+
+vlog = ovs.vlog.Vlog("unixctl_client")
+strtypes = types.StringTypes
+
+
+class UnixctlClient(object):
+    def __init__(self, conn):
+        assert isinstance(conn, ovs.jsonrpc.Connection)
+        self._conn = conn
+
+    def transact(self, command, argv):
+        assert isinstance(command, strtypes)
+        assert isinstance(argv, list)
+        for arg in argv:
+            assert isinstance(arg, strtypes)
+
+        request = ovs.jsonrpc.Message.create_request(command, argv)
+        error, reply = self._conn.transact_block(request)
+
+        if error:
+            vlog.warn("error communicating with %s: %s"
+                      % (self._conn.name, os.strerror(error)))
+            return error, None, None
+
+        if reply.error is not None:
+            return 0, str(reply.error), None
+        else:
+            assert reply.result is not None
+            return 0, None, str(reply.result)
+
+    def close(self):
+        self._conn.close()
+        self.conn = None
+
+    @staticmethod
+    def create(path):
+        assert isinstance(path, str)
+
+        unix = "unix:%s" % ovs.util.abs_file_name(ovs.dirs.RUNDIR, path)
+        error, stream = ovs.stream.Stream.open_block(
+            ovs.stream.Stream.open(unix))
+
+        if error:
+            vlog.warn("failed to connect to %s" % path)
+            return error, None
+
+        return 0, UnixctlClient(ovs.jsonrpc.Connection(stream))
similarity index 74%
rename from python/ovs/unixctl.py
rename to python/ovs/unixctl/server.py
index 396d1be..18e1cf2 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 Nicira Networks
+# Copyright (c) 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -17,89 +17,19 @@ import errno
 import os
 import types
 
-import ovs.daemon
 import ovs.dirs
 import ovs.jsonrpc
 import ovs.stream
+import ovs.unixctl
 import ovs.util
 import ovs.version
 import ovs.vlog
 
 Message = ovs.jsonrpc.Message
-vlog = ovs.vlog.Vlog("unixctl")
-commands = {}
+vlog = ovs.vlog.Vlog("unixctl_server")
 strtypes = types.StringTypes
 
 
-class _UnixctlCommand(object):
-    def __init__(self, usage, min_args, max_args, callback, aux):
-        self.usage = usage
-        self.min_args = min_args
-        self.max_args = max_args
-        self.callback = callback
-        self.aux = aux
-
-
-def _unixctl_help(conn, unused_argv, unused_aux):
-    assert isinstance(conn, UnixctlConnection)
-    reply = "The available commands are:\n"
-    command_names = sorted(commands.keys())
-    for name in command_names:
-        reply += "  "
-        usage = commands[name].usage
-        if usage:
-            reply += "%-23s %s" % (name, usage)
-        else:
-            reply += name
-        reply += "\n"
-    conn.reply(reply)
-
-
-def _unixctl_version(conn, unused_argv, version):
-    assert isinstance(conn, UnixctlConnection)
-    version = "%s (Open vSwitch) %s" % (ovs.util.PROGRAM_NAME, version)
-    conn.reply(version)
-
-
-def command_register(name, usage, min_args, max_args, callback, aux):
-    """ Registers a command with the given 'name' to be exposed by the
-    UnixctlServer. 'usage' describes the arguments to the command; it is used
-    only for presentation to the user in "help" output.
-
-    'callback' is called when the command is received.  It is passed a
-    UnixctlConnection object, the list of arguments as unicode strings, and
-    'aux'.  Normally 'callback' should reply by calling
-    UnixctlConnection.reply() or UnixctlConnection.reply_error() before it
-    returns, but if the command cannot be handled immediately, then it can
-    defer the reply until later.  A given connection can only process a single
-    request at a time, so a reply must be made eventually to avoid blocking
-    that connection."""
-
-    assert isinstance(name, strtypes)
-    assert isinstance(usage, strtypes)
-    assert isinstance(min_args, int)
-    assert isinstance(max_args, int)
-    assert isinstance(callback, types.FunctionType)
-
-    if name not in commands:
-        commands[name] = _UnixctlCommand(usage, min_args, max_args, callback,
-                                         aux)
-
-
-def socket_name_from_target(target):
-    assert isinstance(target, strtypes)
-
-    if target.startswith("/"):
-        return 0, target
-
-    pidfile_name = "%s/%s.pid" % (ovs.dirs.RUNDIR, target)
-    pid = ovs.daemon.read_pidfile(pidfile_name)
-    if pid < 0:
-        return -pid, "cannot read pidfile \"%s\"" % pidfile_name
-
-    return 0, "%s/%s.%d.ctl" % (ovs.dirs.RUNDIR, target, pid)
-
-
 class UnixctlConnection(object):
     def __init__(self, rpc):
         assert isinstance(rpc, ovs.jsonrpc.Connection)
@@ -177,7 +107,7 @@ class UnixctlConnection(object):
         error = None
         params = request.params
         method = request.method
-        command = commands.get(method)
+        command = ovs.unixctl.commands.get(method)
         if command is None:
             error = '"%s" is not a valid command' % method
         elif len(params) < command.min_args:
@@ -200,6 +130,11 @@ class UnixctlConnection(object):
             self.reply_error(error)
 
 
+def _unixctl_version(conn, unused_argv, version):
+    assert isinstance(conn, UnixctlConnection)
+    version = "%s (Open vSwitch) %s" % (ovs.util.PROGRAM_NAME, version)
+    conn.reply(version)
+
 class UnixctlServer(object):
     def __init__(self, listener):
         assert isinstance(listener, ovs.stream.PassiveStream)
@@ -262,8 +197,8 @@ class UnixctlServer(object):
                                % path)
             return error, None
 
-        command_register("help", "", 0, 0, _unixctl_help, None)
-        command_register("version", "", 0, 0, _unixctl_version, version)
+        ovs.unixctl.command_register("version", "", 0, 0, _unixctl_version,
+                                     version)
 
         return 0, UnixctlServer(listener)
 
index 66e429b..cb0574b 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
index ad53781..f7ace66 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (c) 2011 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 import datetime
 import logging
 import logging.handlers
+import re
 import socket
 import sys
 
 import ovs.dirs
+import ovs.unixctl
 import ovs.util
 
 FACILITIES = {"console": "info", "file": "info", "syslog": "info"}
@@ -41,6 +43,8 @@ class Vlog:
     __inited = False
     __msg_num = 0
     __mfl = {}  # Module -> facility -> level
+    __log_file = None
+    __file_handler = None
 
     def __init__(self, name):
         """Creates a new Vlog object representing a module called 'name'.  The
@@ -56,7 +60,7 @@ class Vlog:
         if not Vlog.__inited:
             return
 
-        now = datetime.datetime.now().strftime("%b %d %H:%M:%S")
+        now = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
         message = ("%s|%s|%s|%s|%s"
                    % (now, Vlog.__msg_num, self.name, level, message))
 
@@ -99,6 +103,7 @@ class Vlog:
 
         Vlog.__inited = True
         logging.raiseExceptions = False
+        Vlog.__log_file = log_file
         for f in FACILITIES:
             logger = logging.getLogger(f)
             logger.setLevel(logging.DEBUG)
@@ -110,11 +115,19 @@ class Vlog:
                     logger.addHandler(logging.handlers.SysLogHandler(
                         address="/dev/log",
                         facility=logging.handlers.SysLogHandler.LOG_DAEMON))
-                elif f == "file" and log_file:
-                    logger.addHandler(logging.FileHandler(log_file))
+                elif f == "file" and Vlog.__log_file:
+                    Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
+                    logger.addHandler(Vlog.__file_handler)
             except (IOError, socket.error):
                 logger.setLevel(logging.CRITICAL)
 
+        ovs.unixctl.command_register("vlog/reopen", "", 0, 0,
+                                     Vlog._unixctl_vlog_reopen, None)
+        ovs.unixctl.command_register("vlog/set", "spec", 1, sys.maxint,
+                                     Vlog._unixctl_vlog_set, None)
+        ovs.unixctl.command_register("vlog/list", "", 0, 0,
+                                     Vlog._unixctl_vlog_list, None)
+
     @staticmethod
     def set_level(module, facility, level):
         """ Sets the log level of the 'module'-'facility' tuple to 'level'.
@@ -149,6 +162,75 @@ class Vlog:
             for f in facilities:
                 Vlog.__mfl[m][f] = level
 
+    @staticmethod
+    def set_levels_from_string(s):
+        module = None
+        level = None
+        facility = None
+
+        for word in [w.lower() for w in re.split('[ :]', s)]:
+            if word == "any":
+                pass
+            elif word in FACILITIES:
+                if facility:
+                    return "cannot specify multiple facilities"
+                facility = word
+            elif word in LEVELS:
+                if level:
+                    return "cannot specify multiple levels"
+                level = word
+            elif word in Vlog.__mfl:
+                if module:
+                    return "cannot specify multiple modules"
+                module = word
+            else:
+                return "no facility, level, or module \"%s\"" % word
+
+        Vlog.set_level(module or "any", facility or "any", level or "any")
+
+    @staticmethod
+    def get_levels():
+        lines = ["                 console    syslog    file\n",
+                 "                 -------    ------    ------\n"]
+        lines.extend(sorted(["%-16s  %4s       %4s       %4s\n"
+                             % (m,
+                                Vlog.__mfl[m]["console"],
+                                Vlog.__mfl[m]["syslog"],
+                                Vlog.__mfl[m]["file"]) for m in Vlog.__mfl]))
+        return ''.join(lines)
+
+    @staticmethod
+    def reopen_log_file():
+        """Closes and then attempts to re-open the current log file.  (This is
+        useful just after log rotation, to ensure that the new log file starts
+        being used.)"""
+
+        if Vlog.__log_file:
+            logger = logging.getLogger("file")
+            logger.removeHandler(Vlog.__file_handler)
+            Vlog.__file_handler = logging.FileHandler(Vlog.__log_file)
+            logger.addHandler(Vlog.__file_handler)
+
+    @staticmethod
+    def _unixctl_vlog_reopen(conn, unused_argv, unused_aux):
+        if Vlog.__log_file:
+            Vlog.reopen_log_file()
+            conn.reply(None)
+        else:
+            conn.reply("Logging to file not configured")
+
+    @staticmethod
+    def _unixctl_vlog_set(conn, argv, unused_aux):
+        for arg in argv:
+            msg = Vlog.set_levels_from_string(arg)
+            if msg:
+                conn.reply(msg)
+                return
+        conn.reply(None)
+
+    @staticmethod
+    def _unixctl_vlog_list(conn, unused_argv, unused_aux):
+        conn.reply(Vlog.get_levels())
 
 def add_args(parser):
     """Adds vlog related options to 'parser', an ArgumentParser object.  The
@@ -160,7 +242,7 @@ def add_args(parser):
                        " is used if LOG_FILE is omitted.")
     group.add_argument("-v", "--verbose", nargs="*",
                        help="Sets logging levels, see ovs-vswitchd(8)."
-                       "  Defaults to ANY:ANY:dbg.")
+                       "  Defaults to dbg.")
 
 
 def handle_args(args):
@@ -178,23 +260,8 @@ def handle_args(args):
         args.verbose = ["any:any:dbg"]
 
     for verbose in args.verbose:
-        args = verbose.split(':')
-
-        if len(args) >= 3:
-            level = args[2]
-        else:
-            level = "dbg"
-
-        if len(args) >= 2:
-            facility = args[1]
-        else:
-            facility = "any"
-
-        if len(args) >= 1:
-            module = args[0]
-        else:
-            module = "any"
-
-        Vlog.set_level(module, facility, level)
+        msg = Vlog.set_levels_from_string(verbose)
+        if msg:
+            ovs.util.ovs_fatal(0, "processing \"%s\": %s" % (verbose, msg))
 
     Vlog.init(log_file)
index 3f8d0d9..e90db2a 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -78,6 +78,23 @@ def ip_optional_port(string, default_port, ip_callback):
                                          "must be colon-separated")
 
 
+def ip_optional_port_port(string, default_port1, default_port2, ip_callback):
+    """Convert a string into IP, Port1, Port2 tuple. If any of ports were
+     missing, then default ports will be used. The fourth argument is a
+     callback that verifies whether IP address is given in the expected
+     format."""
+    value = string.split(':')
+    if len(value) == 1:
+        return (ip_callback(value[0]), default_port1, default_port2)
+    elif len(value) == 2:
+        return (ip_callback(value[0]), port(value[1]), default_port2)
+    elif len(value) == 3:
+        return (ip_callback(value[0]), port(value[1]), port(value[2]))
+    else:
+        raise argparse.ArgumentTypeError("Expected IP address and at most "
+                                         "two colon-separated ports")
+
+
 def vlan_tag(string):
     """
     This function verifies whether given string is a correct VLAN tag.
@@ -154,6 +171,37 @@ def tunnel_types(string):
     return string.split(',')
 
 
+def l3_endpoint_client(string):
+    """
+    This function parses command line argument string in
+    remoteIP,localInnerIP[/mask][:ControlPort[:TestPort]],remoteInnerIP[:
+    ControlPort[:TestPort]] format.
+    """
+    try:
+        remote_ip, me, he = string.split(',')
+    except ValueError:
+        raise argparse.ArgumentTypeError("All 3 IP addresses must be comma "
+                                         "separated.")
+    r = (ip_address(remote_ip),
+         ip_optional_port_port(me, CONTROL_PORT, DATA_PORT, ip_optional_mask),
+         ip_optional_port_port(he, CONTROL_PORT, DATA_PORT, ip_address))
+    return r
+
+
+def l3_endpoint_server(string):
+    """
+    This function parses a command line argument string in
+    remoteIP,localInnerIP[/mask][:ControlPort] format.
+    """
+    try:
+        remote_ip, me = string.split(',')
+    except ValueError:
+        raise argparse.ArgumentTypeError("Both IP addresses must be comma "
+                                         "separated.")
+    return (ip_address(remote_ip),
+            ip_optional_port(me, CONTROL_PORT, ip_optional_mask))
+
+
 def ovs_initialize_args():
     """
     Initialize argument parsing for ovs-test utility.
@@ -197,3 +245,37 @@ def ovs_initialize_args():
                 'ovs-test server in the client mode by using 127.0.0.1 as '
                 'OuterIP.')
     return parser.parse_args()
+
+def l3_initialize_args():
+    """
+    Initialize argument parsing for ovs-l3ping utility.
+    """
+    parser = argparse.ArgumentParser(description='Test L3 tunnel '
+                        'connectivity between two Open vSwitch instances.')
+
+    parser.add_argument('-v', '--version', action='version',
+                version='ovs-l3ping (Open vSwitch) @VERSION@')
+
+    parser.add_argument("-b", "--bandwidth", action='store',
+                dest="targetBandwidth", default="1M", type=bandwidth,
+                help='Target bandwidth for UDP tests in bits/second. Use '
+                'postfix M or K to alter unit magnitude.')
+    parser.add_argument("-i", "--interval", action='store',
+                dest="testInterval", default=5, type=int,
+                help='Interval for how long to run each test in seconds.')
+
+    parser.add_argument("-t", "--tunnel-mode", action='store',
+                dest="tunnelMode", required=True,
+                help='Do L3 tests with this tunnel type.')
+
+    group = parser.add_mutually_exclusive_group(required=True)
+    group.add_argument("-s", "--server", action="store", dest="server",
+                metavar="TUNNELIP,SERVER",
+                type=l3_endpoint_server,
+                help='Run in server mode and wait for the client to '
+                'connect.')
+    group.add_argument('-c', "--client", action="store", dest="client",
+                metavar="TUNNELIP,CLIENT,SERVER",
+                type=l3_endpoint_client,
+                help='Run in client mode and connect to the server.')
+    return parser.parse_args()
index 0f680e1..5c92014 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -343,6 +343,12 @@ class TestArena(xmlrpc.XMLRPC):
         """
         return util.get_driver(iface)
 
+    def xmlrpc_get_interface_from_routing_decision(self, ip):
+        """
+        Returns driver version
+        """
+        return util.get_interface_from_routing_decision(ip)
+
 
 def start_rpc_server(port):
     """
index 8413343..d1304c3 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/python/ovstest/tests.py b/python/ovstest/tests.py
new file mode 100644 (file)
index 0000000..5d5a85f
--- /dev/null
@@ -0,0 +1,237 @@
+import math
+import time
+
+import ovstest.util as util
+
+DEFAULT_TEST_BRIDGE = "ovstestbr0"
+DEFAULT_TEST_PORT = "ovstestport0"
+DEFAULT_TEST_TUN = "ovstestport1"
+NO_HANDLE = -1
+
+
+def do_udp_tests(receiver, sender, tbwidth, duration, port_sizes):
+    """Schedule UDP tests between receiver and sender"""
+    server1 = util.rpc_client(receiver[0], receiver[1])
+    server2 = util.rpc_client(sender[0], sender[1])
+
+    udpformat = '{0:>15} {1:>15} {2:>15} {3:>15} {4:>15}'
+
+    print ("UDP test from %s:%u to %s:%u with target bandwidth %s" %
+                            (sender[0], sender[1], receiver[0], receiver[1],
+                             util.bandwidth_to_string(tbwidth)))
+    print udpformat.format("Datagram Size", "Snt Datagrams", "Rcv Datagrams",
+                            "Datagram Loss", "Bandwidth")
+
+    for size in port_sizes:
+        listen_handle = NO_HANDLE
+        send_handle = NO_HANDLE
+        try:
+            packetcnt = (tbwidth * duration) / size
+
+            listen_handle = server1.create_udp_listener(receiver[3])
+            if listen_handle == NO_HANDLE:
+                print ("Server could not open UDP listening socket on port"
+                        " %u. Try to restart the server.\n" % receiver[3])
+                return
+            send_handle = server2.create_udp_sender(
+                                            (util.ip_from_cidr(receiver[2]),
+                                             receiver[3]), packetcnt, size,
+                                             duration)
+
+            # Using sleep here because there is no other synchronization
+            # source that would notify us when all sent packets were received
+            time.sleep(duration + 1)
+
+            rcv_packets = server1.get_udp_listener_results(listen_handle)
+            snt_packets = server2.get_udp_sender_results(send_handle)
+
+            loss = math.ceil(((snt_packets - rcv_packets) * 10000.0) /
+                                                        snt_packets) / 100
+            bwidth = (rcv_packets * size) / duration
+
+            print udpformat.format(size, snt_packets, rcv_packets,
+                          '%.2f%%' % loss, util.bandwidth_to_string(bwidth))
+        finally:
+            if listen_handle != NO_HANDLE:
+                server1.close_udp_listener(listen_handle)
+            if send_handle != NO_HANDLE:
+                server2.close_udp_sender(send_handle)
+    print "\n"
+
+
+def do_tcp_tests(receiver, sender, duration):
+    """Schedule TCP tests between receiver and sender"""
+    server1 = util.rpc_client(receiver[0], receiver[1])
+    server2 = util.rpc_client(sender[0], sender[1])
+
+    tcpformat = '{0:>15} {1:>15} {2:>15}'
+    print "TCP test from %s:%u to %s:%u (full speed)" % (sender[0], sender[1],
+                                                    receiver[0], receiver[1])
+    print tcpformat.format("Snt Bytes", "Rcv Bytes", "Bandwidth")
+
+    listen_handle = NO_HANDLE
+    send_handle = NO_HANDLE
+    try:
+        listen_handle = server1.create_tcp_listener(receiver[3])
+        if listen_handle == NO_HANDLE:
+            print ("Server was unable to open TCP listening socket on port"
+                    " %u. Try to restart the server.\n" % receiver[3])
+            return
+        send_handle = server2.create_tcp_sender(util.ip_from_cidr(receiver[2]),
+                                                receiver[3], duration)
+
+        time.sleep(duration + 1)
+
+        rcv_bytes = long(server1.get_tcp_listener_results(listen_handle))
+        snt_bytes = long(server2.get_tcp_sender_results(send_handle))
+
+        bwidth = rcv_bytes / duration
+
+        print tcpformat.format(snt_bytes, rcv_bytes,
+                               util.bandwidth_to_string(bwidth))
+    finally:
+        if listen_handle != NO_HANDLE:
+            server1.close_tcp_listener(listen_handle)
+        if send_handle != NO_HANDLE:
+            server2.close_tcp_sender(send_handle)
+    print "\n"
+
+
+def do_l3_tests(node1, node2, bandwidth, duration, ps, type):
+    """
+    Do L3 tunneling tests. Each node is given as 4 tuple - physical
+    interface IP, control port, test IP and test port.
+    """
+    server1 = util.rpc_client(node1[0], node1[1])
+    server2 = util.rpc_client(node2[0], node2[1])
+    servers_with_bridges = []
+    try:
+        server1.create_bridge(DEFAULT_TEST_BRIDGE)
+        servers_with_bridges.append(server1)
+        server2.create_bridge(DEFAULT_TEST_BRIDGE)
+        servers_with_bridges.append(server2)
+
+        server1.interface_up(DEFAULT_TEST_BRIDGE)
+        server2.interface_up(DEFAULT_TEST_BRIDGE)
+
+        server1.interface_assign_ip(DEFAULT_TEST_BRIDGE, node1[2], None)
+        server2.interface_assign_ip(DEFAULT_TEST_BRIDGE, node2[2], None)
+
+        server1.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
+        server2.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
+
+        server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
+                              None, type)
+        server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
+                              None, type)
+        server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
+                              "remote_ip", node2[0])
+        server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
+                              "remote_ip", node1[0])
+
+        do_udp_tests(node1, node2, bandwidth, duration, ps)
+        do_udp_tests(node2, node1, bandwidth, duration, ps)
+        do_tcp_tests(node1, node2, duration)
+        do_tcp_tests(node2, node1, duration)
+
+    finally:
+        for server in servers_with_bridges:
+            server.del_bridge(DEFAULT_TEST_BRIDGE)
+
+
+
+def do_vlan_tests(node1, node2, bandwidth, duration, ps, tag):
+    """
+    Do VLAN tests between node1 and node2. Each node is given
+    as 4 tuple - physical interface IP, control port, test IP and
+    test port.
+    """
+    server1 = util.rpc_client(node1[0], node1[1])
+    server2 = util.rpc_client(node2[0], node2[1])
+
+    br_name1 = None
+    br_name2 = None
+
+    servers_with_test_ports = []
+
+    try:
+        interface_node1 = server1.get_interface(node1[0])
+        interface_node2 = server2.get_interface(node2[0])
+
+        if server1.is_ovs_bridge(interface_node1):
+            br_name1 = interface_node1
+        else:
+            br_name1 = DEFAULT_TEST_BRIDGE
+            server1.create_test_bridge(br_name1, interface_node1)
+
+        if server2.is_ovs_bridge(interface_node2):
+            br_name2 = interface_node2
+        else:
+            br_name2 = DEFAULT_TEST_BRIDGE
+            server2.create_test_bridge(br_name2, interface_node2)
+
+        server1.add_port_to_bridge(br_name1, DEFAULT_TEST_PORT)
+        servers_with_test_ports.append(server1)
+        server2.add_port_to_bridge(br_name2, DEFAULT_TEST_PORT)
+        servers_with_test_ports.append(server2)
+
+        server1.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
+        server2.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
+
+        server1.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
+                              "internal")
+        server2.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
+                              "internal")
+
+        server1.interface_assign_ip(DEFAULT_TEST_PORT, node1[2], None)
+        server2.interface_assign_ip(DEFAULT_TEST_PORT, node2[2], None)
+
+        server1.interface_up(DEFAULT_TEST_PORT)
+        server2.interface_up(DEFAULT_TEST_PORT)
+
+        do_udp_tests(node1, node2, bandwidth, duration, ps)
+        do_udp_tests(node2, node1, bandwidth, duration, ps)
+        do_tcp_tests(node1, node2, duration)
+        do_tcp_tests(node2, node1, duration)
+
+    finally:
+        for server in servers_with_test_ports:
+            server.del_port_from_bridge(DEFAULT_TEST_PORT)
+        if br_name1 == DEFAULT_TEST_BRIDGE:
+            server1.del_test_bridge(br_name1, interface_node1)
+        if br_name2 == DEFAULT_TEST_BRIDGE:
+            server2.del_test_bridge(br_name2, interface_node2)
+
+
+def do_direct_tests(node1, node2, bandwidth, duration, ps):
+    """
+    Do tests between outer IPs without involving Open vSwitch. Each
+    node is given as 4 tuple - physical interface IP, control port,
+    test IP and test port. Direct tests will use physical interface
+    IP as the test IP address.
+    """
+    n1 = (node1[0], node1[1], node1[0], node1[3])
+    n2 = (node2[0], node2[1], node2[0], node2[3])
+
+    do_udp_tests(n1, n2, bandwidth, duration, ps)
+    do_udp_tests(n2, n1, bandwidth, duration, ps)
+    do_tcp_tests(n1, n2, duration)
+    do_tcp_tests(n2, n1, duration)
+
+
+def configure_l3(conf, tunnel_mode):
+    """
+    This function creates a temporary test bridge and adds an L3 tunnel.
+    """
+    s = util.start_local_server(conf[1][1])
+    server = util.rpc_client("127.0.0.1", conf[1][1])
+    server.create_bridge(DEFAULT_TEST_BRIDGE)
+    server.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_PORT)
+    server.interface_up(DEFAULT_TEST_BRIDGE)
+    server.interface_assign_ip(DEFAULT_TEST_BRIDGE, conf[1][0],
+                               None)
+    server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type",
+                         None, tunnel_mode)
+    server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "options",
+                         "remote_ip", conf[0])
+    return s
index f34ae86..fa03e74 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
index 7bc7795..d61e4ba 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -19,10 +19,13 @@ import array
 import exceptions
 import fcntl
 import os
+import select
 import socket
 import struct
+import signal
 import subprocess
 import re
+import xmlrpclib
 
 
 def str_ip(ip_address):
@@ -147,3 +150,81 @@ def move_routes(iface1, iface2):
         for route in out.splitlines():
             args = ["ip", "route", "replace", "dev", iface2] + route.split()
             start_process(args)
+
+
+def get_interface_from_routing_decision(ip):
+    """
+    This function returns the interface through which the given ip address
+    is reachable.
+    """
+    args = ["ip", "route", "get", ip]
+    ret, out, _err = start_process(args)
+    if ret == 0:
+        iface = re.search(r'dev (\S+)', out)
+        if iface:
+            return iface.group(1)
+    return None
+
+
+def rpc_client(ip, port):
+    return xmlrpclib.Server("http://%s:%u/" % (ip, port), allow_none=True)
+
+
+def sigint_intercept():
+    """
+    Intercept SIGINT from child (the local ovs-test server process).
+    """
+    signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+
+def start_local_server(port):
+    """
+    This function spawns an ovs-test server that listens on specified port
+    and blocks till the spawned ovs-test server is ready to accept XML RPC
+    connections.
+    """
+    p = subprocess.Popen(["ovs-test", "-s", str(port)],
+                         stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                         preexec_fn=sigint_intercept)
+    fcntl.fcntl( p.stdout.fileno(),fcntl.F_SETFL,
+        fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
+
+    while p.poll() is None:
+        fd = select.select([p.stdout.fileno()], [], [])[0]
+        if fd:
+            out = p.stdout.readline()
+            if out.startswith("Starting RPC server"):
+                break
+    if p.poll() is not None:
+        raise RuntimeError("Couldn't start local instance of ovs-test server")
+    return p
+
+
+def get_datagram_sizes(mtu1, mtu2):
+    """
+    This function calculates all the "interesting" datagram sizes so that
+    we test both - receive and send side with different packets sizes.
+    """
+    s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1])
+    s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2])
+    return sorted(s1.union(s2))
+
+
+def ip_from_cidr(string):
+    """
+    This function removes the netmask (if present) from the given string and
+    returns the IP address.
+    """
+    token = string.split("/")
+    return token[0]
+
+
+def bandwidth_to_string(bwidth):
+    """Convert bandwidth from long to string and add units."""
+    bwidth = bwidth * 8  # Convert back to bits/second
+    if bwidth >= 10000000:
+        return str(int(bwidth / 1000000)) + "Mbps"
+    elif bwidth > 10000:
+        return str(int(bwidth / 1000)) + "Kbps"
+    else:
+        return str(int(bwidth)) + "bps"
index 1d677be..dd315e5 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 Nicira Networks
+# Copyright (c) 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
index 87c4552..4c1d782 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
index 9f1506c..ad7579c 100755 (executable)
@@ -5,7 +5,7 @@
 # chkconfig: 2345 09 91
 # description: Manage Open vSwitch kernel modules and user-space daemons
 
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 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.
@@ -48,6 +48,7 @@ start () {
     if test X"$BRCOMPAT" = Xyes; then
        set "$@" --brcompat
     fi
+    set "$@" $OVS_CTL_OPTS
     "$@"
 
     $ovs_ctl --protocol=gre enable-protocol
index 1a08fa9..46b94b9 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
        missingok
        postrotate
        # Tell Open vSwitch daemons to reopen their log files
-       if [ -e /var/run/openvswitch/ovs-vswitchd.pid ]; then
-           /usr/bin/ovs-appctl -t ovs-vswitchd vlog/reopen
-       fi
-       if [ -e /var/run/openvswitch/ovsdb-server.pid ]; then
-           /usr/bin/ovs-appctl -t ovsdb-server vlog/reopen
-       fi
+        for pidfile in `cd /var/run/openvswitch && echo *.pid`; do
+            ovs-appctl -t "${pidfile%%.pid}" vlog/reopen
+        done
        endscript
 }
index 3b5252d..19047d8 100755 (executable)
@@ -22,6 +22,7 @@ cd /etc/sysconfig/network-scripts
 [ -f ../network ] && . ../network
 
 CONFIG=${1}
+TIMEOUT=10
 
 source_config
 
@@ -37,12 +38,12 @@ case "$TYPE" in
        OVSBridge)
                ${OTHERSCRIPT} ${CONFIG} $2
                retval=$?
-               ovs-vsctl -- --if-exists del-br "$DEVICE"
+               ovs-vsctl -t ${TIMEOUT} -- --if-exists del-br "$DEVICE"
                ;;
        OVSPort|OVSIntPort|OVSBond)
                ${OTHERSCRIPT} ${CONFIG} $2
                retval=$?
-               ovs-vsctl -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE"
+               ovs-vsctl -t ${TIMEOUT} -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE"
                ;;
        *)
                echo $"Invalid OVS interface type $TYPE"
index b800ce0..c427879 100755 (executable)
@@ -22,6 +22,7 @@ cd /etc/sysconfig/network-scripts
 [ -f ../network ] && . ../network
 
 CONFIG=${1}
+TIMEOUT=10
 
 need_config ${CONFIG}
 
@@ -35,7 +36,7 @@ fi
 
 case "$TYPE" in
        OVSBridge)
-               ovs-vsctl -- --may-exist add-br "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
+               ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
                if [ "${OVSBOOTPROTO}" = "dhcp" ] && [ -n "${OVSINTF}" ]; then
                        case " ${OVSDHCPINTERFACES} " in
                                *" ${OVSINTF} "*)
@@ -50,12 +51,12 @@ case "$TYPE" in
        OVSPort)
                /sbin/ifup "$OVS_BRIDGE"
                ${OTHERSCRIPT} ${CONFIG} ${2}
-               ovs-vsctl -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
+               ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
                OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE"
                ;;
        OVSIntPort)
                /sbin/ifup "$OVS_BRIDGE"
-               ovs-vsctl -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=internal ${OVS_EXTRA+-- $OVS_EXTRA}
+               ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=internal ${OVS_EXTRA+-- $OVS_EXTRA}
                ${OTHERSCRIPT} ${CONFIG} ${2}
                ;;
        OVSBond)
@@ -63,7 +64,7 @@ case "$TYPE" in
                for _iface in $BOND_IFACES; do
                        /sbin/ifup ${_iface}
                done
-               ovs-vsctl -- --fake-iface add-bond "$OVS_BRIDGE" "$DEVICE" ${BOND_IFACES} $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
+               ovs-vsctl -t ${TIMEOUT} -- --fake-iface add-bond "$OVS_BRIDGE" "$DEVICE" ${BOND_IFACES} $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
                ${OTHERSCRIPT} ${CONFIG} ${2}
                OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE"
                ;;
index 4052cdd..ae73ea6 100644 (file)
@@ -1,7 +1,7 @@
 # Spec file for Open vSwitch kernel modules on Red Hat Enterprise
 # Linux 5.
 
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
index d165515..d9aeff0 100644 (file)
@@ -1,7 +1,7 @@
 # Spec file for Open vSwitch kernel modules on Red Hat Enterprise
 # Linux 6.
 
-# Copyright (C) 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
index e999757..de22c86 100644 (file)
@@ -1,6 +1,6 @@
 # Spec file for Open vSwitch on Red Hat Enterprise Linux.
 
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -11,7 +11,7 @@ Name: openvswitch
 Summary: Open vSwitch daemon/database/utilities
 Group: System Environment/Daemons
 URL: http://www.openvswitch.org/
-Vendor: Nicira Networks, Inc.
+Vendor: Nicira, Inc.
 Version: @VERSION@
 
 License: ASL 2.0
@@ -50,7 +50,7 @@ rhel_cp usr_share_openvswitch_scripts_sysconfig.template 0644
 
 docdir=$RPM_BUILD_ROOT/usr/share/doc/openvswitch-%{version}
 install -d -m755 "$docdir"
-install -m 0644 rhel/README.RHEL "$docdir"
+install -m 0644 FAQ rhel/README.RHEL "$docdir"
 install python/compat/uuid.py $RPM_BUILD_ROOT/usr/share/openvswitch/python
 install python/compat/argparse.py $RPM_BUILD_ROOT/usr/share/openvswitch/python
 
@@ -59,7 +59,9 @@ rm \
     $RPM_BUILD_ROOT/usr/bin/ovs-controller \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
     $RPM_BUILD_ROOT/usr/bin/ovs-test \
+    $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \
+    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \
     $RPM_BUILD_ROOT/usr/sbin/ovs-vlan-bug-workaround \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-vlan-bug-workaround.8
 
@@ -146,10 +148,12 @@ exit 0
 /usr/share/openvswitch/bugtool-plugins/
 /usr/share/openvswitch/python/
 /usr/share/openvswitch/scripts/ovs-bugtool-*
+/usr/share/openvswitch/scripts/ovs-check-dead-ifs
 /usr/share/openvswitch/scripts/ovs-ctl
 /usr/share/openvswitch/scripts/ovs-lib
 /usr/share/openvswitch/scripts/ovs-save
 /usr/share/openvswitch/scripts/sysconfig.template
 /usr/share/openvswitch/vswitch.ovsschema
+/usr/share/doc/openvswitch-%{version}/FAQ
 /usr/share/doc/openvswitch-%{version}/README.RHEL
 /var/lib/openvswitch
index 257fc82..cad1f53 100644 (file)
@@ -1,6 +1,6 @@
 ### Configuration options for openvswitch
 
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
 
 # FORCE_COREFILES: If 'yes' then core files will be enabled.
 # FORCE_COREFILES=yes
@@ -21,3 +21,7 @@
 
 # BRCOMPAT: If 'yes' compatibility mode will be enabled.
 # BRCOMPAT=yes
+
+# OVS_CTL_OPTS: Extra options to pass to ovs-ctl.  This is, for example,
+# a suitable place to specify --ovs-vswitchd-wrapper=valgrind.
+# OVS_CTL_OPTS=
index 24f8bd7..cca1571 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import re
+
 def xapi_local():
     return Session()
 
@@ -48,6 +50,20 @@ class Table(object):
     def get_all(self):
         return [RecordRef(rec) for rec in self.records]
 
+    def get_all_records_where(self, condition):
+        k, v = re.match(r'field "([^"]*)"="([^"]*)"$', condition).groups()
+        d = {}
+
+        # I'm sure that the keys used in the dictionary below are wrong
+        # but I can't find any documentation on get_all_records_where
+        # and this satisfies the current test case.
+        i = 0
+        for rec in self.records:
+            if rec[k] == v:
+                d[i] = rec
+                i += 1
+        return d
+
     def get_by_uuid(self, uuid):
         recs = [rec for rec in self.records if rec["uuid"] == uuid]
         if len(recs) != 1:
index 4a2865e..e5bcf2c 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 Nicira Networks.
+# Copyright (c) 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ import sys
 
 import ovs.daemon
 import ovs.unixctl
+import ovs.unixctl.client
 import ovs.util
 import ovs.vlog
 
@@ -29,7 +30,7 @@ def connect_to_target(target):
     else:
         socket_name = str_result
 
-    error, client = ovs.unixctl.UnixctlClient.create(socket_name)
+    error, client = ovs.unixctl.client.UnixctlClient.create(socket_name)
     if error:
         ovs.util.ovs_fatal(error, "cannot connect to \"%s\"" % socket_name)
 
index 1d37b59..5360225 100644 (file)
@@ -13,6 +13,17 @@ export PYTHONPATH
 PYTHONIOENCODING=utf_8
 export PYTHONIOENCODING
 
+# PYTHONDONTWRITEBYTECODE=yes keeps Python 2.6+ from creating .pyc and .pyo
+# files.  Creating .py[co] works OK for any given version of Open
+# vSwitch, but it causes trouble if you switch from a version with
+# foo/__init__.py into an (older) version with plain foo.py, since
+# foo/__init__.pyc will cause Python to ignore foo.py.
+#
+# Python before version 2.6 always creates .pyc files, so if you develop
+# with such an older version then you're out of luck.
+PYTHONDONTWRITEBYTECODE=yes
+export PYTHONDONTWRITEBYTECODE
+
 if test $HAVE_PYTHON = yes; then
     if python -m argparse 2>/dev/null; then
         :
index 784ae9c..20f9e82 100644 (file)
@@ -14,6 +14,7 @@ TESTSUITE_AT = \
        tests/check-structs.at \
        tests/daemon.at \
        tests/daemon-py.at \
+       tests/ofp-actions.at \
        tests/ofp-print.at \
        tests/ofp-errors.at \
        tests/ovs-ofctl.at \
@@ -74,7 +75,7 @@ check-local: tests/atconfig tests/atlocal $(TESTSUITE)
 COVERAGE = coverage
 COVERAGE_FILE='$(abs_srcdir)/.coverage'
 check-pycov: all tests/atconfig tests/atlocal $(TESTSUITE) clean-pycov
-       COVERAGE_FILE=$(COVERAGE_FILE) PYTHON='$(COVERAGE) run -p' $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=$(AUTOTEST_PATH) $(TESTSUITEFLAGS)
+       PYTHONDONTWRITEBYTECODE=yes COVERAGE_FILE=$(COVERAGE_FILE) PYTHON='$(COVERAGE) run -p' $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=$(AUTOTEST_PATH) $(TESTSUITEFLAGS)
        @cd $(srcdir) && $(COVERAGE) combine && COVERAGE_FILE=$(COVERAGE_FILE) $(COVERAGE) annotate
        @echo
        @echo '----------------------------------------------------------------------'
@@ -83,64 +84,6 @@ check-pycov: all tests/atconfig tests/atlocal $(TESTSUITE) clean-pycov
        @echo
        @COVERAGE_FILE=$(COVERAGE_FILE) $(COVERAGE) report
 \f
-# lcov support
-
-lcov_wrappers = \
-       tests/lcov/ovs-appctl \
-       tests/lcov/ovs-ofctl \
-       tests/lcov/ovs-vsctl \
-       tests/lcov/ovs-vswitchd \
-       tests/lcov/ovsdb-client \
-       tests/lcov/ovsdb-server \
-       tests/lcov/ovsdb-tool \
-       tests/lcov/test-aes128 \
-       tests/lcov/test-bundle \
-       tests/lcov/test-byte-order \
-       tests/lcov/test-classifier \
-       tests/lcov/test-csum \
-       tests/lcov/test-file_name \
-       tests/lcov/test-flows \
-       tests/lcov/test-hash \
-       tests/lcov/test-heap \
-       tests/lcov/test-hmap \
-       tests/lcov/test-json \
-       tests/lcov/test-jsonrpc \
-       tests/lcov/test-list \
-       tests/lcov/test-lockfile \
-       tests/lcov/test-multipath \
-       tests/lcov/test-odp \
-       tests/lcov/test-ovsdb \
-       tests/lcov/test-packets \
-       tests/lcov/test-random \
-       tests/lcov/test-reconnect \
-       tests/lcov/test-sha1 \
-       tests/lcov/test-stp \
-       tests/lcov/test-timeval \
-       tests/lcov/test-type-props \
-       tests/lcov/test-unix-socket \
-       tests/lcov/test-uuid \
-       tests/lcov/test-vconn
-
-$(lcov_wrappers): tests/lcov-wrapper.in
-       @test -d tests/lcov || mkdir tests/lcov
-       sed -e 's,[@]abs_top_builddir[@],$(abs_top_builddir),' \
-           -e 's,[@]wrap_program[@],$@,' \
-               $(top_srcdir)/tests/lcov-wrapper.in > $@.tmp
-       chmod +x $@.tmp
-       mv $@.tmp $@
-CLEANFILES += $(lcov_wrappers)
-EXTRA_DIST += tests/lcov-wrapper.in
-
-LCOV = lcov -b $(abs_top_builddir) -d $(abs_top_builddir) -q
-check-lcov: all tests/atconfig tests/atlocal $(TESTSUITE) $(lcov_wrappers)
-       rm -fr tests/coverage.html tests/coverage.info
-       $(LCOV) -c -i -o - > tests/coverage.info
-       $(SHELL) '$(TESTSUITE)' -C tests CHECK_LCOV=true DISABLE_LCOV=false AUTOTEST_PATH='tests/lcov:$(AUTOTEST_PATH)' $(TESTSUITEFLAGS); \
-               rc=$$?; \
-               echo "Producing coverage.html..."; \
-               cd tests && genhtml -q -o coverage.html coverage.info; \
-               exit $$rc
-\f
 # valgrind support
 
 valgrind_wrappers = \
@@ -192,7 +135,8 @@ VALGRIND = valgrind --log-file=valgrind.%p --leak-check=full \
        --suppressions=$(abs_top_srcdir)/tests/glibc.supp \
        --suppressions=$(abs_top_srcdir)/tests/openssl.supp --num-callers=20
 EXTRA_DIST += tests/glibc.supp tests/openssl.supp
-check-valgrind: all tests/atconfig tests/atlocal $(TESTSUITE) $(valgrind_wrappers)
+check-valgrind: all tests/atconfig tests/atlocal $(TESTSUITE) \
+                $(valgrind_wrappers) $(check_DATA)
        $(SHELL) '$(TESTSUITE)' -C tests CHECK_VALGRIND=true VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS)
        @echo
        @echo '----------------------------------------------------------------------'
index c3e0b34..634d46f 100644 (file)
@@ -5,11 +5,14 @@ AT_CHECK([ovs-ofctl parse-flow 'actions=autopath(1, NXM_NX_REG0[[]])'], [0],
   [usable protocols: any
 chosen protocol: OpenFlow10-table_id
 OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(1,NXM_NX_REG0[[]])
-])
+], [stderr])
 AT_CHECK([ovs-ofctl parse-flow 'actions=autopath(2, NXM_NX_REG0[[2..30]])'], [0],
   [usable protocols: any
 chosen protocol: OpenFlow10-table_id
 OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(2,NXM_NX_REG0[[2..30]])
+], [stderr])
+AT_CHECK([[sed 's/^[^|]*|[^|]*|//' stderr]], [0], [dnl
+autopath|WARN|The autopath action is deprecated and may be removed in February 2013.  Please email dev@openvswitch.org with concerns.
 ])
 AT_CLEANUP
 
index f188e7c..a80cd3e 100644 (file)
@@ -1,7 +1,6 @@
 AT_BANNER([daemon unit tests - C])
 
 AT_SETUP([daemon])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov wrapper make pids differ
 OVSDB_INIT([db])
 AT_CAPTURE_FILE([pid])
 AT_CAPTURE_FILE([expected])
@@ -20,7 +19,6 @@ AT_CHECK([test ! -e pid])
 AT_CLEANUP
 
 AT_SETUP([daemon --monitor])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov wrapper make pids differ
 OVSDB_INIT([db])
 AT_CAPTURE_FILE([pid])
 AT_CAPTURE_FILE([parent])
@@ -69,13 +67,12 @@ OVS_WAIT_WHILE([kill -0 `cat parent` || kill -0 `cat newpid` || test -e pid],
 AT_CLEANUP
 
 AT_SETUP([daemon --detach])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov wrapper make pids differ
 AT_CAPTURE_FILE([pid])
 OVSDB_INIT([db])
 # Start the daemon and make sure that the pidfile exists immediately.
 # We don't wait for the pidfile to get created because the daemon is
 # supposed to do so before the parent exits.
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0])
 AT_CHECK([test -s pid])
 AT_CHECK([kill -0 `cat pid`])
 # Kill the daemon and make sure that the pidfile gets deleted.
@@ -86,7 +83,6 @@ AT_CHECK([test ! -e pid])
 AT_CLEANUP
 
 AT_SETUP([daemon --detach --monitor])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov wrapper make pids differ
 m4_define([CHECK], 
   [AT_CHECK([$1], [$2], [$3], [$4], [kill `cat daemon monitor`])])
 OVSDB_INIT([db])
@@ -99,7 +95,7 @@ AT_CAPTURE_FILE([init])
 # Start the daemon and make sure that the pidfile exists immediately.
 # We don't wait for the pidfile to get created because the daemon is
 # supposed to do so before the parent exits.
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/daemon --monitor --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/daemon --monitor --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0])
 AT_CHECK([test -s daemon])
 # Check that the pidfile names a running process,
 # and that the parent process of that process is a running process,
@@ -138,7 +134,7 @@ AT_CLEANUP
 AT_SETUP([daemon --detach startup errors])
 AT_CAPTURE_FILE([pid])
 OVSDB_INIT([db])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/nonexistent/unixctl db], [1], [], [stderr])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/nonexistent/unixctl db], [1], [], [stderr])
 AT_CHECK([grep 'ovsdb-server: could not initialize control socket' stderr],
   [0], [ignore], [])
 AT_CHECK([test ! -s pid])
@@ -147,54 +143,8 @@ AT_CLEANUP
 AT_SETUP([daemon --detach --monitor startup errors])
 AT_CAPTURE_FILE([pid])
 OVSDB_INIT([db])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --monitor --unixctl="`pwd`"/nonexistent/unixctl db], [1], [], [stderr])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --monitor --unixctl="`pwd`"/nonexistent/unixctl db], [1], [], [stderr])
 AT_CHECK([grep 'ovsdb-server: could not initialize control socket' stderr],
   [0], [ignore], [])
 AT_CHECK([test ! -s pid])
 AT_CLEANUP
-
-# xxx This test hangs frequently, which often prevents builds from
-# xxx completing.   Skip this test until we have time to debug it.
-AT_SETUP([daemon --detach closes standard fds])
-AT_SKIP_IF([:])
-AT_CAPTURE_FILE([pid])
-AT_CAPTURE_FILE([status])
-AT_CAPTURE_FILE([stderr])
-OVSDB_INIT([db])
-AT_CHECK([(yes 2>stderr; echo $? > status) | ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl db], [0], [], [])
-AT_CHECK([kill `cat pid`])
-AT_CHECK([test -s status])
-if grep '[[bB]]roken pipe' stderr >/dev/null 2>&1; then
-  # Something in the environment caused SIGPIPE to be ignored, but
-  # 'yes' at least told us that it got EPIPE.  Good enough; we know
-  # that stdout was closed.
-  :
-else
-  # Otherwise make sure that 'yes' died from SIGPIPE.
-  AT_CHECK([kill -l `cat status`], [0], [PIPE
-])
-fi
-AT_CLEANUP
-
-# xxx This test hangs frequently, which often prevents builds from
-# xxx completing.   Skip this test until we have time to debug it.
-AT_SETUP([daemon --detach --monitor closes standard fds])
-AT_SKIP_IF([:])
-AT_CAPTURE_FILE([pid])
-AT_CAPTURE_FILE([status])
-AT_CAPTURE_FILE([stderr])
-OVSDB_INIT([db])
-AT_CHECK([(yes 2>stderr; echo $? > status) | ovsdb-server --detach --monitor --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl db], [0], [], [])
-AT_CHECK([kill `cat pid`])
-AT_CHECK([test -s status])
-if grep '[[bB]]roken pipe' stderr >/dev/null 2>&1; then
-  # Something in the environment caused SIGPIPE to be ignored, but
-  # 'yes' at least told us that it got EPIPE.  Good enough; we know
-  # that stdout was closed.
-  :
-else
-  # Otherwise make sure that 'yes' died from SIGPIPE.
-  AT_CHECK([kill -l `cat status`], [0], [PIPE
-])
-fi
-AT_CLEANUP
index e0b43dc..aee4070 100644 (file)
@@ -24,3 +24,100 @@ CHECK_FILE_NAME([dir/file], [dir], [file])
 CHECK_FILE_NAME([dir/file/], [dir], [file])
 CHECK_FILE_NAME([dir/file//], [dir], [file])
 CHECK_FILE_NAME([///foo], [/], [foo])
+
+AT_BANNER([test follow_symlinks function])
+
+m4_define([CHECK_FOLLOW],
+  [echo "check $1 -> $2"
+   AT_CHECK_UNQUOTED([test-util follow-symlinks "$1"], [0], [$2
+])
+   echo])
+
+AT_SETUP([follow_symlinks - relative symlinks])
+: > target
+ln -s target source
+AT_SKIP_IF([test ! -h source])
+CHECK_FOLLOW([source], [target])
+
+mkdir dir
+ln -s target2 dir/source2
+CHECK_FOLLOW([dir/source2], [dir/target2])
+
+mkdir dir/dir2
+ln -s dir/b a
+ln -s c dir/b
+ln -s dir2/d dir/c
+CHECK_FOLLOW([a], [dir/dir2/d])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - absolute symlinks])
+: > target
+ln -s "`pwd`/target" source
+AT_SKIP_IF([test ! -h source])
+CHECK_FOLLOW([source], [`pwd`/target])
+
+mkdir dir
+ln -s "`pwd`/dir/target2" dir/source2
+CHECK_FOLLOW([dir/source2], [`pwd`/dir/target2])
+
+mkdir dir/dir2
+ln -s "`pwd`/dir/b" a
+ln -s "`pwd`/dir/c" dir/b
+ln -s "`pwd`/dir/dir2/d" dir/c
+CHECK_FOLLOW([a], [`pwd`/dir/dir2/d])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - symlinks to directories])
+mkdir target
+ln -s target source
+AT_SKIP_IF([test ! -h source])
+ln -s target/ source2
+CHECK_FOLLOW([source], [target])
+CHECK_FOLLOW([source2], [target/])
+
+# follow_symlinks() doesn't expand symlinks in the middle of a name.
+: > source/x
+CHECK_FOLLOW([source/x], [source/x])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - nonexistent targets])
+ln -s target source
+AT_SKIP_IF([test ! -h source])
+CHECK_FOLLOW([source], [target])
+CHECK_FOLLOW([target], [target])
+CHECK_FOLLOW([target], [target])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - regular files])
+touch x
+CHECK_FOLLOW([x], [x])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - device targets])
+AT_SKIP_IF([test ! -e /dev/null])
+AT_SKIP_IF([test ! -e /dev/full])
+ln -s /dev/null x
+ln -s /dev/full y
+CHECK_FOLLOW([x], [/dev/null])
+CHECK_FOLLOW([y], [/dev/full])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - nonexistent files])
+CHECK_FOLLOW([nonexistent], [nonexistent])
+CHECK_FOLLOW([a/b/c], [a/b/c])
+CHECK_FOLLOW([/a/b/c], [/a/b/c])
+CHECK_FOLLOW([//a/b/c], [//a/b/c])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - symlink loop])
+ln -s a b
+AT_SKIP_IF([test ! -h b])
+ln -s b a
+AT_SKIP_IF([test ! -h a])
+
+AT_CHECK([test-util follow-symlinks a], [0], [a
+], [stderr])
+AT_CHECK([sed 's/^[[^|]]*|//' stderr], [0],
+  [00001|util|WARN|a: too many levels of symlinks
+])
+AT_CLEANUP
index 95c8f48..536fb32 100755 (executable)
@@ -1,6 +1,6 @@
 #! /usr/bin/perl
 
-# Copyright (c) 2009, 2010, 2011 Nicira Networks.
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -109,10 +109,13 @@ sub output {
 
     # Compose packet.
     my $packet = '';
+    my $wildcards = 0;
     $packet .= pack_ethaddr($flow{DL_DST});
     $packet .= pack_ethaddr($flow{DL_SRC});
     if ($flow{DL_VLAN} != 0xffff) {
         $packet .= pack('nn', 0x8100, $flow{DL_VLAN});
+    } else {
+        $wildcards |= 1 << 20;   # OFPFW10_DL_VLAN_PCP
     }
     my $len_ofs = length($packet);
     $packet .= pack('n', 0) if $attrs{DL_HEADER} =~ /^802.2/;
@@ -204,8 +207,8 @@ sub output {
     print "\n";
 
     print FLOWS pack('Nn',
-                     0,         # wildcards
-                     1);        # in_port
+                     $wildcards, # wildcards
+                     1);         # in_port
     print FLOWS pack_ethaddr($flow{DL_SRC});
     print FLOWS pack_ethaddr($flow{DL_DST});
     print FLOWS pack('nCxnCCxxNNnn',
index 3658882..59507ff 100644 (file)
@@ -709,7 +709,7 @@ configure_datapath: bridge      - xenbr2
 configure_datapath: physical    - [u'eth2']
 configure_datapath: extra ports - []
 configure_datapath: extra bonds - []
-/usr/bin/ovs-vsctl --timeout=5 -vANY:console:off get-fail-mode xenbr2
+/usr/bin/ovs-vsctl --timeout=5 -vconsole:off get-fail-mode xenbr2
 Applying changes to /etc/sysconfig/network-scripts/route-xenbr2 configuration
 Applying changes to /etc/sysconfig/network configuration
 Applying changes to /etc/sysconfig/network-scripts/ifcfg-xenbr2 configuration
@@ -724,7 +724,7 @@ Applying changes to /etc/sysconfig/network-scripts/ifcfg-xenbr2 configuration
     set Bridge xenbr2 fail_mode=secure
     remove Bridge xenbr2 other_config disable-in-band
     br-set-external-id xenbr2 xs-network-uuids d08c8749-0c8f-9e8d-ce25-fd364661ee99
-/usr/bin/ovs-vsctl --timeout=5 -vANY:console:off get interface eth2 ofport
+/usr/bin/ovs-vsctl --timeout=5 -vconsole:off get interface eth2 ofport
 /usr/bin/ovs-ofctl add-flow xenbr2 idle_timeout=0,priority=0,in_port=5,arp,nw_proto=1,actions=local
 /usr/bin/ovs-ofctl add-flow xenbr2 idle_timeout=0,priority=0,in_port=local,arp,dl_src=00:15:17:a0:29:80,actions=5
 /usr/bin/ovs-ofctl add-flow xenbr2 idle_timeout=0,priority=0,in_port=5,dl_dst=00:15:17:a0:29:80,actions=local
index b6aa27d..2a7f91b 100644 (file)
@@ -1,7 +1,7 @@
 AT_BANNER([JSON-RPC - C])
 
 AT_SETUP([JSON-RPC request and successful reply])
-AT_CHECK([test-jsonrpc --detach --pidfile="`pwd`"/pid listen punix:socket])
+AT_CHECK([test-jsonrpc --detach --no-chdir --pidfile="`pwd`"/pid listen punix:socket])
 AT_CHECK([test -s pid])
 AT_CHECK([kill -0 `cat pid`])
 AT_CHECK(
@@ -12,7 +12,7 @@ AT_CHECK([kill `cat pid`])
 AT_CLEANUP
 
 AT_SETUP([JSON-RPC request and error reply])
-AT_CHECK([test-jsonrpc --detach --pidfile="`pwd`"/pid listen punix:socket])
+AT_CHECK([test-jsonrpc --detach --no-chdir --pidfile="`pwd`"/pid listen punix:socket])
 AT_CHECK([test -s pid])
 AT_CHECK([kill -0 `cat pid`])
 AT_CHECK(
@@ -23,7 +23,7 @@ AT_CHECK([kill `cat pid`])
 AT_CLEANUP
 
 AT_SETUP([JSON-RPC notification])
-AT_CHECK([test-jsonrpc --detach --pidfile="`pwd`"/pid listen punix:socket])
+AT_CHECK([test-jsonrpc --detach --no-chdir --pidfile="`pwd`"/pid listen punix:socket])
 AT_CHECK([test -s pid])
 # When a daemon dies it deletes its pidfile, so make a copy.
 AT_CHECK([cp pid pid2])
index 543aa25..7d96143 100644 (file)
@@ -17,6 +17,7 @@ AT_CHECK([ovs-appctl lacp/show], [0], [dnl
 slave: p1: expired attached
        port_id: 1
        port_priority: 65535
+       may_enable: false
 
        actor sys_id: aa:55:aa:55:00:00
        actor sys_priority: 65535
@@ -64,6 +65,7 @@ AT_CHECK([sed -e 's/aggregation key:.*/aggregation key: <omitted>/' < stdout], [
 slave: p1: expired attached
        port_id: 11
        port_priority: 111
+       may_enable: false
 
        actor sys_id: 11:22:33:44:55:66
        actor sys_priority: 54321
@@ -82,6 +84,7 @@ slave: p1: expired attached
 slave: p2: expired attached
        port_id: 22
        port_priority: 222
+       may_enable: false
 
        actor sys_id: 11:22:33:44:55:66
        actor sys_priority: 54321
diff --git a/tests/lcov-wrapper.in b/tests/lcov-wrapper.in
deleted file mode 100755 (executable)
index 0c6a359..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#! /bin/sh
-
-abs_top_builddir='@abs_top_builddir@'
-wrap_program=`basename '@wrap_program@'`
-
-# Strip the first directory from $PATH that contains $wrap_program,
-# so that below we run the real $wrap_program, not ourselves.
-not_found=true
-new_path=
-first=true
-save_IFS=$IFS
-IFS=:
-for dir in $PATH; do
-    IFS=$save_IFS
-    if $not_found && test -x "$dir/$wrap_program"; then
-        not_found=false
-    else
-        if $first; then
-            first=false
-            new_path=$dir
-        else
-            new_path=$new_path:$dir
-        fi
-    fi
-done
-IFS=$save_IFS
-if $not_found; then
-    echo "$0: error: cannot find $wrap_program in \$PATH" >&2
-    exit 1
-fi
-PATH=$new_path
-export PATH
-
-if test "$DISABLE_LCOV" = true; then
-    exec $wrap_program "$@"
-    exit 1
-fi
-
-# XXX Probably want some kind of synchronization here to deal with
-# programs running in parallel.
-LCOV="lcov -b $abs_top_builddir -d $abs_top_builddir -q"
-$LCOV -z
-
-# Run the subprocess and propagate signals to it.
-for signal in 1 2 3 5 15; do
-    trap "kill -$signal \$!     # Propagate signal
-          trap - $signal        # Reset signal to default
-          wait                  # Wait for child to die
-          kill -$signal $$      # Kill ourselves with same signal
-          exit 1                # Exit in case 'kill' failed" $signal
-done
-$wrap_program 0<&0 "$@" &       # 0<&0 prevents shell from closing stdin
-exec 0</dev/null                # Don't hold stdin open unnecessarily
-wait $!; rc=$?
-
-# Run lcov, but only if some .gcda files were produced, since lcov
-# complains otherwise.
-for file in `find "$abs_top_builddir" -name '*.gcda'`; do
-    $LCOV -c -o - >> "$abs_top_builddir/tests/coverage.info"
-    break
-done
-
-exit $rc
index 0d1406c..da82f51 100644 (file)
@@ -10,7 +10,7 @@ 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)
-OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0x000a->NXM_NX_REG0[5..10])
+OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0xa->NXM_NX_REG0[5..10])
 OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
 ]])
 AT_CLEANUP
@@ -37,12 +37,12 @@ AT_SETUP([learning action - satisfied prerequisites])
 AT_DATA([flows.txt],
 [[actions=learn(eth_type=0x800,load:5->NXM_OF_IP_DST[])
 ip,actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[])
-ip,actions=learn(eth_type=0x800,NXM_OF_IP_DST[])
+ip,actions=learn(eth_type=0x800,OXM_OF_IPV4_DST[])
 ]])
 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,eth_type=0x800,load:0x00000005->NXM_OF_IP_DST[])
+OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x5->NXM_OF_IP_DST[])
 OFPT_FLOW_MOD (xid=0x2): ADD ip actions=learn(table=1,load:NXM_OF_IP_DST[]->NXM_NX_REG1[])
 OFPT_FLOW_MOD (xid=0x3): ADD ip actions=learn(table=1,eth_type=0x800,NXM_OF_IP_DST[])
 ]])
@@ -78,7 +78,7 @@ AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2,0,1
 
 # Check for the MAC learning entry.
 AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
  table=1, priority=0 actions=FLOOD
 NXST_FLOW reply:
 ])
@@ -91,8 +91,8 @@ AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3
 
 # Check for both MAC learning entries.
 AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip |sort], [0], [dnl
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
  table=1, priority=0 actions=FLOOD
 NXST_FLOW reply:
 ])
@@ -104,8 +104,8 @@ AT_CHECK([tail -1 stdout], [0], [Datapath actions: 3,0,1
 
 # Check that the MAC learning entry was updated.
 AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:2
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:2
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
  table=1, priority=0 actions=FLOOD
 NXST_FLOW reply:
 ])
@@ -127,7 +127,7 @@ AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2,0,1
 
 # Check for the learning entry.
 AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
- table=1, hard_timeout=60,tcp,nw_src=192.168.0.1,nw_dst=192.168.0.2,tp_src=80,tp_dst=40000 actions=drop
+ table=1, hard_timeout=60, tcp,nw_src=192.168.0.1,nw_dst=192.168.0.2,tp_src=80,tp_dst=40000 actions=drop
 NXST_FLOW reply:
 ])
 OVS_VSWITCHD_STOP
@@ -150,7 +150,7 @@ AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2,0,1
 
 # Check for the learning entry.
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- table=1, hard_timeout=60,tcp6,ipv6_src=fec0::1,ipv6_dst=2001:db8:85a3::8a2e:370:7334,tp_src=80,tp_dst=40000 actions=load:0x13198a2e03707348->NXM_NX_IPV6_DST[[0..63]],load:0x20010db885a308d3->NXM_NX_IPV6_DST[[64..127]]
+ table=1, hard_timeout=60, tcp6,ipv6_src=fec0::1,ipv6_dst=2001:db8:85a3::8a2e:370:7334,tp_src=80,tp_dst=40000 actions=load:0x13198a2e03707348->NXM_NX_IPV6_DST[[0..63]],load:0x20010db885a308d3->NXM_NX_IPV6_DST[[64..127]]
  tcp6 actions=learn(table=1,hard_timeout=60,eth_type=0x86dd,nw_proto=6,NXM_NX_IPV6_SRC[[]]=NXM_NX_IPV6_DST[[]],ipv6_dst=2001:db8:85a3::8a2e:370:7334,NXM_OF_TCP_SRC[[]]=NXM_OF_TCP_DST[[]],NXM_OF_TCP_DST[[]]=NXM_OF_TCP_SRC[[]],load:0x20010db885a308d313198a2e03707348->NXM_NX_IPV6_DST[[]]),FLOOD
 NXST_FLOW reply:
 ])
index dce69de..70660a2 100644 (file)
@@ -100,10 +100,17 @@ nibble   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
 ])
 AT_CLEANUP
 
-AT_SETUP([test utility functions])
-AT_KEYWORDS([util])
-AT_CHECK([test-util])
-AT_CLEANUP
+m4_foreach(
+  [testname],
+  [[ctz],
+   [log_2_floor],
+   [bitwise_copy],
+   [bitwise_zero],
+   [bitwise_one],
+   [bitwise_is_all_zeros]],
+  [AT_SETUP([testname[()] function])
+   AT_CHECK([test-util testname], [0], [], [])
+   AT_CLEANUP])
 
 AT_SETUP([test unix socket -- short pathname])
 AT_CHECK([test-unix-socket x])
index 9cc95a8..877cc87 100644 (file)
@@ -15,6 +15,6 @@ CHECK_LOCKFILE([lock_blocks_same_process_twice], [0])
 CHECK_LOCKFILE([lock_blocks_other_process], [1])
 CHECK_LOCKFILE([lock_twice_blocks_other_process], [1])
 CHECK_LOCKFILE([lock_and_unlock_allows_other_process], [1])
-CHECK_LOCKFILE([lock_timeout_gets_the_lock], [1])
-CHECK_LOCKFILE([lock_timeout_runs_out], [1])
 CHECK_LOCKFILE([lock_multiple], [0])
+CHECK_LOCKFILE([lock_symlink], [0])
+CHECK_LOCKFILE([lock_symlink_to_dir], [0])
index 90a1192..9617af2 100644 (file)
@@ -1,6 +1,7 @@
 AT_BANNER([datapath parsing and formatting])
 
-AT_SETUP([OVS datapath parsing and formatting - valid forms])
+AT_SETUP([OVS datapath 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),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15)
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234)
@@ -45,6 +46,12 @@ s/$/)/' odp-base.txt
  echo '# Valid forms with tun_id and VLAN headers.'
  sed 's/^/tun_id(0xfedcba9876543210),/
 s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
+s/$/)/' odp-base.txt
+
+ echo
+ echo '# Valid forms with QOS priority, tun_id, and VLAN headers.'
+ sed 's/^/priority(1234),tun_id(0xfedcba9876543210),/
+s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
 s/$/)/' odp-base.txt
 
  echo
@@ -55,8 +62,37 @@ sed -n 's/,frag=no),/,frag=first),/p' odp-base.txt
  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 < odp.txt], [0], [`cat odp.txt`
+AT_CHECK_UNQUOTED([test-odp parse-keys < odp.txt], [0], [`cat odp.txt`
 ])
 AT_CLEANUP
 
-dnl We could add a test for invalid forms, but that's less important.
+AT_SETUP([OVS datapath actions parsing and formatting - valid forms])
+AT_DATA([actions.txt], [dnl
+1,2,3
+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=9123,userdata=0x815309)
+set(tun_id(0x7f10354))
+set(in_port(2))
+set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))
+set(eth_type(0x1234))
+set(ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=no))
+set(tcp(src=80,dst=8080))
+set(udp(src=81,dst=6632))
+set(icmp(type=1,code=2))
+set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no))
+set(icmpv6(type=1,code=2))
+push_vlan(vid=12,pcp=0)
+push_vlan(vid=13,pcp=5,cfi=0)
+push_vlan(tpid=0x9100,vid=13,pcp=5)
+push_vlan(tpid=0x9100,vid=13,pcp=5,cfi=0)
+pop_vlan
+sample(sample=9.7%,actions(1,2,3,push_vlan(vid=1,pcp=2)))
+])
+AT_CHECK_UNQUOTED([test-odp parse-actions < actions.txt], [0],
+  [`cat actions.txt`
+])
+AT_CLEANUP
diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at
new file mode 100644 (file)
index 0000000..ba5003a
--- /dev/null
@@ -0,0 +1,318 @@
+AT_BANNER([OpenFlow actions])
+
+AT_SETUP([OpenFlow 1.0 action translation])
+AT_KEYWORDS([OF1.0])
+AT_DATA([test-data], [dnl
+# actions=LOCAL
+0000 0008 fffe 04d2
+
+# actions=CONTROLLER:1234
+0000 0008 fffd 04d2
+
+# actions=mod_vlan_vid:9
+0001 0008 0009 0000
+
+# actions=mod_vlan_pcp:6
+0002 0008 06 000000
+
+# actions=strip_vlan
+0003 0008 00000000
+
+# actions=mod_dl_src:00:11:22:33:44:55
+0004 0010 001122334455 000000000000
+
+# actions=mod_dl_dst:10:20:30:40:50:60
+0005 0010 102030405060 000000000000
+
+# actions=mod_nw_src:1.2.3.4
+0006 0008 01020304
+
+# actions=mod_nw_dst:192.168.0.1
+0007 0008 c0a80001
+
+# actions=mod_nw_tos:48
+0008 0008 30 000000
+
+# actions=mod_tp_src:80
+0009 0008 0050 0000
+
+# actions=mod_tp_dst:443
+000a 0008 01bb 0000
+
+# actions=enqueue:10q55
+000b 0010 000a 000000000000 00000037
+
+# actions=resubmit:5
+ffff 0010 00002320 0001 0005 00000000
+
+# actions=set_tunnel:0x12345678
+ffff 0010 00002320 0002 0000 12345678
+
+# actions=set_queue:2309737729
+ffff 0010 00002320 0004 0000 89abcd01
+
+# actions=pop_queue
+ffff 0010 00002320 0005 000000000000
+
+# actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0006 0010 0000 0000 00000002 00000802
+
+# actions=load:0xf009->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0007 000f 00000802 000000000000f009
+
+# actions=note:11.e9.9a.ad.67.f3
+ffff 0010 00002320 0008 11e99aad67f3
+
+# actions=set_tunnel64:0xc426384d49c53d60
+ffff 0018 00002320 0009 000000000000 c426384d49c53d60
+
+# actions=set_tunnel64:0x885f3298
+ffff 0018 00002320 0009 000000000000 00000000885f3298
+
+# actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[])
+ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004
+
+# actions=autopath(2,NXM_NX_REG0[2..30])
+& autopath|WARN|The autopath action is deprecated and may be removed in February 2013.  Please email dev@openvswitch.org with concerns.
+ffff 0018 00002320 000b 009c 00010004 00000002 00000000
+
+# actions=bundle(eth_src,0,hrw,ofport,slaves:4,8)
+ffff 0028 00002320 000c 0001 0000 0000 00000002 0002 0000 00000000 00000000 dnl
+0004 0008 00000000
+
+# actions=bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[],slaves:4,8)
+ffff 0028 00002320 000d 0001 0000 0000 00000002 0002 001f 00010004 00000000 dnl
+0004 0008 00000000
+
+# actions=resubmit(10,5)
+ffff 0010 00002320 000e 000a 05 000000
+
+# actions=output:NXM_NX_REG1[5..10]
+ffff 0018 00002320 000f 0145 00010204 ffff 000000000000
+
+# actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
+ffff 0048 00002320 0010 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 dnl
+000c 00000802 0000 00000802 0000 dnl
+0030 00000406 0000 00000206 0000 dnl
+1010 00000002 0000 dnl
+00000000
+
+# actions=exit
+ffff 0010 00002320 0011 000000000000
+
+# actions=dec_ttl
+ffff 0010 00002320 0012 000000000000
+
+# actions=fin_timeout(idle_timeout=10,hard_timeout=20)
+ffff 0010 00002320 0013 000a 0014 0000
+
+# actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
+ffff 0010 00002320 0014 04d2 162e 02 00
+
+# actions=dec_ttl(32768,12345,90,765,1024)
+ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+  [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp10-actions < input.txt],
+  [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([OpenFlow 1.1 action translation])
+AT_KEYWORDS([OF1.1])
+AT_DATA([test-data], [dnl
+# actions=LOCAL
+0000 0010 fffffffe 04d2 000000000000
+
+# actions=CONTROLLER:1234
+0000 0010 fffffffd 04d2 000000000000
+
+# actions=mod_vlan_vid:9
+0001 0008 0009 0000
+
+# actions=mod_vlan_pcp:6
+0002 0008 06 000000
+
+# actions=mod_dl_src:00:11:22:33:44:55
+0003 0010 001122334455 000000000000
+
+# actions=mod_dl_dst:10:20:30:40:50:60
+0004 0010 102030405060 000000000000
+
+# actions=mod_nw_src:1.2.3.4
+0005 0008 01020304
+
+# actions=mod_nw_dst:192.168.0.1
+0006 0008 c0a80001
+
+# actions=mod_nw_tos:48
+0007 0008 30 000000
+
+# actions=mod_tp_src:80
+0009 0008 0050 0000
+
+# actions=mod_tp_dst:443
+000a 0008 01bb 0000
+
+# actions=resubmit:5
+ffff 0010 00002320 0001 0005 00000000
+
+# actions=set_tunnel:0x12345678
+ffff 0010 00002320 0002 0000 12345678
+
+# actions=set_queue:2309737729
+ffff 0010 00002320 0004 0000 89abcd01
+
+# actions=pop_queue
+ffff 0010 00002320 0005 000000000000
+
+# actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0006 0010 0000 0000 00000002 00000802
+
+# actions=load:0xf009->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0007 000f 00000802 000000000000f009
+
+# actions=note:11.e9.9a.ad.67.f3
+ffff 0010 00002320 0008 11e99aad67f3
+
+# actions=set_tunnel64:0xc426384d49c53d60
+ffff 0018 00002320 0009 000000000000 c426384d49c53d60
+
+# actions=set_tunnel64:0x885f3298
+ffff 0018 00002320 0009 000000000000 00000000885f3298
+
+# actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[])
+ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004
+
+# actions=autopath(2,NXM_NX_REG0[2..30])
+& autopath|WARN|The autopath action is deprecated and may be removed in February 2013.  Please email dev@openvswitch.org with concerns.
+ffff 0018 00002320 000b 009c 00010004 00000002 00000000
+
+# actions=bundle(eth_src,0,hrw,ofport,slaves:4,8)
+ffff 0028 00002320 000c 0001 0000 0000 00000002 0002 0000 00000000 00000000 dnl
+0004 0008 00000000
+
+# actions=bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[],slaves:4,8)
+ffff 0028 00002320 000d 0001 0000 0000 00000002 0002 001f 00010004 00000000 dnl
+0004 0008 00000000
+
+# actions=resubmit(10,5)
+ffff 0010 00002320 000e 000a 05 000000
+
+# actions=output:NXM_NX_REG1[5..10]
+ffff 0018 00002320 000f 0145 00010204 ffff 000000000000
+
+# actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
+ffff 0048 00002320 0010 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 dnl
+000c 00000802 0000 00000802 0000 dnl
+0030 00000406 0000 00000206 0000 dnl
+1010 00000002 0000 dnl
+00000000
+
+# actions=exit
+ffff 0010 00002320 0011 000000000000
+
+# actions=dec_ttl
+ffff 0010 00002320 0012 000000000000
+
+# actions=fin_timeout(idle_timeout=10,hard_timeout=20)
+ffff 0010 00002320 0013 000a 0014 0000
+
+# actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
+ffff 0010 00002320 0014 04d2 162e 02 00
+
+# actions=dec_ttl(32768,12345,90,765,1024)
+ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+  [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp11-actions < input.txt],
+  [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([OpenFlow 1.1 instruction translation])
+AT_KEYWORDS([OF1.1])
+AT_DATA([test-data], [dnl
+# actions=LOCAL
+0004 0018 00000000 dnl
+0000 0010 fffffffe 04d2 000000000000
+
+dnl Check that an empty Apply-Actions instruction gets dropped.
+# actions=drop
+#  0: 00 -> (none)
+#  1: 04 -> (none)
+#  2: 00 -> (none)
+#  3: 08 -> (none)
+#  4: 00 -> (none)
+#  5: 00 -> (none)
+#  6: 00 -> (none)
+#  7: 00 -> (none)
+0004 0008 00000000
+
+# bad OF1.1 instructions: NXBIC_DUP_TYPE
+0004 0008 00000000 0004 0008 00000000
+
+dnl Instructions not multiple of 8 in length.
+& ofp_actions|WARN|OpenFlow message instructions length 9 is not a multiple of 8
+# bad OF1.1 instructions: OFPBIC_BAD_LEN
+0004 0009 01 00000000
+
+dnl Goto-Table instruction too long.
+# bad OF1.1 instructions: OFPBIC_BAD_LEN
+0001 0010 01 000000 0000000000000000
+
+dnl Goto-Table not supported yet.
+# bad OF1.1 instructions: OFPBIC_UNSUP_INST
+0001 0008 01 000000
+
+dnl Write-Metadata not supported yet.
+# bad OF1.1 instructions: OFPBIC_UNSUP_INST
+0002 0018 00000000 fedcba9876543210 ffffffffffffffff
+
+dnl Write-Metadata too short.
+# bad OF1.1 instructions: OFPBIC_BAD_LEN
+0002 0010 00000000 fedcba9876543210
+
+dnl Write-Metadata too long.
+# bad OF1.1 instructions: OFPBIC_BAD_LEN
+0002 0020 00000000 fedcba9876543210 ffffffffffffffff 0000000000000000
+
+dnl Write-Actions not supported yet.
+# bad OF1.1 instructions: OFPBIC_UNSUP_INST
+0003 0008 01 000000
+
+dnl Clear-Actions not supported yet.
+# bad OF1.1 instructions: OFPBIC_UNSUP_INST
+0005 0008 01 000000
+
+dnl Experimenter actions not supported yet.
+# bad OF1.1 instructions: OFPBIC_BAD_EXPERIMENTER
+ffff 0008 01 000000
+
+dnl Bad instruction number (0 not assigned).
+# bad OF1.1 instructions: OFPBIC_UNKNOWN_INST
+0000 0008 01 000000
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+  [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp11-instructions < input.txt],
+  [0], [expout], [experr])
+AT_CLEANUP
index 5f76294..b45a33a 100644 (file)
@@ -18,10 +18,28 @@ AT_CLEANUP
 
 AT_SETUP([OFPT_ERROR with type OFPET_BAD_REQUEST - OF1.0])
 AT_KEYWORDS([ofp-print ofp-errors])
-AT_CHECK([ovs-ofctl ofp-print 01010014000000000001000601bbccddeeff0011], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 0101001400000000000100060105ccddeeff0011], [0], [dnl
 OFPT_ERROR (xid=0x0): OFPBRC_BAD_LEN
+OFPT_FEATURES_REQUEST (xid=0xeeff0011):
 (***truncated to 8 bytes from 52445***)
-00000000  01 bb cc dd ee ff 00 11-                        |........        |
+00000000  01 05 cc dd ee ff 00 11-                        |........        |
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_ERROR prints type of truncated inner messages])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print "0101004c092529d500010006 \
+01 06 00 e0 00 00 00 01 00 00 50 54 00 00 00 01 \
+00 00 01 00 02 00 00 00 00 00 00 87 00 00 0f ff \
+ff fe 50 54 00 00 00 01 62 72 30 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01"], [0], [dnl
+OFPT_ERROR (xid=0x92529d5): OFPBRC_BAD_LEN
+OFPT_FEATURES_REPLY (xid=0x1):
+(***truncated to 64 bytes from 224***)
+00000000  01 06 00 e0 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....|
+00000010  00 00 01 00 02 00 00 00-00 00 00 87 00 00 0f ff |................|
+00000020  ff fe 50 54 00 00 00 01-62 72 30 00 00 00 00 00 |..PT....br0.....|
+00000030  00 00 00 00 00 00 00 00-00 00 00 01 00 00 00 01 |................|
 ])
 AT_CLEANUP
 
@@ -45,18 +63,20 @@ dnl Error type 3, code 1 is OFPFMFC_OVERLAP in OF1.0
 dnl and OFPBIC_UNSUP_INST in OF1.1, so check that value in both versions.
 AT_SETUP([OFPT_ERROR with type OFPFMFC_OVERLAP - OF1.0])
 AT_KEYWORDS([ofp-print ofp-errors])
-AT_CHECK([ovs-ofctl ofp-print 01010014000000000003000101bbccddeeff0011], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 0101001400000000000300010106ccddeeff0011], [0], [dnl
 OFPT_ERROR (xid=0x0): OFPFMFC_OVERLAP
+OFPT_FEATURES_REPLY (xid=0xeeff0011):
 (***truncated to 8 bytes from 52445***)
-00000000  01 bb cc dd ee ff 00 11-                        |........        |
+00000000  01 06 cc dd ee ff 00 11-                        |........        |
 ])
 AT_CLEANUP
 AT_SETUP([OFPT_ERROR with type OFPBIC_UNSUP_INST - OF1.1])
 AT_KEYWORDS([ofp-print ofp-errors])
-AT_CHECK([ovs-ofctl ofp-print 02010014000000000003000102bbccddeeff0011], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 0201001400000000000300010206ccddeeff0011], [0], [dnl
 OFPT_ERROR (OF1.1) (xid=0x0): OFPBIC_UNSUP_INST
+OFPT_FEATURES_REPLY (OF1.1) (xid=0xeeff0011):
 (***truncated to 8 bytes from 52445***)
-00000000  02 bb cc dd ee ff 00 11-                        |........        |
+00000000  02 06 cc dd ee ff 00 11-                        |........        |
 ])
 AT_CLEANUP
 
@@ -86,12 +106,12 @@ 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 (OF 0x03) (xid=0x55555555): OFPBIC_BAD_EXPERIMENTER
+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 (OF 0x03) (xid=0x55555555): OFPBIC_BAD_EXP_TYPE
+OFPT_ERROR (OF1.2) (xid=0x55555555): OFPBIC_BAD_EXP_TYPE
 OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
 ])
 AT_CLEANUP
index 31d3293..3c55d91 100644 (file)
@@ -20,23 +20,23 @@ AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print 00bb0008eeff0011],
   [0], [dnl
 ***decode error: OFPBRC_BAD_TYPE***
 00000000  00 bb 00 08 ee ff 00 11-                        |........        |
-], [ofp_util|WARN|received OpenFlow message of unknown type 187
+], [ofp_msgs|WARN|unknown OpenFlow message (version 0, type 187)
 ])
 AT_CLEANUP
 
 AT_SETUP([truncated message])
 AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print 01bbccddeeff0011], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 0110ccddeeff0011], [0], [dnl
 (***truncated to 8 bytes from 52445***)
-00000000  01 bb cc dd ee ff 00 11-                        |........        |
+00000000  01 10 cc dd ee ff 00 11-                        |........        |
 ])
 AT_CLEANUP
 
 AT_SETUP([message only uses part of buffer])
 AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print 01bb0009eeff00112233], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 01100009eeff00112233], [0], [dnl
 (***only uses 9 bytes out of 10***)
-00000000  01 bb 00 09 ee ff 00 11-22 33                   |........"3      |
+00000000  01 10 00 09 ee ff 00 11-22 33                   |........"3      |
 ])
 # "
 AT_CLEANUP
@@ -202,7 +202,6 @@ ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
 OFPT_FEATURES_REPLY (OF1.1) (xid=0x1): dpid:0000505400000001
 n_tables:2, n_buffers:256
 capabilities: FLOW_STATS TABLE_STATS PORT_STATS ARP_MATCH_IP
-actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP SET_DL_SRC SET_DL_DST SET_NW_SRC SET_NW_DST SET_NW_ECN SET_NW_TOS SET_TP_SRC SET_TP_DST COPY_TTL_OUT COPY_TTL_IN SET_MPLS_LABEL SET_MPLS_TC SET_MPLS_TTL
  3(eth0): addr:50:54:00:00:00:01
      config:     0
      state:      0
@@ -246,6 +245,66 @@ received OFPT_FEATURES_REPLY with incorrect length 144 (must be exactly 32 bytes
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_FEATURES_REPLY - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \
+00 00 01 00 ff 00 00 00 00 00 01 77 00 00 06 ff \
+ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
+62 72 30 0a 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 01 86 a0 00 01 86 a0 \
+00 00 00 03 00 00 00 00 50 54 00 00 00 01 00 00 \
+65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 20 08 00 00 28 0f \
+00 00 28 0f 00 00 00 00 00 01 86 a0 00 01 86 a0 \
+"], [0], [dnl
+OFPT_FEATURES_REPLY (OF1.2) (xid=0x1): dpid:0000505400000001
+n_tables:255, n_buffers:256
+capabilities: FLOW_STATS TABLE_STATS PORT_STATS IP_REASM QUEUE_STATS PORT_BLOCKED
+ 3(eth0): addr:50:54:00:00:00:01
+     config:     0
+     state:      0
+     current:    100MB-FD AUTO_NEG
+     advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+     supported:  10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+     speed: 100 Mbps now, 100 Mbps max
+ LOCAL(br0): addr:50:54:00:00:00:01
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 100 Mbps now, 100 Mbps max
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_FEATURES_REPLY cut off mid-port - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \
+00 00 01 00 ff 00 00 00 00 00 01 77 00 00 06 ff \
+ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
+62 72 30 0a 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 01 86 a0 00 01 86 a0 \
+00 00 00 03 00 00 00 00 50 54 00 00 00 01 00 00 \
+65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 20 08 00 00 28 0f \
+"], [0], [dnl
+OFPT_FEATURES_REPLY (OF1.2) (xid=0x1):
+(***truncated to 144 bytes from 160***)
+00000000  03 06 00 a0 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....|
+00000010  00 00 01 00 ff 00 00 00-00 00 01 77 00 00 06 ff |...........w....|
+00000020  ff ff ff fe 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......|
+00000030  62 72 30 0a 00 00 00 00-00 00 00 00 00 00 00 00 |br0.............|
+00000040  00 00 00 01 00 00 00 01-00 00 00 00 00 00 00 00 |................|
+00000050  00 00 00 00 00 00 00 00-00 01 86 a0 00 01 86 a0 |................|
+00000060  00 00 00 03 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......|
+00000070  65 74 68 30 00 00 00 00-00 00 00 00 00 00 00 00 |eth0............|
+00000080  00 00 00 00 00 00 00 00-00 00 20 08 00 00 28 0f |.......... ...@{:@.|
+], [stderr])
+AT_CHECK([sed 's/.*|//' stderr], [0], [dnl
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_GET_CONFIG_REQUEST])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print '0107000800000001'], [0], [dnl
@@ -268,7 +327,7 @@ OFPT_GET_CONFIG_REPLY (xid=0x3): frags=reassemble miss_send_len=255
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPT_PACKET_IN])
+AT_SETUP([OFPT_PACKET_IN - OF1.0])
 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 \
@@ -278,11 +337,25 @@ c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \
 50 00 02 00 26 e8 00 00 00 00 00 00 00 00 \
 "], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:06) type:0800 proto:6 tos:0 ttl:64 ip(192.168.0.1->192.168.0.2) port(10031->0) tcp_csum:26e8
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:06) type:0800 proto:6 tos:0 ttl:64 ip(192.168.0.1->192.168.0.2) port(10031->0) tcp_csum:26e8
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPT_FLOW_REMOVED])
+AT_SETUP([OFPT_PACKET_IN - OF1.2])
+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 \
+"], [0], [dnl
+OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:23:20:83:c1:5f->ff:ff:ff:ff:ff:ff) type:8035 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_FLOW_REMOVED - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 0b 00 58 00 00 00 00 00 00 00 00 00 03 50 54 \
@@ -296,6 +369,17 @@ OFPT_FLOW_REMOVED (xid=0x0): priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_FLOW_REMOVED - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 0b 00 40 00 00 00 00 fe dc ba 98 76 54 32 10 \
+80 00 01 05 00 00 00 01 00 98 96 80 00 3c 00 78 \
+00 00 00 00 00 12 d6 87 00 00 00 00 6f 68 ba 66 \
+00 01 00 0a 80 00 0c 02 10 09 00 00 00 00 00 00"], [0], [dnl
+OFPT_FLOW_REMOVED (OF1.2) (xid=0x0): dl_vlan=9 reason=hard cookie:0xfedcba9876543210 duration1.01s idle60 hard120 pkts1234567 bytes1869134438
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_PORT_STATUS - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -333,7 +417,7 @@ OFPT_PORT_STATUS (OF1.1) (xid=0x0): MOD: 3(eth0): addr:50:54:00:00:00:01
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPT_PACKET_OUT])
+AT_SETUP([OFPT_PACKET_OUT - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 0d 00 54 00 00 00 00 00 00 01 14 00 01 00 08 \
@@ -347,8 +431,19 @@ OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 buffer=0x00000114
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PACKET_OUT - OF1.1])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 0d 00 28 88 58 df c5 ff ff ff 00 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 \
+"], [0], [dnl
+OFPT_PACKET_OUT (OF1.2) (xid=0x8858dfc5): in_port=LOCAL actions=FLOOD buffer=0xffffff00
+])
+AT_CLEANUP
+
 # The flow is formatted with cls_rule_format() for the low-verbosity case.
-AT_SETUP([OFPT_FLOW_MOD - low verbosity])
+AT_SETUP([OFPT_FLOW_MOD - OF1.0 - low verbosity])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
 01 0e 00 50 00 00 00 00 00 00 00 00 00 01 50 54 \
@@ -357,7 +452,7 @@ AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 \
 00 00 01 0e 00 00 00 00 00 00 00 08 00 03 00 00 \
 " 2], [0], [dnl
-OFPT_FLOW_MOD (xid=0x0): ADD priority=65535,arp,in_port=1,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,arp_op=2 idle:5 buf:0x10e actions=output:3
+OFPT_FLOW_MOD (xid=0x0): ADD priority=65535,arp,in_port=1,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,arp_op=2 idle:5 buf:0x10e out_port:0 actions=output:3
 ], [dnl
 ofp_util|INFO|normalization changed ofp_match, details:
 ofp_util|INFO| pre: priority=65535,arp,in_port=1,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,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0
@@ -365,8 +460,28 @@ ofp_util|INFO|post: priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00
 ])
 AT_CLEANUP
 
-# The flow is formatted with ofp_match_to_string() for the high-verbosity case.
-AT_SETUP([OFPT_FLOW_MOD - high verbosity])
+# The flow is formatted with cls_rule_format() for the low-verbosity case.
+AT_SETUP([OFPT_FLOW_MOD - OF1.2 - low verbosity])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 ff 00 00 00 00 00 ff ff \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \
+50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \
+80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \
+00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \
+00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:255 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2 actions=output:3
+], [dnl
+])
+AT_CLEANUP
+
+# The flow is formatted with ofp10_match_to_string() for the
+# high-verbosity case.
+AT_SETUP([OFPT_FLOW_MOD - OF1.0 - high verbosity])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
 01 0e 00 50 00 00 00 00 00 00 00 00 00 01 50 54 \
@@ -375,7 +490,7 @@ AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 \
 00 00 01 0e 00 00 00 00 00 00 00 08 00 03 00 00 \
 " 3], [0], [dnl
-OFPT_FLOW_MOD (xid=0x0): ADD arp,in_port=1,dl_vlan=65535,dl_vlan_pcp=0,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,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 idle:5 pri:65535 buf:0x10e actions=output:3
+OFPT_FLOW_MOD (xid=0x0): ADD arp,in_port=1,dl_vlan=65535,dl_vlan_pcp=0,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,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 idle:5 pri:65535 buf:0x10e out_port:0 actions=output:3
 ], [dnl
 ofp_util|INFO|normalization changed ofp_match, details:
 ofp_util|INFO| pre: priority=65535,arp,in_port=1,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,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0
@@ -383,6 +498,25 @@ ofp_util|INFO|post: priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00
 ])
 AT_CLEANUP
 
+# The flow is formatted with cls_rule_format() for the low-verbosity case.
+AT_SETUP([OFPT_FLOW_MOD - OF1.2 - low verbosity])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 ff 00 00 00 00 00 ff ff \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \
+50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \
+80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \
+00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \
+00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:255 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2 actions=output:3
+], [dnl
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_PORT_MOD - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -410,6 +544,20 @@ OFPT_PORT_MOD (OF1.1) (xid=0x3):port: 3: addr:50:54:00:00:00:01
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_PORT_MOD - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 10 00 28 00 00 00 03 00 00 00 03 00 00 00 00 \
+50 54 00 00 00 01 00 00 00 00 00 01 00 00 00 01 \
+00 00 00 00 00 00 00 00 \
+" 3], [0], [dnl
+OFPT_PORT_MOD (OF1.2) (xid=0x3):port: 3: addr:50:54:00:00:00:01
+     config: PORT_DOWN
+     mask:   PORT_DOWN
+     advertise: UNCHANGED
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_DESC request])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100000000"], [0], [dnl
@@ -421,8 +569,8 @@ AT_SETUP([OFPST_DESC reply])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 11 04 2c 00 00 00 01 00 00 00 00 4e 69 63 69 \
-72 61 20 4e 65 74 77 6f 72 6b 73 2c 20 49 6e 63 \
-2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+72 61 2c 20 49 6e 63 2e 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 00 00 00 00 \
@@ -489,7 +637,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
 00 00 00 00 00 00 00 00 00 00 00 00 \
 "], [0], [dnl
 OFPST_DESC reply (xid=0x1):
-Manufacturer: Nicira Networks, Inc.
+Manufacturer: Nicira, Inc.
 Hardware: Open vSwitch
 Software: 1.1.0pre2
 Serial Num: None
@@ -497,7 +645,7 @@ DP Description: None
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPST_FLOW request])
+AT_SETUP([OFPST_FLOW request - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 10 00 38 00 00 00 04 00 01 00 00 00 38 20 ff \
@@ -509,7 +657,19 @@ OFPST_FLOW request (xid=0x4): @&t@
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPST_FLOW reply])
+AT_SETUP([OFPST_FLOW request - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 12 00 38 00 00 00 02 00 01 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW request (OF1.2) (xid=0x2): @&t@
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_FLOW reply - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 11 01 e4 00 00 00 04 00 01 00 00 00 60 00 00 \
@@ -545,15 +705,50 @@ c0 a8 00 02 00 08 00 00 00 00 00 09 05 b8 d8 00 \
 00 00 00 00 00 00 00 00 \
 "], [0], [dnl
 OFPST_FLOW reply (xid=0x4):
- cookie=0x0, duration=4.2s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,arp,in_port=3,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,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:1
- cookie=0x0, duration=8.9s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5,priority=65535,icmp,in_port=1,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,icmp_type=0,icmp_code=0 actions=output:3
- cookie=0x0, duration=4.28s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,arp,in_port=1,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,arp_op=1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:3
- cookie=0x0, duration=9.096s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5,icmp,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,icmp_type=8,icmp_code=0 actions=output:1
+ cookie=0x0, duration=4.2s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=3,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,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:1
+ cookie=0x0, duration=8.9s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5, priority=65535,icmp,in_port=1,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,icmp_type=0,icmp_code=0 actions=output:3
+ cookie=0x0, duration=4.28s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=1,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,arp_op=1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:3
+ cookie=0x0, duration=9.096s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5, icmp,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,icmp_type=8,icmp_code=0 actions=output:1
  cookie=0x0, duration=0s, table=2, n_packets=0, n_bytes=0, actions=drop
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPST_AGGREGATE request])
+AT_SETUP([OFPST_FLOW reply - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 13 01 78 00 00 00 02 00 01 00 00 00 00 00 00 \
+00 78 00 00 00 00 00 03 01 5e f3 c0 80 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 00 00 00 00 00 00 62 \
+00 01 00 2d 80 00 00 04 00 00 00 02 80 00 06 06 \
+ca da ad d6 0d 37 80 00 0a 02 08 00 80 00 10 01 \
+00 80 00 04 08 00 00 00 00 00 00 00 00 00 00 00 \
+00 04 00 18 00 00 00 00 00 00 00 10 00 00 00 02 \
+05 dc 00 00 00 00 00 00 00 78 00 00 00 00 00 04 \
+20 7c 0a 40 80 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 02 \
+00 00 00 00 00 00 00 8c 00 01 00 2d 80 00 00 04 \
+00 00 00 02 80 00 06 06 52 54 00 c3 00 89 80 00 \
+0a 02 08 00 80 00 10 01 00 80 00 04 08 00 00 00 \
+00 00 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 00 00 00 02 05 dc 00 00 00 00 00 00 \
+00 78 00 00 00 00 00 04 20 a9 d1 00 80 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 00 00 00 00 00 00 2a \
+00 01 00 2d 80 00 00 04 00 00 00 02 80 00 06 06 \
+52 54 00 97 00 69 80 00 0a 02 08 00 80 00 10 01 \
+00 80 00 04 08 00 00 00 00 00 00 00 00 00 00 00 \
+00 04 00 18 00 00 00 00 00 00 00 10 00 00 00 02 \
+05 dc 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW reply (OF1.2) (xid=0x2):
+ cookie=0x0, duration=3.023s, table=0, n_packets=1, n_bytes=98, ip,metadata=0,in_port=2,dl_dst=ca:da:ad:d6:0d:37,nw_tos=0 actions=output:2
+ cookie=0x0, duration=4.545s, table=0, n_packets=2, n_bytes=140, ip,metadata=0,in_port=2,dl_dst=52:54:00:c3:00:89,nw_tos=0 actions=output:2
+ cookie=0x0, duration=4.548s, table=0, n_packets=1, n_bytes=42, ip,metadata=0,in_port=2,dl_dst=52:54:00:97:00:69,nw_tos=0 actions=output:2
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_AGGREGATE request - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 10 00 38 00 00 00 04 00 02 00 00 00 38 20 ff \
@@ -565,7 +760,19 @@ OFPST_AGGREGATE request (xid=0x4): @&t@
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPST_AGGREGATE reply])
+AT_SETUP([OFPST_AGGREGATE request - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 12 00 38 00 00 00 02 00 02 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE request (OF1.2) (xid=0x2): @&t@
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_AGGREGATE reply - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 11 00 24 00 00 00 04 00 02 00 00 00 00 00 00 \
@@ -576,6 +783,17 @@ OFPST_AGGREGATE reply (xid=0x4): packet_count=386 byte_count=37752 flow_count=4
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_AGGREGATE reply - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 13 00 28 00 00 00 02 00 02 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 79 00 00 00 00 00 00 4b 4f \
+00 00 00 03 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE reply (OF1.2) (xid=0x2): packet_count=121 byte_count=19279 flow_count=3
+])
+AT_CLEANUP
+
 AT_SETUP([OFPST_TABLE request])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100030000"], [0], [dnl
@@ -583,7 +801,7 @@ OFPST_TABLE request (xid=0x1):
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPST_TABLE reply])
+AT_SETUP([OFPST_TABLE reply - OF1.0])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 11 00 4c 00 00 00 01 00 03 00 00 00 00 00 00 \
@@ -598,6 +816,61 @@ OFPST_TABLE reply (xid=0x1): 1 tables
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPST_TABLE reply - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+(mid="wild=0xfffffffff, max=1000000,"
+ tail="
+               match=0xfffffffff, instructions=0x00000007, config=0x00000000
+               write_actions=0x00000000, apply_actions=0x00000000
+               write_setfields=0x0000000fffffffff
+               apply_setfields=0x0000000fffffffff
+               metadata_match=0x0000000000000000
+               metadata_write=0x0000000000000000"
+ echo "OFPST_TABLE reply (OF1.2) (xid=0x2): 255 tables
+  0: classifier: $mid active=1
+               lookup=74614, matched=106024$tail"
+ x=1
+ while test $x -lt 254; do
+   printf "  %d: %-8s: $mid active=0
+               lookup=0, matched=0$tail
+" $x table$x
+   x=`expr $x + 1`
+ done
+ echo "  254: table254: $mid active=2
+               lookup=0, matched=0$tail") > expout
+
+(pad32="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ pad7="00 00 00 00 00 00 00 "
+ mid="00 00 00 0f ff ff ff ff \
+00 00 00 0f ff ff ff ff 00 00 00 00 00 00 00 00 \
+00 00 00 0f ff ff ff ff 00 00 00 0f ff ff ff ff \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 07 00 00 00 00 00 0f 42 40 "
+ tail="00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
+
+ echo -n "03 13 7f 90 00 00 00 02 00 03 00 00 00 00 00 00 "
+
+ x=0
+ printf "%02x $pad7" $x
+ printf "%s$pad32" "classifier" | od -A n -t x1 -v -N 32 | tr '\n' ' '
+ echo -n "$mid 00 00 00 01  "
+ echo -n "00 00 00 00 00 01 23 76 00 00 00 00 00 01 9e 28 "
+
+ x=1
+ while test $x -lt 254; do
+   printf "%02x $pad7" $x
+   printf "%s$pad32" "table$x" | od -A n -t x1 -v -N 32 | tr '\n' ' '
+   echo -n "$mid 00 00 00 00 $tail "
+   x=`expr $x + 1`
+ done
+
+ x=254
+ printf "%02x $pad7" $x
+ printf "%s$pad32" "table$x" | od -A n -t x1 -v -N 32 | tr '\n' ' '
+ echo -n "$mid 00 00 00 02 $tail") > in
+AT_CHECK([ovs-ofctl ofp-print "$(cat in)"], [0], [expout])
+AT_CLEANUP
+
 AT_SETUP([OFPST_PORT request])
 AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -688,20 +961,74 @@ OFPST_QUEUE reply (xid=0x1): 6 queues
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPT_BARRIER_REQUEST])
+AT_SETUP([OFPST_PORT_DESC request - OF1.0])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "0110000c00000001000d0000"], [0], [dnl
+OFPST_PORT_DESC request (xid=0x1):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_PORT_DESC reply - OF1.0])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 11 00 3c 00 00 00 00 00 0d 00 00 00 03 50 54 \
+00 00 00 01 65 74 68 30 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 01 00 00 00 01 00 00 02 08 \
+00 00 02 8f 00 00 02 8f 00 00 00 00 \
+"], [0], [dnl
+OFPST_PORT_DESC reply (xid=0x0):
+ 3(eth0): addr:50:54:00:00:00:01
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     current:    100MB-FD AUTO_NEG
+     advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+     supported:  10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+     speed: 100 Mbps now, 100 Mbps max
+])
+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
 OFPT_BARRIER_REQUEST (xid=0x1):
 ])
 AT_CLEANUP
 
-AT_SETUP([OFPT_BARRIER_REPLY])
+AT_SETUP([OFPT_BARRIER_REQUEST - OF1.1])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '02 14 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REQUEST (OF1.1) (xid=0x1):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BARRIER_REQUEST - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '03 14 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REQUEST (OF1.2) (xid=0x1):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BARRIER_REPLY - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print '01 13 00 08 00 00 00 01'], [0], [dnl
 OFPT_BARRIER_REPLY (xid=0x1):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_BARRIER_REPLY] - OF1.1)
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '02 15 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REPLY (OF1.1) (xid=0x1):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BARRIER_REPLY] - OF1.2)
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '03 15 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REPLY (OF1.2) (xid=0x1):
+])
+AT_CLEANUP
+
 AT_SETUP([NXT_ROLE_REQUEST])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -735,20 +1062,21 @@ AT_CLEANUP
 AT_SETUP([NXT_PACKET_IN])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
-01 04 00 aa 00 00 00 00 00 00 23 20 00 00 00 11 \
+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 3a 00 00 00 00 00 00 00 00 00 02 00 01 00 01 \
+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 00 00 00 00 00 00 00 00 82 82 82 82 82 82 \
+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 \
 "], [0], [dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 metadata=0x5a5a5a5a5a5a5a5a reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
 ])
 AT_CLEANUP
 
@@ -782,6 +1110,34 @@ NXT_SET_CONTROLLER_ID (xid=0x3): id=123
 ])
 AT_CLEANUP
 
+AT_SETUP([NXT_FLOW_MONITOR_CANCEL])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 14 00 00 00 03 00 00 23 20 00 00 00 15 \
+01 02 30 40 \
+"], [0], [dnl
+NXT_FLOW_MONITOR_CANCEL (xid=0x3): id=16920640
+])
+AT_CLEANUP
+
+AT_SETUP([NXT_FLOW_MONITOR_PAUSED])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 16 \
+"], [0], [dnl
+NXT_FLOW_MONITOR_PAUSED (xid=0x3):
+])
+AT_CLEANUP
+
+AT_SETUP([NXT_FLOW_MONITOR_RESUMED])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 17 \
+"], [0], [dnl
+NXT_FLOW_MONITOR_RESUMED (xid=0x3):
+])
+AT_CLEANUP
+
 AT_SETUP([NXT_SET_FLOW_FORMAT])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -798,27 +1154,28 @@ AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 04 00 60 00 00 00 02 00 00 23 20 00 00 00 0d \
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 \
-ff ff ff ff ff ff 00 00 00 14 00 00 00 00 00 00 \
+ff ff ff ff 00 10 00 00 00 14 00 00 00 00 00 00 \
 00 01 20 08 00 00 00 00 00 00 01 c8 00 01 00 04 \
 00 00 00 7b 00 00 00 00 ff ff 00 18 00 00 23 20 \
 00 07 00 1f 00 01 00 04 00 00 00 00 00 00 00 05 \
 " 2], [0], [dnl
-NXT_FLOW_MOD (xid=0x2): ADD reg0=0x7b,tun_id=0x1c8 actions=load:0x5->NXM_NX_REG0[[]]
+NXT_FLOW_MOD (xid=0x2): ADD reg0=0x7b,tun_id=0x1c8 out_port:16 actions=load:0x5->NXM_NX_REG0[[]]
 ])
 AT_CLEANUP
 
-# The flow is formatted with ofp_match_to_string() for the low-verbosity case.
+# The flow is formatted with ofp10_match_to_string() for the
+# low-verbosity case.
 AT_SETUP([NXT_FLOW_MOD, high verbosity])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
 01 04 00 60 00 00 00 02 00 00 23 20 00 00 00 0d \
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 \
-ff ff ff ff ff ff 00 00 00 14 00 00 00 00 00 00 \
+ff ff ff ff 01 00 00 00 00 14 00 00 00 00 00 00 \
 00 01 20 08 00 00 00 00 00 00 01 c8 00 01 00 04 \
 00 00 00 7b 00 00 00 00 ff ff 00 18 00 00 23 20 \
 00 07 00 1f 00 01 00 04 00 00 00 00 00 00 00 05 \
 " 3], [0], [dnl
-NXT_FLOW_MOD (xid=0x2): ADD NXM_NX_TUN_ID(00000000000001c8), NXM_NX_REG0(0000007b) actions=load:0x5->NXM_NX_REG0[[]]
+NXT_FLOW_MOD (xid=0x2): ADD NXM_NX_TUN_ID(00000000000001c8), NXM_NX_REG0(0000007b) out_port:256 actions=load:0x5->NXM_NX_REG0[[]]
 ])
 AT_CLEANUP
 
@@ -993,20 +1350,20 @@ ff ff 00 18 00 00 23 20 00 07 00 1f 00 01 00 04 \
 00 00 00 00 00 00 00 64 00 00 00 00 00 00 19 00 \
 "], [0],
 [[NXST_FLOW reply (xid=0x4):
- cookie=0x0, duration=1.048s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=3,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,tp_src=2535,tp_dst=0 actions=output:1
- cookie=0x0, duration=3.84s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,idle_age=2,priority=65535,tcp,in_port=3,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,tp_src=2532,tp_dst=0 actions=output:1
- cookie=0x0, duration=2.872s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,idle_age=4,priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2533 actions=output:3
- cookie=0x0, duration=4.756s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,idle_age=0,priority=65535,tcp,in_port=3,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,tp_src=2531,tp_dst=0 actions=output:1
- cookie=0x0, duration=2.88s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,hard_timeout=10,idle_age=2,priority=65535,tcp,in_port=3,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,tp_src=2533,tp_dst=0 actions=output:1
- cookie=0x0, duration=5.672s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=3,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,tp_src=2530,tp_dst=0 actions=output:1
- cookie=0x0, duration=1.04s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2535 actions=output:3
- cookie=0x0, duration=1.952s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2534 actions=output:3
- cookie=0x0, duration=4.668s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2531 actions=output:3
- cookie=0x0, duration=3.752s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2532 actions=output:3
- cookie=0x0, duration=0.172s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=3,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,tp_src=2536,tp_dst=0 actions=output:1
- cookie=0x0, duration=5.624s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2530 actions=output:3
- cookie=0x0, duration=0.08s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2536 actions=output:3
- cookie=0x0, duration=1.96s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=3,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,tp_src=2534,tp_dst=0 actions=output:1
+ cookie=0x0, duration=1.048s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,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,tp_src=2535,tp_dst=0 actions=output:1
+ cookie=0x0, duration=3.84s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=2, priority=65535,tcp,in_port=3,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,tp_src=2532,tp_dst=0 actions=output:1
+ cookie=0x0, duration=2.872s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=4, priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2533 actions=output:3
+ cookie=0x0, duration=4.756s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=0, priority=65535,tcp,in_port=3,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,tp_src=2531,tp_dst=0 actions=output:1
+ cookie=0x0, duration=2.88s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, hard_timeout=10, idle_age=2, priority=65535,tcp,in_port=3,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,tp_src=2533,tp_dst=0 actions=output:1
+ cookie=0x0, duration=5.672s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,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,tp_src=2530,tp_dst=0 actions=output:1
+ cookie=0x0, duration=1.04s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2535 actions=output:3
+ cookie=0x0, duration=1.952s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2534 actions=output:3
+ cookie=0x0, duration=4.668s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2531 actions=output:3
+ cookie=0x0, duration=3.752s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2532 actions=output:3
+ cookie=0x0, duration=0.172s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,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,tp_src=2536,tp_dst=0 actions=output:1
+ cookie=0x0, duration=5.624s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2530 actions=output:3
+ cookie=0x0, duration=0.08s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,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,tp_src=0,tp_dst=2536 actions=output:3
+ cookie=0x0, duration=1.96s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,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,tp_src=2534,tp_dst=0 actions=output:1
  cookie=0x0, duration=228.78s, table=0, n_packets=0, n_bytes=0, reg0=0x7b,tun_id=0x1c8 actions=load:0x5->NXM_NX_REG0[]
  cookie=0x0, duration=3600.0005s, table=1, n_packets=100, n_bytes=6400, actions=drop
 ]])
@@ -1032,3 +1389,30 @@ AT_CHECK([ovs-ofctl ofp-print "\
 NXST_AGGREGATE reply (xid=0x4): packet_count=7 byte_count=420 flow_count=7
 ])
 AT_CLEANUP
+
+AT_SETUP([NXST_FLOW_MONITOR request])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 10 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
+00 00 40 00 00 3f ff fe 00 00 01 00 00 00 00 00 \
+00 00 20 00 00 04 ff ff 00 06 02 00 00 00 00 00 00 00 00 02 00 01 00 00 \
+"], [0], [dnl
+NXST_FLOW_MONITOR request (xid=0x4):
+ id=16384 flags=initial,add,delete,modify,actions,own out_port=LOCAL table=1
+ id=8192 flags=delete table=2 in_port=1
+])
+AT_CLEANUP
+
+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 00 00 02 00 01 00 00 \
+00 08 00 03 00 01 86 a0 \
+"], [0], [dnl
+NXST_FLOW_MONITOR reply (xid=0x4):
+ event=DELETED reason=eviction table=1 idle_timeout=5 hard_timeout=16 cookie=0x123456789abcdef0 in_port=1
+ event=ABBREV xid=0x186a0
+])
+AT_CLEANUP
index 2c5df96..cc5d770 100644 (file)
@@ -11,7 +11,7 @@ table=1 in_port=2 priority=1500 icmp actions=output(17),resubmit(,2)
 table=1 in_port=3 priority=1500 icmp actions=output(14),resubmit(,2)
 ])
 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 br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 10,11,12,13,14,15,16,17,18,19,20,21
 ])
@@ -78,9 +78,10 @@ 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([tail -2 stdout], [0],
+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 not cachable.
+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([tail -1 stdout], [0],
@@ -91,12 +92,13 @@ 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_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --pidfile 2> ofctl_monitor.log])
+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])
 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 tun_id=0x0 reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via invalid_ttl) data_len=42 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:1 tos:0 ttl:1 ip(192.168.0.1->192.168.0.2)
+NXT_PACKET_IN (xid=0x0): table_id=1 total_len=42 in_port=1 (via invalid_ttl) data_len=42 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:1 tos:0 ttl:1 ip(192.168.0.1->192.168.0.2)
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -252,7 +254,7 @@ cookie=0x9 table=7 in_port=86 actions=mod_tp_dst:86,controller,controller
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 
 dnl Flow miss.
-AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
 
 for i in 1 2 3 ; do
     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=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'
@@ -261,17 +263,17 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
 ])
 
 dnl Singleton controller action.
-AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
 
 for i in 1 2 3 ; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,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=10)'
@@ -280,17 +282,17 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
 ])
 
 dnl Modified controller action.
-AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
 
 for i in 1 2 3 ; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=30:33:33:33:33:33,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=10)'
@@ -299,17 +301,17 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
 ])
 
 dnl Checksum TCP.
-AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --no-chdir --pidfile 2> ofctl_monitor.log])
 
 for i in 1 ; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=20:22:22:22:22:22,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=11)'
@@ -317,36 +319,36 @@ done
 
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
-NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 tun_id=0x0 reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 reg0=0x1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) tcp_csum:1a03
+NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) tcp_csum:1a03
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) tcp_csum:3205
+NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) tcp_csum:3205
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) tcp_csum:31b8
+NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) tcp_csum:31b8
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
 ])
 
 dnl Checksum UDP.
-AT_CHECK([ovs-ofctl monitor br0 65534 --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
 
 for i in 1 ; do
     ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 20 22 22 22 22 22 08 00 45 00 00 1C 00 00 00 00 00 11 00 00 C0 A8 00 01 C0 A8 00 02 00 08 00 0B 00 00 12 34 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00'
@@ -354,32 +356,32 @@ done
 
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
-NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 tun_id=0x0 reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 reg0=0x1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) udp_csum:2c37
+NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) udp_csum:2c37
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) udp_csum:4439
+NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) udp_csum:4439
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) udp_csum:43ec
+NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) udp_csum:43ec
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
 dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
 ])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -947,7 +949,9 @@ OVS_VSWITCHD_START(
      add-id-to-interface=false], [<0>
 ])
 
-AT_CHECK([test-netflow --detach --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])AT_CAPTURE_FILE([netflow.log])
+ON_EXIT([kill `cat test-netflow.pid`])
+AT_CHECK([test-netflow --detach --no-chdir --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])
+AT_CAPTURE_FILE([netflow.log])
 
 for delay in 1000 30000; do
     ovs-appctl netdev-dummy/receive p1 '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=64,frag=no),icmp(type=8,code=0)'
@@ -956,12 +960,13 @@ for delay in 1000 30000; do
     ovs-appctl time/warp $delay
 done
 
+sleep 1
 OVS_VSWITCHD_STOP
 ovs-appctl -t test-netflow exit
 
 AT_CHECK([[sed -e 's/, uptime [0-9]*//
 s/, now [0-9.]*//
-s/time \([0-9]*\)\.\.\.\1\b/time <moment>/
+s/time \([0-9]*\)\.\.\.\1$/time <moment>/
 s/time [0-9]*\.\.\.[0-9]*/time <range>/
 ' netflow.log]], [0],
   [header: v5, seq 0, engine 2,1
@@ -989,7 +994,8 @@ OVS_VSWITCHD_START(
      add-id-to-interface=false], [<0>
 ])
 
-AT_CHECK([test-netflow --detach --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])AT_CAPTURE_FILE([netflow.log])
+ON_EXIT([kill `test-netflow.pid`])
+AT_CHECK([test-netflow --detach --no-chdir --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])AT_CAPTURE_FILE([netflow.log])
 
 AT_CHECK([ovs-appctl time/stop])
 n=1
@@ -1004,6 +1010,7 @@ done
 
 ovs-appctl time/warp 10000
 
+sleep 1
 OVS_VSWITCHD_STOP
 ovs-appctl -t test-netflow exit
 
index a7ac388..52f19fc 100644 (file)
@@ -10,8 +10,8 @@ s/ cookie=0x0,//
 s/ table=0,//
 s/ n_packets=0,//
 s/ n_bytes=0,//
-s/idle_age=[0-9]*,//
-s/hard_age=[0-9]*,//
+s/ idle_age=[0-9]*,//
+s/ hard_age=[0-9]*,//
 '
 }]
 m4_divert_pop([PREPARE_TESTS])
@@ -31,27 +31,30 @@ m4_define([TESTABLE_LOG], [-vPATTERN:ANY:'%c|%p|%m'])
 m4_define([OVS_VSWITCHD_START],
   [OVS_RUNDIR=`pwd`; export OVS_RUNDIR
    OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+   OVS_DBDIR=`pwd`; export OVS_DBDIR
    OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
-   trap 'kill `cat ovsdb-server.pid ovs-vswitchd.pid`' 0
+   ON_EXIT([kill `cat ovsdb-server.pid ovs-vswitchd.pid`])
 
    dnl Create database.
-   mkdir openvswitch
-   touch openvswitch/.conf.db.~lock~
-   AT_CHECK([ovsdb-tool create openvswitch/conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema])
+   touch .conf.db.~lock~
+   AT_CHECK([ovsdb-tool create conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema])
 
    dnl Start ovsdb-server.
-   AT_CHECK([ovsdb-server --detach --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock], [0], [], [stderr])
-   AT_CHECK([[sed < stderr '/vlog|INFO|opened log file/d']])
+   AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock], [0], [], [stderr])
+   AT_CHECK([[sed < stderr '
+/vlog|INFO|opened log file/d
+/ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']])
    AT_CAPTURE_FILE([ovsdb-server.log])
 
    dnl Initialize database.
    AT_CHECK([ovs-vsctl --no-wait init])
 
    dnl Start ovs-vswitchd.
-   AT_CHECK([ovs-vswitchd --detach --pidfile --enable-dummy --disable-system --log-file], [0], [], [stderr])
+   AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --enable-dummy --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
+/vswitchd|INFO|ovs-vswitchd (Open vSwitch)/d
 /reconnect|INFO|/d
 /ofproto|INFO|using datapath ID/d
 /ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
@@ -62,5 +65,4 @@ m4_define([OVS_VSWITCHD_START],
 
 m4_define([OVS_VSWITCHD_STOP],
   [AT_CHECK([ovs-appctl -t ovs-vswitchd exit])
-   AT_CHECK([ovs-appctl -t ovsdb-server exit])
-   trap '' 0])
+   AT_CHECK([ovs-appctl -t ovsdb-server exit])])
index 0f0fd1f..a055851 100644 (file)
@@ -2,13 +2,13 @@ AT_BANNER([ofproto])
 
 AT_SETUP([ofproto - echo request])
 OVS_VSWITCHD_START
-AT_CHECK([ovs-ofctl -vANY:ANY:WARN probe br0])
+AT_CHECK([ovs-ofctl -vwarn probe br0])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([ofproto - feature request, config request])
 OVS_VSWITCHD_START
-AT_CHECK([ovs-ofctl -vANY:ANY:WARN show br0], [0], [stdout])
+AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout])
 AT_CHECK([STRIP_XIDS stdout], [0], [dnl
 OFPT_FEATURES_REPLY: dpid:fedcba9876543210
 n_tables:255, n_buffers:256
@@ -27,7 +27,7 @@ dnl This is really bare-bones.
 dnl It at least checks request and reply serialization and deserialization.
 AT_SETUP([ofproto - port stats])
 OVS_VSWITCHD_START
-AT_CHECK([ovs-ofctl -vANY:ANY:WARN dump-ports br0], [0], [stdout])
+AT_CHECK([ovs-ofctl -vwarn dump-ports br0], [0], [stdout])
 AT_CHECK([STRIP_XIDS stdout], [0], [dnl
 OFPST_PORT reply: 1 ports
   port 65534: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
@@ -36,14 +36,37 @@ OFPST_PORT reply: 1 ports
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - port-desc stats])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -vwarn dump-ports-desc br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_PORT_DESC reply:
+ LOCAL(br0): addr:aa:55:aa:55:00:00
+     config:     PORT_DOWN
+     state:      LINK_DOWN
+     speed: 100 Mbps now, 100 Mbps max
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 dnl This is really bare-bones.
 dnl It at least checks request and reply serialization and deserialization.
 AT_SETUP([ofproto - queue stats])
 OVS_VSWITCHD_START
-AT_CHECK([ovs-ofctl -vANY:ANY:WARN queue-stats br0], [0], [stdout])
+AT_CHECK([ovs-ofctl -vwarn queue-stats br0], [0], [stdout])
 AT_CHECK([STRIP_XIDS stdout], [0], [dnl
 OFPST_QUEUE reply: 0 queues
 ])
+AT_CHECK([ovs-ofctl -vwarn queue-stats br0 ALL 5], [0],
+  [OFPT_ERROR (xid=0x2): OFPQOFC_BAD_QUEUE
+OFPST_QUEUE request (xid=0x2):port=ALL queue=5
+])
+AT_CHECK([ovs-ofctl -vwarn queue-stats br0 10], [0],
+  [OFPT_ERROR (xid=0x2): OFPQOFC_BAD_PORT
+OFPST_QUEUE request (xid=0x2):port=10 queue=ALL
+])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
@@ -53,12 +76,19 @@ for command_config_state in \
     'up 0 0' \
     'noflood NO_FLOOD 0' \
     'down PORT_DOWN,NO_FLOOD LINK_DOWN' \
-    'flood PORT_DOWN LINK_DOWN'
+    'flood PORT_DOWN LINK_DOWN' \
+    'no-receive PORT_DOWN,NO_RECV LINK_DOWN' \
+    'no-forward PORT_DOWN,NO_RECV,NO_FWD LINK_DOWN' \
+    'no-packet-in PORT_DOWN,NO_RECV,NO_FWD,NO_PACKET_IN LINK_DOWN' \
+    'forward PORT_DOWN,NO_RECV,NO_PACKET_IN LINK_DOWN' \
+    'packet-in PORT_DOWN,NO_RECV LINK_DOWN' \
+    'up NO_RECV 0' \
+    'receive 0 0'
 do
     set $command_config_state
     command=$[1] config=`echo $[2] | sed 's/,/ /g'` state=$[3]
-    AT_CHECK([ovs-ofctl -vANY:ANY:WARN mod-port br0 br0 $command])
-    AT_CHECK([ovs-ofctl -vANY:ANY:WARN show br0], [0], [stdout])
+    AT_CHECK([ovs-ofctl -vwarn mod-port br0 br0 $command])
+    AT_CHECK([ovs-ofctl -vwarn show br0], [0], [stdout])
     AT_CHECK_UNQUOTED([STRIP_XIDS stdout], [0], [dnl
 OFPT_FEATURES_REPLY: dpid:fedcba9876543210
 n_tables:255, n_buffers:256
@@ -132,11 +162,11 @@ NXST_FLOW reply:
 AT_CHECK([ovs-ofctl dump-aggregate br0 table=0 | STRIP_XIDS], [0], [dnl
 NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=3
 ])
-AT_CHECK([ovs-ofctl dump-flows br0 cookie=0x3 | ofctl_strip | sort], [0], [dnl
+AT_CHECK([ovs-ofctl dump-flows br0 cookie=0x3/-1 | ofctl_strip | sort], [0], [dnl
  cookie=0x3, in_port=3 actions=output:0
 NXST_FLOW reply:
 ])
-AT_CHECK([ovs-ofctl dump-aggregate br0 cookie=0x3 | STRIP_XIDS], [0], [dnl
+AT_CHECK([ovs-ofctl dump-aggregate br0 cookie=0x3/-1 | STRIP_XIDS], [0], [dnl
 NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=1
 ])
 OVS_VSWITCHD_STOP
@@ -167,7 +197,121 @@ NXST_AGGREGATE reply: packet_count=0 byte_count=0 flow_count=2
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - del flows with cookie])
+AT_SETUP([ofproto - mod flow with cookie change (OpenFlow 1.0)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -F openflow10 add-flow br0 cookie=0x1,in_port=1,actions=0])
+AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:0
+OFPST_FLOW reply:
+])
+
+AT_CHECK([ovs-ofctl -F openflow10 mod-flows br0 cookie=0x2,in_port=1,actions=0])
+AT_CHECK([ovs-ofctl -F openflow10 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x2, in_port=1 actions=output:0
+OFPST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod flow with cookie change (NXM)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -F nxm add-flow br0 cookie=0x1,in_port=1,actions=0])
+AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:0
+NXST_FLOW reply:
+])
+
+AT_CHECK([ovs-ofctl -F nxm mod-flows br0 cookie=0x2,in_port=1,actions=0])
+AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x2, in_port=1 actions=output:0
+NXST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod flows based on cookie mask])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=0])
+AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=2,actions=0])
+AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=3,actions=0])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:0
+ cookie=0x1, in_port=2 actions=output:0
+ cookie=0x2, in_port=3 actions=output:0
+NXST_FLOW reply:
+])
+
+AT_CHECK([ovs-ofctl -F nxm mod-flows br0 cookie=0x1/0xff,actions=4])
+AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:4
+ cookie=0x1, in_port=2 actions=output:4
+ cookie=0x2, in_port=3 actions=output:0
+NXST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod flows based on cookie mask with cookie change])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=0])
+AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=2,actions=0])
+AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=3,actions=0])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:0
+ cookie=0x1, in_port=2 actions=output:0
+ cookie=0x2, in_port=3 actions=output:0
+NXST_FLOW reply:
+])
+
+AT_CHECK([ovs-ofctl -F nxm mod-flows br0 cookie=1/-1,cookie=4,actions=4])
+AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x2, in_port=3 actions=output:0
+ cookie=0x4, in_port=1 actions=output:4
+ cookie=0x4, in_port=2 actions=output:4
+NXST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod flow with cookie miss (mask==0)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -F nxm mod-flows br0 in_port=1,actions=0])
+AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ in_port=1 actions=output:0
+NXST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - mod flow with cookie miss (mask!=0)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -F nxm mod-flows br0 cookie=1/1,in_port=1,actions=0])
+AT_CHECK([ovs-ofctl -F nxm dump-flows br0 | ofctl_strip | sort], [0], [dnl
+NXST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - del flows with cookies])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=0])
+AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=0])
+AT_CHECK([ovs-ofctl add-flow br0 cookie=0x3,in_port=3,actions=0])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0x1, in_port=1 actions=output:0
+ cookie=0x2, in_port=2 actions=output:0
+ cookie=0x3, in_port=3 actions=output:0
+NXST_FLOW reply:
+])
+
+AT_CHECK([ovs-ofctl del-flows br0])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+NXST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - del flows based on cookie])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=0])
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=0])
@@ -179,7 +323,7 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
 NXST_FLOW reply:
 ])
 
-AT_CHECK([ovs-ofctl del-flows br0 cookie=0x3])
+AT_CHECK([ovs-ofctl del-flows br0 cookie=0x3/-1])
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  cookie=0x1, in_port=1 actions=output:0
  cookie=0x2, in_port=2 actions=output:0
@@ -188,7 +332,7 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto - del flows with cookie mask])
+AT_SETUP([ofproto - del flows based on cookie mask])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=0])
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x2,in_port=2,actions=0])
@@ -210,16 +354,18 @@ AT_CLEANUP
 AT_SETUP([ofproto - flow table configuration])
 OVS_VSWITCHD_START
 # Check the default configuration.
-(echo "OFPST_TABLE reply (xid=0x1): 255 tables
+(echo "OFPST_TABLE reply (xid=0x2): 255 tables
   0: classifier: wild=0x3fffff, max=1000000, active=0
                lookup=0, matched=0"
  x=1
- while test $x -lt 255; do
+ while test $x -lt 254; do
    printf "  %d: %-8s: wild=0x3fffff, max=1000000, active=0
                lookup=0, matched=0
 " $x table$x
    x=`expr $x + 1`
- done) > expout
+ done
+ echo "  254: table254: wild=0x3fffff, max=1000000, active=2
+               lookup=0, matched=0") > expout
 AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout])
 # Change the configuration.
 AT_CHECK(
@@ -233,7 +379,7 @@ AT_CHECK(
 ])
 # Check that the configuration was updated.
 mv expout orig-expout
-(echo "OFPST_TABLE reply (xid=0x1): 255 tables
+(echo "OFPST_TABLE reply (xid=0x2): 255 tables
   0: main    : wild=0x3fffff, max=1000000, active=0
                lookup=0, matched=0
   1: table1  : wild=0x3fffff, max=  1024, active=0
@@ -302,18 +448,18 @@ for in_port in 4 3 2 1; do
     ovs-ofctl add-flow br0 idle_timeout=${in_port}0,in_port=$in_port,actions=drop
 done
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=10,in_port=1 actions=drop
- idle_timeout=20,in_port=2 actions=drop
- idle_timeout=30,in_port=3 actions=drop
- idle_timeout=40,in_port=4 actions=drop
+ idle_timeout=10, in_port=1 actions=drop
+ idle_timeout=20, in_port=2 actions=drop
+ idle_timeout=30, in_port=3 actions=drop
+ idle_timeout=40, in_port=4 actions=drop
 NXST_FLOW reply:
 ])
 # Adding another flow will cause the one that expires soonest to be evicted.
 AT_CHECK([ovs-ofctl add-flow br0 in_port=5,actions=drop])
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=20,in_port=2 actions=drop
- idle_timeout=30,in_port=3 actions=drop
- idle_timeout=40,in_port=4 actions=drop
+ idle_timeout=20, in_port=2 actions=drop
+ idle_timeout=30, in_port=3 actions=drop
+ idle_timeout=40, in_port=4 actions=drop
  in_port=5 actions=drop
 NXST_FLOW reply:
 ])
@@ -323,7 +469,7 @@ AT_CHECK([ovs-ofctl mod-flows br0 in_port=6,actions=drop])
 AT_CHECK([ovs-ofctl add-flow br0 in_port=4,actions=normal])
 AT_CHECK([ovs-ofctl mod-flows br0 in_port=3,actions=output:1])
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=30,in_port=3 actions=output:1
+ idle_timeout=30, in_port=3 actions=output:1
  in_port=4 actions=NORMAL
  in_port=5 actions=drop
  in_port=6 actions=drop
@@ -365,10 +511,10 @@ idle_timeout=30 in_port=1 dl_src=00:22:33:44:55:66 actions=drop
 idle_timeout=40 in_port=1 dl_src=00:33:44:55:66:77 actions=drop
 EOF
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=10,in_port=2,dl_src=00:44:55:66:77:88 actions=drop
- idle_timeout=20,in_port=1,dl_src=00:11:22:33:44:55 actions=drop
- idle_timeout=30,in_port=1,dl_src=00:22:33:44:55:66 actions=drop
- idle_timeout=40,in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=10, in_port=2,dl_src=00:44:55:66:77:88 actions=drop
+ idle_timeout=20, in_port=1,dl_src=00:11:22:33:44:55 actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
 NXST_FLOW reply:
 ])
 # Adding another flow will cause the one that expires soonest within
@@ -377,9 +523,9 @@ NXST_FLOW reply:
 # (which is what makes the test interesting):
 AT_CHECK([ovs-ofctl add-flow br0 in_port=2,dl_src=00:55:66:77:88:99,actions=drop])
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=10,in_port=2,dl_src=00:44:55:66:77:88 actions=drop
- idle_timeout=30,in_port=1,dl_src=00:22:33:44:55:66 actions=drop
- idle_timeout=40,in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=10, in_port=2,dl_src=00:44:55:66:77:88 actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
  in_port=2,dl_src=00:55:66:77:88:99 actions=drop
 NXST_FLOW reply:
 ])
@@ -392,12 +538,12 @@ idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=drop
 idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=drop
 EOF
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=10,in_port=2,dl_src=00:44:55:66:77:88 actions=drop
- idle_timeout=30,in_port=1,dl_src=00:22:33:44:55:66 actions=drop
- idle_timeout=40,in_port=1,dl_src=00:33:44:55:66:77 actions=drop
- idle_timeout=50,in_port=2,dl_src=00:66:77:88:99:aa actions=drop
- idle_timeout=60,in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
- idle_timeout=70,in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
+ idle_timeout=10, in_port=2,dl_src=00:44:55:66:77:88 actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
+ idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
+ idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
  in_port=2,dl_src=00:55:66:77:88:99 actions=drop
 NXST_FLOW reply:
 ])
@@ -405,12 +551,12 @@ NXST_FLOW reply:
 # to be evicted.
 AT_CHECK([ovs-ofctl add-flow br0 'idle_timeout=80 in_port=2 dl_src=00:99:aa:bb:cc:dd actions=drop'])
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=30,in_port=1,dl_src=00:22:33:44:55:66 actions=drop
- idle_timeout=40,in_port=1,dl_src=00:33:44:55:66:77 actions=drop
- idle_timeout=50,in_port=2,dl_src=00:66:77:88:99:aa actions=drop
- idle_timeout=60,in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
- idle_timeout=70,in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
- idle_timeout=80,in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
+ idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
+ idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
+ idle_timeout=80, in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
  in_port=2,dl_src=00:55:66:77:88:99 actions=drop
 NXST_FLOW reply:
 ])
@@ -418,9 +564,9 @@ NXST_FLOW reply:
 # overall to be evicted.
 AT_CHECK([ovs-vsctl set Flow_Table evict flow-limit=4])
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=60,in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
- idle_timeout=70,in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
- idle_timeout=80,in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
+ idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
+ idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
+ idle_timeout=80, in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
  in_port=2,dl_src=00:55:66:77:88:99 actions=drop
 NXST_FLOW reply:
 ])
@@ -442,21 +588,21 @@ check_async () {
     ovs-ofctl -v packet-out br0 none controller '0001020304050010203040501234'
     if test X"$1" = X"OFPR_ACTION"; then shift;
         echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via action) data_len=14 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)"
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)"
     fi
 
     # OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123)
     ovs-ofctl -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234'
     if test X"$1" = X"OFPR_NO_MATCH"; then shift;
         echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via no_match) data_len=14 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)"
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)"
     fi
 
     # OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0)
     ovs-ofctl packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003fb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00'
     if test X"$1" = X"OFPR_INVALID_TTL"; then shift;
         echo >>expout "OFPT_PACKET_IN: total_len=76 in_port=NONE (via invalid_ttl) data_len=76 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(00:26:b9:8c:b0:f9->00:25:83:df:b4:00) type:0800 proto:17 tos:0 ttl:0 ip(172.17.55.13->172.16.0.2) port(55155->53) udp_csum:8f6d"
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:26:b9:8c:b0:f9->00:25:83:df:b4:00) type:0800 proto:17 tos:0 ttl:0 ip(172.17.55.13->172.16.0.2) port(55155->53) udp_csum:8f6d"
     fi
 
     # OFPT_PORT_STATUS, OFPPR_ADD
@@ -499,7 +645,7 @@ s/00:0.$/00:0x/' < monitor.log]],
 # It's a service connection so initially there should be no async messages.
 check_async 1
 
-# Set miss_send_len to 128, turning on packet-outs for our service connection.
+# Set miss_send_len to 128, turning on packet-ins for our service connection.
 ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
 check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_DELETE OFPRR_DELETE
 
@@ -529,3 +675,308 @@ check_async 7 OFPR_ACTION OFPPR_ADD
 ovs-appctl -t ovs-ofctl exit
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+dnl This test checks that OFPT_PACKET_OUT accepts both OFPP_NONE (as
+dnl specified by OpenFlow 1.0) and OFPP_CONTROLLER (used by some
+dnl controllers despite the spec) as meaning a packet that was generated
+dnl by the controller.
+AT_SETUP([ofproto - packet-out from controller])
+OVS_VSWITCHD_START
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P openflow10 monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER (65533) as in_port.
+AT_CHECK([ovs-ofctl packet-out br0 none controller '0001020304050010203040501234'])
+AT_CHECK([ovs-ofctl packet-out br0 65533 controller '0001020304050010203040505678'])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+OFPT_PACKET_IN: total_len=14 in_port=NONE (via action) data_len=14 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)
+OFPT_PACKET_IN: total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:5678 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)
+OFPT_BARRIER_REPLY:
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This test checks that metadata is encoded in packet_in structures,
+dnl supported by NXAST.
+AT_SETUP([ofproto - packet-out with metadata (NXM)])
+OVS_VSWITCHD_START
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P nxm monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send a packet-out with a load action to set some metadata, and forward to controller
+AT_CHECK([ovs-ofctl packet-out br0 none 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), controller' '0001020304050010203040501234'])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+NXT_PACKET_IN: total_len=14 in_port=NONE metadata=0xfafafafa5a5a5a5a (via action) data_len=14 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)
+OFPT_BARRIER_REPLY:
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - flow monitoring])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:1
+
+# Start a monitor watching the flow table and check the initial reply.
+ovs-ofctl monitor br0 watch: --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+ovs-appctl -t ovs-ofctl ofctl/barrier
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
+  [NXST_FLOW_MONITOR reply:
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
+OFPT_BARRIER_REPLY:
+])
+
+# Add, delete, and modify some flows and check the updates.
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=124,actions=output:2
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:5
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,dl_vlan_pcp=0,actions=output:6
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,dl_vlan_pcp=1,actions=output:7
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:8
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=0,actions=output:9
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=1,actions=output:10
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,actions=output:11
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,dl_vlan_pcp=0,actions=output:12
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,dl_vlan_pcp=1,actions=output:13
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,actions=output:14
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=0,actions=output:15
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=1,actions=output:16
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,actions=output:17
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=0,actions=output:18
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=1,actions=output:19
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,actions=output:20
+ovs-ofctl add-flow br0 in_port=0,dl_vlan_pcp=0,actions=output:21
+ovs-ofctl add-flow br0 in_port=0,dl_vlan_pcp=1,actions=output:22
+ovs-ofctl add-flow br0 in_port=0,actions=output:23
+ovs-ofctl mod-flows br0 cookie=5,dl_vlan=123,actions=output:3
+ovs-ofctl del-flows br0 dl_vlan=123
+ovs-ofctl del-flows br0
+ovs-appctl -t ovs-ofctl ofctl/barrier
+sort='
+    # Sorts groups of lines that start with a space, without moving them
+    # past the nearest line that does not start with a space.
+    use warnings;
+    use strict;
+    my @buffer = ();
+    while (<STDIN>) {
+        if (/^ /) {
+            push(@buffer, $_);
+        } else {
+            print $_ foreach sort(@buffer);
+            print $_;
+            @buffer = ();
+        }
+    }
+    print $_ foreach sort(@buffer);
+'
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log | perl -e "$sort"], [0],
+[NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:5
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:6
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:7
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:8
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:9
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:10
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,vlan_tci=0x0000 actions=output:11
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:15
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:16
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:17
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0 actions=output:23
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3
+ event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
+ event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3
+ event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
+ event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=DELETED reason=delete table=0 cookie=0 in_port=0 actions=output:23
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,vlan_tci=0x0000 actions=output:11
+OFPT_BARRIER_REPLY:
+])
+
+# Check that our own changes are reported as abbreviations.
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+ovs-ofctl add-flow br0 in_port=1,actions=output:2
+ovs-ofctl add-flow br0 in_port=2,actions=output:1
+ovs-appctl -t ovs-ofctl ofctl/send 010e004812345678003fffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000ffffffffffff0000
+ovs-appctl -t ovs-ofctl ofctl/barrier
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
+])
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
+[NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=1 actions=output:2
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=2 actions=output:1
+send: OFPT_FLOW_MOD: DEL priority=0 actions=drop
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ABBREV xid=0x12345678
+OFPT_BARRIER_REPLY:
+])
+
+ovs-appctl -t ovs-ofctl exit
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - flow monitoring pause and resume])
+AT_KEYWORDS([monitor])
+
+# The maximum socket receive buffer size is important for this test, which
+# tests behavior when the receive buffer overflows.
+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
+else
+    # Don't know how to get maximum socket receive buffer on this OS
+    AT_SKIP_IF([:])
+fi
+# Calculate the total amount of queuing: rmem_max in the kernel, 128 kB
+# in ofproto sending userspace (see ofmonitor_flush() in connmgr.c).
+queue_size=`expr $rmem_max + 128 \* 1024`
+echo rmem_max=$rmem_max queue_size=$queue_size
+
+# Each flow update message takes up at least 48 bytes of space in queues
+# and in practice more than that.
+n_msgs=`expr $queue_size / 48`
+echo n_msgs=$n_msgs
+
+OVS_VSWITCHD_START
+
+# Start a monitor watching the flow table, then make it block.
+ON_EXIT([kill `cat ovs-ofctl.pid`])
+ovs-ofctl monitor br0 watch: --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+ovs-appctl -t ovs-ofctl ofctl/block
+
+# Add $n_msgs flows.
+(echo "in_port=2,actions=output:2"
+perl -e '
+    for ($i = 0; $i < '$n_msgs'; $i++) {
+        print "cookie=1,reg1=$i,actions=drop\n";
+    }
+') > flows.txt
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+# Check that multipart flow dumps work properly:
+AT_CHECK([ovs-ofctl diff-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl add-flow br0 in_port=1,cookie=3,actions=drop])
+AT_CHECK([ovs-ofctl mod-flows br0 in_port=2,cookie=2,actions=output:2])
+AT_CHECK([ovs-ofctl del-flows br0 cookie=1/-1])
+
+ovs-appctl -t ovs-ofctl ofctl/unblock
+ovs-appctl -t ovs-ofctl ofctl/barrier
+
+ovs-appctl -t ovs-ofctl exit
+
+# Check that the flow monitor reported the same number of flows
+# added and deleted, but fewer than we actually added and deleted.
+adds=`grep -c 'ADDED.*reg1=' monitor.log`
+deletes=`grep -c 'DELETED.*reg1=' monitor.log`
+echo adds=$adds deletes=$deletes
+AT_CHECK([test $adds -gt 100 && test $adds -lt $n_msgs])
+AT_CHECK([test $adds = $deletes])
+
+# Check that the flow monitor reported everything in the expected order:
+#
+#     event=ADDED table=0 cookie=0x1 reg1=0x22
+# ...
+#    NXT_FLOW_MONITOR_PAUSED:
+# ...
+#     event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
+# ...
+#     event=ADDED table=0 cookie=0x3 in_port=1
+#     event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2
+#    NXT_FLOW_MONITOR_RESUMED:
+#
+# except that, between the PAUSED and RESUMED, the order of the ADDED
+# and MODIFIED lines lines depends on hash order, that is, it varies
+# as we change the hash function or change architecture.  Therefore,
+# we use a couple of tests below to accept both orders.
+AT_CHECK([ofctl_strip < monitor.log | sed -n -e '
+/reg1=0x22$/p
+/cookie=0x[[23]]/p
+/NXT_FLOW_MONITOR_PAUSED:/p
+/NXT_FLOW_MONITOR_RESUMED:/p
+' > monitor.log.subset])
+AT_CHECK([grep -v MODIFIED monitor.log.subset], [0], [dnl
+ event=ADDED table=0 cookie=0x1 reg1=0x22
+NXT_FLOW_MONITOR_PAUSED:
+ event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
+ event=ADDED table=0 cookie=0x3 in_port=1
+NXT_FLOW_MONITOR_RESUMED:
+])
+AT_CHECK([grep -v ADDED monitor.log.subset], [0], [dnl
+NXT_FLOW_MONITOR_PAUSED:
+ event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
+ event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2
+NXT_FLOW_MONITOR_RESUMED:
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index befa8b7..e66c943 100644 (file)
@@ -4,10 +4,11 @@ AT_SETUP([ovs-monitor-ipsec])
 AT_SKIP_IF([test $HAVE_PYTHON = no])
 
 OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
 OVS_PKGDATADIR=`pwd`; export OVS_PKGDATADIR
 cp "$top_srcdir/vswitchd/vswitch.ovsschema" .
 
-trap 'kill `cat pid ovs-monitor-ipsec.pid`' 0
+ON_EXIT([kill `cat pid ovs-monitor-ipsec.pid`])
 
 mkdir etc etc/init.d etc/racoon etc/racoon/certs
 mkdir usr usr/sbin
@@ -32,7 +33,7 @@ chmod +x usr/sbin/setkey
 touch etc/racoon/certs/ovs-stale.pem
 
 ovs_vsctl () {
-    ovs-vsctl --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket "$@"
+    ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket "$@"
 }
 trim () {  # Removes blank lines and lines starting with # from input.
     sed -e '/^#/d' -e '/^[       ]*$/d' "$@"
index 37498a7..947f985 100644 (file)
@@ -42,6 +42,7 @@ actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel:0x123456789
 actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, NXM_NX_REG0[0..12])
 table=1,actions=drop
 tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop
+metadata=0x1234ffff5678ffff/0xffff0000ffff0000,actions=drop
 actions=bundle(eth_src,50,active_backup,ofport,slaves:1)
 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3)
 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:)
@@ -75,6 +76,7 @@ NXT_FLOW_MOD: ADD table:255 actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tu
 NXT_FLOW_MOD: ADD table:255 actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
 NXT_FLOW_MOD: ADD table:1 actions=drop
 NXT_FLOW_MOD: ADD table:255 tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop
+NXT_FLOW_MOD: ADD table:255 metadata=0x1234000056780000/0xffff0000ffff0000 actions=drop
 NXT_FLOW_MOD: ADD table:255 actions=bundle(eth_src,50,active_backup,ofport,slaves:1)
 NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3)
 NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:)
@@ -181,10 +183,11 @@ actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 tun_id=0x1234,cookie=0x5678,actions=flood
 actions=drop
 reg0=123,actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG2[0..31],move:NXM_NX_REG0[0..31]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
+actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[]
 actions=autopath(5,NXM_NX_REG0[])
 vlan_tci=0x1123/0x1fff,actions=drop
 ]])
-AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout])
+AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout], [stderr])
 AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
 [[usable protocols: NXM
 chosen protocol: NXM-table_id
@@ -209,9 +212,13 @@ NXT_FLOW_MOD: ADD <any> actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06
 NXT_FLOW_MOD: ADD NXM_NX_TUN_ID(0000000000001234) cookie:0x5678 actions=FLOOD
 NXT_FLOW_MOD: ADD <any> actions=drop
 NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
+NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[]
 NXT_FLOW_MOD: ADD <any> actions=autopath(5,NXM_NX_REG0[])
 NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop
 ]])
+AT_CHECK([[sed 's/^[^|]*|[^|]*|//' stderr]], [0], [dnl
+autopath|WARN|The autopath action is deprecated and may be removed in February 2013.  Please email dev@openvswitch.org with concerns.
+])
 AT_CLEANUP
 
 AT_SETUP([ovs-ofctl parse-nx-match])
@@ -229,10 +236,15 @@ NXM_OF_ETH_DST_W(010000000000/010000000000)
 NXM_OF_ETH_DST_W(000000000000/010000000000)
 NXM_OF_ETH_DST_W(ffffffffffff/010000000000)
 NXM_OF_ETH_DST_W(0002e30f80a4/ffffffffffff)
+NXM_OF_ETH_DST_W(60175619848f/000000000000)
 NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
+NXM_OF_ETH_DST_W(60175619848f/5a5a5a5a5a5a)
 
 # eth src
 NXM_OF_ETH_SRC(020898456ddb)
+NXM_OF_ETH_SRC_W(012345abcdef/ffffff555555)
+NXM_OF_ETH_SRC_W(020898456ddb/ffffffffffff)
+NXM_OF_ETH_SRC_W(020898456ddb/000000000000)
 
 # eth type
 NXM_OF_ETH_TYPE(0800)
@@ -245,6 +257,8 @@ NXM_OF_VLAN_TCI(0000)           # Packets without 802.1Q header.
 NXM_OF_VLAN_TCI(3123)           # Packets with VID=123, PCP=1.
 NXM_OF_VLAN_TCI(0123)           # Does not make sense (but supported anyway)
 NXM_OF_VLAN_TCI_W(1123/1fff)    # Packets with VID=123, any PCP.
+NXM_OF_VLAN_TCI_W(1123/ffff)    # Packets with VID=123, PCP=0
+NXM_OF_VLAN_TCI_W(1123/0000)    # Packets with or without 802.1Q header
 NXM_OF_VLAN_TCI_W(f000/f000)    # Packets with any VID, PCP=7.
 NXM_OF_VLAN_TCI_W(0000/e000)    # No 802.1Q or with VID=0
 
@@ -271,33 +285,47 @@ NXM_NX_IP_TTL(80)
 # IP source
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC(ac100014)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/FFFF0000)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/5a5a5a5a)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/ffffffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/00000000)
 NXM_OF_ETH_TYPE(0806) NXM_OF_IP_SRC(ac100014)
 NXM_OF_IP_SRC_W(C0D80000/FFFF0000)
 
 # IP destination
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST(ac100014)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a88012/FFFF0000)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a80000/5a5a5a5a)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a80000/ffffffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a80000/00000000)
 NXM_OF_IP_DST(ac100014)
 NXM_OF_ETH_TYPE(0806) NXM_OF_IP_DST_W(C0D80000/FFFF0000)
 
 # TCP source port
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC(4231)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC_W(5050/F0F0)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC_W(5050/ffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC_W(5050/0000)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_SRC(4231)
 
 # TCP destination port
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST(4231)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/FFF0)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/ffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/0000)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_DST(4231)
 
 # UDP source port
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC(8732)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/01FF)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/ffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/0000)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_UDP_SRC(7823)
 
 # UDP destination port
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST(1782)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST_W(5005/F00F)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST_W(5005/FFFF)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST_W(5005/0000)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_OF_UDP_DST(1293)
 
 # ICMP type
@@ -320,12 +348,18 @@ NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(0001) NXM_OF_ARP_OP(0001)
 # ARP source protocol address
 NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA(ac100014)
 NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/FFFFFF00)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/aaaaaa00)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/ffffffff)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/00000000)
 NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_SPA(ac100014)
 NXM_OF_ARP_SPA_W(C0D8fedc/FFFF0000)
 
 # ARP destination protocol address
 NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA(ac100014)
 NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a812fe/FFFFFF00)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a81234/77777777)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a81234/ffffffff)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a81234/00000000)
 NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_TPA(ac100014)
 NXM_OF_ARP_TPA_W(C0D80000/FFFF0000)
 
@@ -343,14 +377,32 @@ NXM_NX_ARP_THA(0002e30f80a4)
 NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
 NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
 NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/5a5a5a5a5a5a5a5a0000000000000000)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/00000000000000000000000000000000)
 NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
 
 # IPv6 destination
 NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
 NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
-NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/77777777777777777777777777777777)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/00000000000000000000000000000000)
 NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
 
+# IPv6 Flow Label
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_LABEL(1000000f)
+NXM_NX_IPV6_LABEL(0000000f)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_LABEL(0000000f)
+
+# ND target address
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/0123456789abcdeffedcba9876543210)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/ffffffffffffffffffffffffffffffff)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/00000000000000000000000000000000)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/fedcba98765432100123456789abcdef)
+
 # ND source hardware address
 NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4)
 NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4)
@@ -374,6 +426,8 @@ NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(00/02)
 NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(01/01)
 NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(02/02)
 NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(03/03)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(03/ff)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(03/00)
 NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG(f3)
 
 # IPv6 fragments.
@@ -387,19 +441,27 @@ NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(00/02)
 NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(01/01)
 NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(02/02)
 NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/03)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/00)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/ff)
 NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG(f3)
 
 # Flow cookie.
 NXM_NX_COOKIE(00000000abcdef01)
 NXM_NX_COOKIE_W(84200000abcdef01/84200000FFFFFFFF)
+NXM_NX_COOKIE_W(84200000abcdef01/ffffffffffffffff)
+NXM_NX_COOKIE_W(84200000abcdef01/0000000000000000)
 
 # Tunnel ID.
 NXM_NX_TUN_ID(00000000abcdef01)
 NXM_NX_TUN_ID_W(84200000abcdef01/84200000FFFFFFFF)
+NXM_NX_TUN_ID_W(84200000abcdef01/FFFFFFFFFFFFFFFF)
+NXM_NX_TUN_ID_W(84200000abcdef01/0000000000000000)
 
 # Register 0.
 NXM_NX_REG0(acebdf56)
 NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
+NXM_NX_REG0_W(a0e0d050/ffffffff)
+NXM_NX_REG0_W(a0e0d050/00000000)
 
 # Invalid field number.
 01020304(1111/2222)
@@ -426,10 +488,15 @@ NXM_OF_ETH_DST_W(010000000000/010000000000)
 NXM_OF_ETH_DST_W(000000000000/010000000000)
 NXM_OF_ETH_DST_W(010000000000/010000000000)
 NXM_OF_ETH_DST(0002e30f80a4)
+<any>
 NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
+NXM_OF_ETH_DST_W(40125218000a/5a5a5a5a5a5a)
 
 # eth src
 NXM_OF_ETH_SRC(020898456ddb)
+NXM_OF_ETH_SRC_W(012345014545/ffffff555555)
+NXM_OF_ETH_SRC(020898456ddb)
+<any>
 
 # eth type
 NXM_OF_ETH_TYPE(0800)
@@ -442,6 +509,8 @@ NXM_OF_VLAN_TCI(0000)
 NXM_OF_VLAN_TCI(3123)
 NXM_OF_VLAN_TCI(0123)
 NXM_OF_VLAN_TCI_W(1123/1fff)
+NXM_OF_VLAN_TCI(1123)
+<any>
 NXM_OF_VLAN_TCI_W(f000/f000)
 NXM_OF_VLAN_TCI_W(0000/e000)
 
@@ -468,33 +537,47 @@ nx_pull_match() returned error OFPBMC_BAD_PREREQ
 # IP source
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(ac100014)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC_W(c0a80000/ffff0000)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC_W(40080000/5a5a5a5a)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80000)
+NXM_OF_ETH_TYPE(0800)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # IP destination
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST(ac100014)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST_W(c0a80000/ffff0000)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST_W(40080000/5a5a5a5a)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST(c0a80000)
+NXM_OF_ETH_TYPE(0800)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # TCP source port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(4231)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC_W(5050/f0f0)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(5050)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # TCP destination port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(4231)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST_W(fde0/fff0)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(fde0)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # UDP source port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(8732)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC_W(0132/01ff)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(0132)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # UDP destination port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(1782)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST_W(5005/f00f)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(5005)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # ICMP type
@@ -517,12 +600,18 @@ nx_pull_match() returned error OFPBMC_DUP_FIELD
 # ARP source protocol address
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(ac100014)
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA_W(c0a81200/ffffff00)
+NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA_W(80a80200/aaaaaa00)
+NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(c0a81234)
+NXM_OF_ETH_TYPE(0806)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # ARP destination protocol address
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA(ac100014)
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA_W(c0a81200/ffffff00)
+NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA_W(40201234/77777777)
+NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA(c0a81234)
+NXM_OF_ETH_TYPE(0806)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
@@ -540,13 +629,31 @@ nx_pull_match() returned error OFPBMC_BAD_PREREQ
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(00000818184800000000000000000000/5a5a5a5a5a5a5a5a0000000000000000)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000000)
+NXM_OF_ETH_TYPE(86dd)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
 # IPv6 destination
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
-NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010530344500010000000000000000/77777777777777777777777777777777)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST(20010db83c4d00010000000000000000)
+NXM_OF_ETH_TYPE(86dd)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IPv6 Flow Label
+nx_pull_match() returned error OFPBMC_BAD_VALUE
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_LABEL(0000000f)
+
+# ND target address
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET_W(00010520080900010000000000040000/0123456789abcdeffedcba9876543210)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET_W(20000898344400000002000300000005/fedcba98765432100123456789abcdef)
 
 # ND source hardware address
 NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005), NXM_NX_ND_SLL(0002e30f80a4)
@@ -571,6 +678,8 @@ NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(00/02)
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(01/01)
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(02/02)
 NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(03)
+NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(03)
+NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(00)
 nx_pull_match() returned error OFPBMC_BAD_VALUE
 
 # IPv6 fragments.
@@ -584,19 +693,27 @@ NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(00/02)
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(01/01)
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(02/02)
 NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(03)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(00)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(03)
 nx_pull_match() returned error OFPBMC_BAD_VALUE
 
 # Flow cookie.
 NXM_NX_COOKIE(00000000abcdef01)
 NXM_NX_COOKIE_W(84200000abcdef01/84200000ffffffff)
+NXM_NX_COOKIE(84200000abcdef01)
+<any>
 
 # Tunnel ID.
 NXM_NX_TUN_ID(00000000abcdef01)
 NXM_NX_TUN_ID_W(84200000abcdef01/84200000ffffffff)
+NXM_NX_TUN_ID(84200000abcdef01)
+<any>
 
 # Register 0.
 NXM_NX_REG0(acebdf56)
 NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
+NXM_NX_REG0(a0e0d050)
+<any>
 
 # Invalid field number.
 nx_pull_match() returned error OFPBMC_BAD_FIELD
@@ -612,6 +729,541 @@ nx_pull_match() returned error OFPBMC_BAD_FIELD
 ])
 AT_CLEANUP
 
+AT_SETUP([ovs-ofctl parse-ofp10-match])
+AT_KEYWORDS([OF1.0])
+AT_DATA([test-data], [dnl
+# in_port=65534
+003820fe fffe xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_src=00:01:02:03:04:05
+003820fb xxxx 000102030405 xxxxxxxxxxxx xxxx xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_dst=10:20:30:40:50:60
+003820f7 xxxx xxxxxxxxxxxx 102030405060 xxxx xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_vlan=291
+003820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx 0123 xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_vlan_pcp=5
+002820ff xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx 05 xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_vlan=291,dl_vlan_pcp=4
+002820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx 0123 04 xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# vlan_tci=0x0000
+003820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx ffff xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl dl_vlan_pcp doesn't make sense when dl_vlan is "none", so
+dnl OVS ignores it and drops it on output.
+# vlan_tci=0x0000
+#  1: 28 -> 38
+# 20: 05 -> 00
+002820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx ffff 05 xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl Invalid VID and PCP discards out-of-range bits:
+# dl_vlan=256,dl_vlan_pcp=7
+# 18: f1 -> 01
+# 20: ff -> 07
+002820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx f100 ff xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_type=0x1234
+003820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# ip,nw_proto=5
+003820cf xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 05 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl Ignore nw_proto if not IP or ARP:
+# dl_type=0x1234,nw_proto=5
+# normal:  3: cf -> ef
+# normal: 25: 05 -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: dl_type=0x1234,nw_proto=5
+& ofp_util|INFO|post: dl_type=0x1234
+003820cf xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx 05 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# ip,nw_tos=252
+001820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 fc xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl Ignore nw_tos if not IP:
+# arp,nw_tos=4
+# 24: 05 -> 04
+# normal:  1: 18 -> 38
+# normal: 24: 04 -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: arp,nw_tos=4
+& ofp_util|INFO|post: arp
+001820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 05 xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl Low 2 bits of invalid TOS are forced to 0:
+# ip,nw_tos=48
+# 24: 31 -> 30
+001820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 31 xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# arp,arp_op=2
+003820cf xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx 02 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# ip,nw_src=192.168.128.85
+003800ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+# ip,nw_src=192.168.128.0/24
+# 31: 55 -> 00
+003808ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+# ip,nw_dst=192.168.128.85
+003020ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+# ip,nw_dst=192.168.128.0/24
+# 35: 55 -> 00
+003220ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+# arp,nw_src=192.168.128.85
+003800ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+# arp,nw_src=192.168.128.0/24
+# 31: 55 -> 00
+003808ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+# arp,nw_dst=192.168.128.85
+003020ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+# arp,nw_dst=192.168.128.0/24
+# 35: 55 -> 00
+003220ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+dnl Ignore nw_src if not IP or ARP:
+# dl_type=0x1234,nw_src=192.168.128.0/24
+# 31: 55 -> 00
+# normal:  2: 08 -> 20
+# normal: 28: c0 -> 00
+# normal: 29: a8 -> 00
+# normal: 30: 80 -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: dl_type=0x1234,nw_src=192.168.128.0/24
+& ofp_util|INFO|post: dl_type=0x1234
+003808ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+dnl Ignore nw_dst if not IP or ARP:
+# dl_type=0x1234,nw_dst=192.168.128.0/24
+# 35: 55 -> 00
+# normal:  1: 32 -> 38
+# normal: 32: c0 -> 00
+# normal: 33: a8 -> 00
+# normal: 34: 80 -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: dl_type=0x1234,nw_dst=192.168.128.0/24
+& ofp_util|INFO|post: dl_type=0x1234
+003220ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+# tcp,tp_src=443
+0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 06 xxxx dnl
+xxxxxxxx xxxxxxxx 01bb xxxx
+
+# tcp,tp_dst=443
+0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 06 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx 01bb
+
+# udp,tp_src=443
+0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 11 xxxx dnl
+xxxxxxxx xxxxxxxx 01bb xxxx
+
+# udp,tp_dst=443
+0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 11 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx 01bb
+
+# icmp,icmp_type=5
+0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 01 xxxx dnl
+xxxxxxxx xxxxxxxx 0005 xxxx
+
+# icmp,icmp_code=8
+0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 01 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx 0008
+
+dnl Ignore tp_src if not TCP or UDP:
+# ip,nw_proto=21,tp_src=443
+# normal:  3: 8f -> cf
+# normal: 36: 01 -> 00
+# normal: 37: bb -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: ip,nw_proto=21,tp_src=443
+& ofp_util|INFO|post: ip,nw_proto=21
+0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 15 xxxx dnl
+xxxxxxxx xxxxxxxx 01bb xxxx
+
+dnl Ignore tp_dst if not TCP or UDP:
+# ip,nw_proto=21,tp_dst=443
+# normal:  3: 4f -> cf
+# normal: 38: 01 -> 00
+# normal: 39: bb -> 00
+dnl The normalization details are suppressed here due to rate-limiting.
+0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 15 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx 01bb
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+  [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp10-match < input.txt],
+  [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([ovs-ofctl parse-ofp11-match])
+AT_KEYWORDS([OF1.1])
+AT_DATA([test-data], [dnl
+# in_port=65534
+0000 0058 fffffffe 000003fe dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# bad ofp11_match: OFPBMC_BAD_VALUE
+& ofp_util|WARN|port 305419896 is outside the supported range 0 through 65279 or 0xffffff00 through 0xffffffff
+0000 0058 12345678 000003fe dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_src=00:01:02:03:04:05
+0000 0058 00000000 000003ff dnl
+000102030405000000000000 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_src=55:55:55:55:55:55/55:55:55:55:55:55
+0000 0058 00000000 000003ff dnl
+555555555555aaaaaaaaaaaa 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_dst=00:01:02:03:04:05
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000102030405000000000000 dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_dst=01:00:00:00:00:00/01:00:00:00:00:00
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 010000000000feffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_dst=00:01:02:03:04:05/fe:ff:ff:ff:ff:ff
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000102030405010000000000 dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_dst=55:55:55:55:55:55/55:55:55:55:55:55
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 555555555555aaaaaaaaaaaa dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl dl_vlan_pcp is ignored if dl_vlan is wildcarded, which causes the
+dnl the wildcard bit and the dl_vlan_pcp to be dropped for output:
+# in_port=1
+# 11: fa -> fe
+# 38: 03 -> 00
+0000 0058 00000001 000003fa dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 03 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_vlan=291
+0000 0058 00000000 000003fd dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0123 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl OFPVID_NONE:
+# vlan_tci=0x0000
+0000 0058 00000000 000003fd dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+ffff 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl OFPVID_NONE ignores dl_vlan_pcp even if not wildcarded, which causes
+dnl the wildcard bit and the dl_vlan_pcp to be dropped for output:
+# vlan_tci=0x0000
+# 11: f9 -> fd
+# 38: 05 -> 00
+0000 0058 00000000 000003f9 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+ffff 05 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# vlan_tci=0x1000/0x1000
+0000 0058 00000000 000003fd dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+fffe 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl Try invalid VID:
+# bad ofp11_match: OFPBMC_BAD_VALUE
+0000 0058 00000000 000003fd dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+1234 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_vlan_pcp=4
+0000 0058 00000000 000003f9 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+fffe 04 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_vlan=10,dl_vlan_pcp=6
+0000 0058 00000000 000003f9 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+000a 06 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_type=0x1234
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 1234 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_tos=252
+0000 0058 00000000 000003e7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 fc 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl Try invalid TOS:
+# bad ofp11_match: OFPBMC_BAD_VALUE
+0000 0058 00000000 000003e7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 01 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_proto=5
+0000 0058 00000000 000003d7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 05 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# arp,arp_op=2
+0000 0058 00000000 000003d7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0806 00 02 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_src=192.168.128.0/24
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 00 c0a88000000000ff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_src=128.160.128.0/165.165.165.165
+# 44: c0 -> 80
+# 45: a8 -> a0
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 00 c0a880005a5a5a5a 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_dst=192.168.128.0/24
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 00 00000000ffffffff c0a88000000000ff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_dst=128.160.128.0/165.165.165.165
+# 52: c0 -> 80
+# 53: a8 -> a0
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 00 00000000ffffffff c0a880005a5a5a5a 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# arp,nw_src=192.168.128.0/24
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0806 00 00 c0a88000000000ff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# arp,nw_dst=192.168.128.0/24
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0806 00 00 00000000ffffffff c0a88000000000ff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# tcp,tp_src=443
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 06 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# tcp,tp_dst=443
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 06 00000000ffffffff 00000000ffffffff 0000 01bb dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# udp,tp_src=443
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# icmp,icmp_type=5
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 01 00000000ffffffff 00000000ffffffff 0005 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# icmp,icmp_code=8
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 01 00000000ffffffff 00000000ffffffff 0000 0008 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# udp,tp_src=443
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# udp,tp_dst=443
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 0000 01bb dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl SCTP, no ports.
+# ip,nw_proto=132
+0000 0058 00000000 000003d7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl SCTP tp_src matching not supported:
+# bad ofp11_match: OFPBMC_BAD_FIELD
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl SCTP tp_dst matching not supported:
+# bad ofp11_match: OFPBMC_BAD_FIELD
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 0000 01bb dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl Ignore tp_src if not TCP or UDP or SCTP:
+# ip,nw_proto=21
+# 11: 97 -> d7
+# 60: 01 -> 00
+# 61: bb -> 00
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 15 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl Ignore tp_dst if not TCP or UDP or SCTP:
+# ip,nw_proto=22
+# 11: 57 -> d7
+# 62: 01 -> 00
+# 63: bb -> 00
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 16 00000000ffffffff 00000000ffffffff 0000 01bb dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl mpls_label not yet supported:
+# bad ofp11_match: OFPBMC_BAD_TAG
+0000 0058 00000000 000002f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 8847 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+12345678 00 000000 0000000000000000ffffffffffffffff
+
+dnl mpls_tc not yet supported:
+# bad ofp11_match: OFPBMC_BAD_TAG
+0000 0058 00000000 000001f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 8848 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 5a 000000 0000000000000000ffffffffffffffff
+
+dnl mpls_label and mpls_tc must be ignored if dl_type is not MPLS:
+# dl_type=0x1234
+# 10: 00 -> 03
+# 64: 12 -> 00
+# 65: 34 -> 00
+# 66: 56 -> 00
+# 67: 78 -> 00
+# 68: 5a -> 00
+0000 0058 00000000 000000f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 1234 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+12345678 5a 000000 0000000000000000ffffffffffffffff
+
+dnl metadata match:
+# metadata=0x1234567890abcdef
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 1234567890abcdef0000000000000000
+
+dnl metadata match:
+# metadata=0x5555555555555555/0x5555555555555555
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 5555555555555555aaaaaaaaaaaaaaaa
+
+dnl metadata match:
+# metadata=0x1234000090ab0000/0xffff0000ffff0000
+# 74: 56 -> 00
+# 75: 78 -> 00
+# 78: cd -> 00
+# 79: ef -> 00
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 1234567890abcdef0000ffff0000ffff
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+  [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp11-match < input.txt],
+  [0], [expout], [experr])
+AT_CLEANUP
+
 AT_SETUP([ovs-ofctl parse-nx-match loose])
 AT_KEYWORDS([nx-match])
 AT_DATA([nx-match.txt], [dnl
@@ -627,12 +1279,521 @@ NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800)
 ])
 AT_CLEANUP
 
-dnl Check that "-F openflow10" rejects a flow_mod with a tun_id, since
-dnl OpenFlow 1.0 doesn't support tunnels.
-AT_SETUP([ovs-ofctl -F option and tun_id])
+AT_SETUP([ovs-ofctl parse-oxm])
+AT_KEYWORDS([oxm])
+AT_DATA([oxm.txt], [dnl
+<any>
+
+# in port
+OXM_OF_IN_PORT(00000000)
+OXM_OF_IN_PORT(fffffffe)
+
+# metadata
+OXM_OF_METADATA(5a5a5a5a5a5a5a5a)
+OXM_OF_METADATA_W(0000000000000000/00000000ffffffff)
+OXM_OF_METADATA_W(1234567890abcdef/ffff0000ffff0000)
+OXM_OF_METADATA_W(1234567890abcdef/ffffffffffffffff)
+OXM_OF_METADATA_W(1234567890abcdef/0000000000000000)
+
+# eth dst
+OXM_OF_ETH_DST(0002e30f80a4)
+OXM_OF_ETH_DST_W(010000000000/010000000000)
+OXM_OF_ETH_DST_W(000000000000/010000000000)
+OXM_OF_ETH_DST_W(ffffffffffff/010000000000)
+OXM_OF_ETH_DST_W(0002e30f80a4/ffffffffffff)
+OXM_OF_ETH_DST_W(0002e30f80a4/000000000000)
+OXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
+
+# eth src
+OXM_OF_ETH_SRC(020898456ddb)
+
+# eth type
+OXM_OF_ETH_TYPE(0800)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IN_PORT(00000012)
+
+# vlan
+OXM_OF_VLAN_VID(1009) OXM_OF_VLAN_VID(1009) # Duplicate Field
+OXM_OF_VLAN_VID(f009)           # Bad Value
+OXM_OF_VLAN_PCP(00)             # Bad Pre-Requisite
+OXM_OF_VLAN_VID(0000)           # Packets without 802.1Q header or with VID=0
+OXM_OF_VLAN_VID(1123)           # Packets with VID=123, any PCP
+OXM_OF_VLAN_VID(1123) OXM_OF_VLAN_PCP(01) # Packets with VID=123, PCP=1.
+OXM_OF_VLAN_VID(0123)           # Does not make sense (but supported anyway)
+OXM_OF_VLAN_VID_W(0123/0123)      # Does not make sense (but supported anyway)
+OXM_OF_VLAN_VID_W(1123/0123)      # Does not make sense (but supported anyway)
+OXM_OF_VLAN_VID_W(0123/1123)      # Does not make sense (but supported anyway)
+OXM_OF_VLAN_VID(0123) OXM_OF_VLAN_PCP(01)  #Bad Pre-Requisite
+OXM_OF_VLAN_VID_W(1123/1fff)    # Packets with VID=123, any PCP.
+OXM_OF_VLAN_VID_W(1123/ffff)    # Packets with VID=123, any PCP.
+OXM_OF_VLAN_VID_W(1123/0000)    # Packets with or without 802.1Q header
+OXM_OF_VLAN_VID_W(1123/1f0f),   # Packets with # VID=123 (masked)
+OXM_OF_VLAN_VID_W(1123/1f0f), OXM_OF_VLAN_PCP(01)  # Packets with VID=123 (masked), any PCP.
+OXM_OF_VLAN_VID_W(1000/1000)    # Packets with any VID, any PCP
+OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01)  # Packets with any VID, PCP=1.
+
+# IP ECN
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_ECN(03)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_ECN(06)
+OXM_OF_IP_ECN(03)
+
+# IP protocol
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(05)
+OXM_OF_IP_PROTO(05)
+
+# IP source
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC(ac100014)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC_W(C0a80000/FFFF0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC_W(C0a80000/FFFFFFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC_W(C0a80000/00000000)
+OXM_OF_ETH_TYPE(0806) OXM_OF_IPV4_SRC(ac100014)
+OXM_OF_IPV4_SRC_W(C0D80000/FFFF0000)
+
+# IP destination
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST(ac100014)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST_W(C0a88012/FFFF0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST_W(C0a88012/FFFFFFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST_W(C0a88012/00000000)
+OXM_OF_IPV4_DST(ac100014)
+OXM_OF_ETH_TYPE(0806) OXM_OF_IPV4_DST_W(C0D80000/FFFF0000)
+
+# TCP source port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC(4231)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC_W(5050/F0F0)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC_W(5050/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC_W(5050/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(07) OXM_OF_TCP_SRC(4231)
+
+# TCP destination port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST(4231)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST_W(FDE0/FFF0)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST_W(FDE0/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST_W(FDE0/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(07) OXM_OF_TCP_DST(4231)
+
+# UDP source port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC(8732)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC_W(0132/01FF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC_W(0132/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC_W(0132/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_UDP_SRC(7823)
+
+# UDP destination port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST(1782)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/F00F)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(02) OXM_OF_UDP_DST(1293)
+
+# ICMP type
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01) OXM_OF_ICMPV4_TYPE(12)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(00) OXM_OF_ICMPV4_TYPE(10)
+
+# ICMP code
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01) OXM_OF_ICMPV4_CODE(12)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(00) OXM_OF_ICMPV4_CODE(10)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ICMPV4_CODE(10)
+OXM_OF_ICMPV4_CODE(00)
+
+# ARP opcode
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_OP(0001)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_OP(1111)
+OXM_OF_ETH_TYPE(0000) OXM_OF_ARP_OP(0001)
+OXM_OF_ARP_OP(0001)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_OP(0001) OXM_OF_ARP_OP(0001)
+
+# ARP source protocol address
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA(ac100014)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81234/FFFFFF00)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81234/FFFFFFFF)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81234/00000000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_SPA(ac100014)
+OXM_OF_ARP_SPA_W(C0D8fedc/FFFF0000)
+
+# ARP destination protocol address
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA(ac100014)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA_W(C0a812fe/FFFFFF00)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA_W(C0a812fe/FFFFFFFF)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA_W(C0a812fe/00000000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_TPA(ac100014)
+OXM_OF_ARP_TPA_W(C0D80000/FFFF0000)
+
+# ARP source hardware address
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_SHA(0002e30f80a4)
+OXM_OF_ARP_SHA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA_W(0002e30f80a4/ffffffffffff)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA_W(0002e30f80a4/000000000000)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA_W(0002e30f80a4/00000000000f)
+
+# ARP destination hardware address
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_THA(0002e30f80a4)
+OXM_OF_ARP_THA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA_W(0002e30f80a4/ffffffffffff)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA_W(0002e30f80a4/000000000000)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA_W(0002e30f80a4/00000000000f)
+
+# IPv6 source
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC(20010db83c4d00010002000300040005)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_SRC(20010db83c4d00010002000300040005)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/00000000000000000000000000000000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+
+# IPv6 destination
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST(20010db83c4d00010002000300040005)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_DST(20010db83c4d00010002000300040005)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/00000000000000000000000000000000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+
+# IPv6 Flow Label
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL(1000000f)
+OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/0000000f)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/000fffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/000ffff0)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/100fffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/ffffffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/00000000)
+
+# ND source hardware address
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3b) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+
+# ND destination hardware address
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3b) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+
+# Invalid field number.
+01020304(1111/2222)
+])
+AT_CHECK([ovs-ofctl --strict parse-oxm < oxm.txt], [0], [dnl
+<any>
+
+# in port
+OXM_OF_IN_PORT(00000000)
+OXM_OF_IN_PORT(fffffffe)
+
+# metadata
+OXM_OF_METADATA(5a5a5a5a5a5a5a5a)
+OXM_OF_METADATA_W(0000000000000000/00000000ffffffff)
+OXM_OF_METADATA_W(1234000090ab0000/ffff0000ffff0000)
+OXM_OF_METADATA(1234567890abcdef)
+<any>
+
+# eth dst
+OXM_OF_ETH_DST(0002e30f80a4)
+OXM_OF_ETH_DST_W(010000000000/010000000000)
+OXM_OF_ETH_DST_W(000000000000/010000000000)
+OXM_OF_ETH_DST_W(010000000000/010000000000)
+OXM_OF_ETH_DST(0002e30f80a4)
+<any>
+OXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
+
+# eth src
+OXM_OF_ETH_SRC(020898456ddb)
+
+# eth type
+OXM_OF_ETH_TYPE(0800)
+OXM_OF_IN_PORT(00000012), OXM_OF_ETH_TYPE(0800)
+
+# vlan
+nx_pull_match() returned error OFPBMC_DUP_FIELD
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_VLAN_VID(0000)
+OXM_OF_VLAN_VID(1123)
+OXM_OF_VLAN_VID(1123), OXM_OF_VLAN_PCP(01)
+OXM_OF_VLAN_VID(0123)
+OXM_OF_VLAN_VID_W(0123/0123)
+OXM_OF_VLAN_VID_W(0123/0123)
+OXM_OF_VLAN_VID_W(0123/1123)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_VLAN_VID(1123)
+OXM_OF_VLAN_VID(1123)
+<any>
+OXM_OF_VLAN_VID_W(1103/1f0f)
+OXM_OF_VLAN_VID_W(1103/1f0f), OXM_OF_VLAN_PCP(01)
+OXM_OF_VLAN_VID_W(1000/1000)
+OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01)
+
+# IP ECN
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_ECN(03)
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IP protocol
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(05)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IP source
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_SRC(ac100014)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_SRC_W(c0a80000/ffff0000)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_SRC(c0a80000)
+OXM_OF_ETH_TYPE(0800)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IP destination
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_DST(ac100014)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_DST_W(c0a80000/ffff0000)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_DST(c0a88012)
+OXM_OF_ETH_TYPE(0800)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# TCP source port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_SRC(4231)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_SRC_W(5050/f0f0)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_SRC(5050)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# TCP destination port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_DST(4231)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_DST_W(fde0/fff0)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_DST(fde0)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# UDP source port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_SRC(8732)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_SRC_W(0132/01ff)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_SRC(0132)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# UDP destination port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST(1782)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST_W(5005/f00f)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST(5005)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ICMP type
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01), OXM_OF_ICMPV4_TYPE(12)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ICMP code
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01), OXM_OF_ICMPV4_CODE(12)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ARP opcode
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_OP(0001)
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_DUP_FIELD
+
+# ARP source protocol address
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SPA(ac100014)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SPA_W(c0a81200/ffffff00)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SPA(c0a81234)
+OXM_OF_ETH_TYPE(0806)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ARP destination protocol address
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_TPA(ac100014)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_TPA_W(c0a81200/ffffff00)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_TPA(c0a812fe)
+OXM_OF_ETH_TYPE(0806)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ARP source hardware address
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SHA(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SHA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0806)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SHA_W(000000000004/00000000000f)
+
+# ARP destination hardware address
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_THA(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_THA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0806)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_THA_W(000000000004/00000000000f)
+
+# IPv6 source
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_SRC(20010db83c4d00010002000300040005)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_SRC(20010db83c4d00010000000000000000)
+OXM_OF_ETH_TYPE(86dd)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IPv6 destination
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_DST(20010db83c4d00010002000300040005)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_DST(20010db83c4d00010000000000000000)
+OXM_OF_ETH_TYPE(86dd)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IPv6 Flow Label
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL_W(0000000f/0000000f)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL_W(00000000/000ffff0)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd)
+
+# ND source hardware address
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IP_PROTO(3a), OXM_OF_ICMPV6_TYPE(87), OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005), OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ND destination hardware address
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IP_PROTO(3a), OXM_OF_ICMPV6_TYPE(88), OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005), OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# Invalid field number.
+nx_pull_match() returned error OFPBMC_BAD_FIELD
+])
+AT_CLEANUP
+
+AT_SETUP([ovs-ofctl parse-oxm loose])
+AT_KEYWORDS([oxm])
+AT_DATA([oxm.txt], [dnl
+OXM_OF_IN_PORT(00000001), 01020304(1111/2222), OXM_OF_ETH_TYPE(0800)
+])
+
+AT_CHECK([ovs-ofctl --strict parse-oxm < oxm.txt], [0], [dnl
+nx_pull_match() returned error OFPBMC_BAD_FIELD
+])
+
+AT_CHECK([ovs-ofctl parse-oxm < oxm.txt], [0], [dnl
+OXM_OF_IN_PORT(00000001), OXM_OF_ETH_TYPE(0800)
+])
+AT_CLEANUP
+
+dnl Check all of the patterns mentioned in the "VLAN Matching" section
+dnl in the DESIGN file at top level.
+AT_SETUP([ovs-ofctl check-vlan])
+AT_KEYWORDS([VLAN])
+
+dnl [1]
+AT_CHECK([ovs-ofctl check-vlan 0000 0000], [0], [dnl
+ -> 0000/0000
+NXM: <any> -> 0000/0000
+OXM: <any> -> 0000/0000,--
+OF1.0: 0000/1,00/1 -> 0000/0000
+OF1.1: 0000/1,00/1 -> 0000/0000
+])
+
+dnl [2]
+AT_CHECK([ovs-ofctl check-vlan 0000 ffff], [0], [dnl
+vlan_tci=0x0000 -> 0000/ffff
+NXM: NXM_OF_VLAN_TCI(0000) -> 0000/ffff
+OXM: OXM_OF_VLAN_VID(0000) -> 0000/1fff,--
+OF1.0: ffff/0,00/1 -> 0000/ffff
+OF1.1: ffff/0,00/1 -> 0000/ffff
+])
+
+dnl [3]
+AT_CHECK([ovs-ofctl check-vlan 1abc 1fff], [0], [dnl
+dl_vlan=2748 -> 1abc/1fff
+NXM: NXM_OF_VLAN_TCI_W(1abc/1fff) -> 1abc/1fff
+OXM: OXM_OF_VLAN_VID(1abc) -> 1abc/1fff,--
+OF1.0: 0abc/0,00/1 -> 1abc/1fff
+OF1.1: 0abc/0,00/1 -> 1abc/1fff
+])
+
+dnl [4]
+AT_CHECK([ovs-ofctl check-vlan b000 f000], [0], [dnl
+dl_vlan_pcp=5 -> b000/f000
+NXM: NXM_OF_VLAN_TCI_W(b000/f000) -> b000/f000
+OXM: OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(05) -> 1000/1000,05
+OF1.0: 0000/1,05/0 -> b000/f000
+OF1.1: fffe/0,05/0 -> b000/f000
+])
+
+dnl [5]
+AT_CHECK([ovs-ofctl check-vlan babc ffff], [0], [dnl
+dl_vlan=2748,dl_vlan_pcp=5 -> babc/ffff
+NXM: NXM_OF_VLAN_TCI(babc) -> babc/ffff
+OXM: OXM_OF_VLAN_VID(1abc), OXM_OF_VLAN_PCP(05) -> 1abc/1fff,05
+OF1.0: 0abc/0,05/0 -> babc/ffff
+OF1.1: 0abc/0,05/0 -> babc/ffff
+])
+
+dnl [6]
+AT_CHECK([ovs-ofctl check-vlan 0000 0fff], [0], [dnl
+vlan_tci=0x0000/0x0fff -> 0000/0fff
+NXM: NXM_OF_VLAN_TCI_W(0000/0fff) -> 0000/0fff
+OXM: OXM_OF_VLAN_VID_W(0000/0fff) -> 0000/0fff,--
+OF1.0: 0000/0,00/1 -> 1000/1fff
+OF1.1: 0000/0,00/1 -> 1000/1fff
+])
+
+dnl [7]
+AT_CHECK([ovs-ofctl check-vlan 0000 f000], [0], [dnl
+vlan_tci=0x0000/0xf000 -> 0000/f000
+NXM: NXM_OF_VLAN_TCI_W(0000/f000) -> 0000/f000
+OXM: OXM_OF_VLAN_VID_W(0000/1000) -> 0000/1000,--
+OF1.0: ffff/0,00/1 -> 0000/ffff
+OF1.1: ffff/0,00/1 -> 0000/ffff
+])
+
+dnl [8]
+AT_CHECK([ovs-ofctl check-vlan 0000 efff], [0], [dnl
+vlan_tci=0x0000/0xefff -> 0000/efff
+NXM: NXM_OF_VLAN_TCI_W(0000/efff) -> 0000/efff
+OXM: OXM_OF_VLAN_VID_W(0000/0fff) -> 0000/0fff,--
+OF1.0: 0000/0,00/0 -> 1000/ffff
+OF1.1: 0000/0,00/0 -> 1000/ffff
+])
+
+dnl [9]
+AT_CHECK([ovs-ofctl check-vlan 1001 1001], [0], [dnl
+vlan_tci=0x1001/0x1001 -> 1001/1001
+NXM: NXM_OF_VLAN_TCI_W(1001/1001) -> 1001/1001
+OXM: OXM_OF_VLAN_VID_W(1001/1001) -> 1001/1001,--
+OF1.0: 0001/0,00/1 -> 1001/1fff
+OF1.1: 0001/0,00/1 -> 1001/1fff
+])
+
+dnl [10]
+AT_CHECK([ovs-ofctl check-vlan 3000 3000], [0], [dnl
+vlan_tci=0x3000/0x3000 -> 3000/3000
+NXM: NXM_OF_VLAN_TCI_W(3000/3000) -> 3000/3000
+OXM: OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01) -> 1000/1000,01
+OF1.0: 0000/1,01/0 -> 3000/f000
+OF1.1: fffe/0,01/0 -> 3000/f000
+])
+AT_CHECK
+AT_CLEANUP
+
+dnl Check that "-F openflow10" rejects a flow_mod with unsupported features,
+dnl such as tunnels and metadata.
+AT_SETUP([ovs-ofctl -F option and NXM features])
 AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy tun_id=123,actions=drop],
   [1], [], [ovs-ofctl: none of the usable flow formats (NXM) is among the allowed flow formats (OpenFlow10)
 ])
+AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy metadata=123,actions=drop],
+  [1], [], [ovs-ofctl: none of the usable flow formats (NXM) is among the allowed flow formats (OpenFlow10)
+])
 AT_CLEANUP
 
 dnl Check that "-F nxm" really forces add-flow to use the NXM flow format.
@@ -682,3 +1843,142 @@ ofp_util|INFO|post: @&t@
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+dnl Check that --sort and --rsort works with dump-flows
+dnl Default field is 'priority'. Flow entries are displayed based
+dnl on field to sort.
+AT_SETUP([ovs-ofctl dump-flows with sorting])
+OVS_VSWITCHD_START
+AT_KEYWORDS([sort])
+AT_DATA([allflows.txt], [[
+priority=4,in_port=23213 actions=output:42
+priority=5,in_port=1029 actions=output:43
+priority=7,in_port=1029 actions=output:43
+priority=3,in_port=1028 actions=output:44
+priority=1,in_port=1026 actions=output:45
+priority=6,in_port=1027 actions=output:64
+priority=2,in_port=1025 actions=output:47
+priority=8,tcp,tp_src=5 actions=drop
+priority=9,tcp,tp_src=6 actions=drop
+]])
+
+AT_CHECK([ovs-ofctl add-flows br0 allflows.txt
+], [0], [ignore])
+AT_CHECK([ovs-ofctl --sort dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=1,in_port=1026 actions=output:45
+ priority=2,in_port=1025 actions=output:47
+ priority=3,in_port=1028 actions=output:44
+ priority=4,in_port=23213 actions=output:42
+ priority=5,in_port=1029 actions=output:43
+ priority=6,in_port=1027 actions=output:64
+ priority=7,in_port=1029 actions=output:43
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=9,tcp,tp_src=6 actions=drop
+])
+AT_CHECK([ovs-ofctl --rsort dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=7,in_port=1029 actions=output:43
+ priority=6,in_port=1027 actions=output:64
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+ priority=3,in_port=1028 actions=output:44
+ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+])
+AT_CHECK([ovs-ofctl --sort=in_port dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+ priority=6,in_port=1027 actions=output:64
+ priority=3,in_port=1028 actions=output:44
+ priority=7,in_port=1029 actions=output:43
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=8,tcp,tp_src=5 actions=drop
+])
+AT_CHECK([ovs-ofctl --rsort=in_port dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=4,in_port=23213 actions=output:42
+ priority=7,in_port=1029 actions=output:43
+ priority=5,in_port=1029 actions=output:43
+ priority=3,in_port=1028 actions=output:44
+ priority=6,in_port=1027 actions=output:64
+ priority=1,in_port=1026 actions=output:45
+ priority=2,in_port=1025 actions=output:47
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=8,tcp,tp_src=5 actions=drop
+])
+AT_CHECK([ovs-ofctl --sort=tcp_src dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=7,in_port=1029 actions=output:43
+ priority=6,in_port=1027 actions=output:64
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+ priority=3,in_port=1028 actions=output:44
+ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+])
+AT_CHECK(
+  [ovs-ofctl --sort=in_port --sort=tcp_src dump-flows br0 | ofctl_strip], [0],
+  [ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+ priority=6,in_port=1027 actions=output:64
+ priority=3,in_port=1028 actions=output:44
+ priority=7,in_port=1029 actions=output:43
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=9,tcp,tp_src=6 actions=drop
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ovs-ofctl diff-flows])
+OVS_VSWITCHD_START
+
+# Add tons of flows to br0.
+for i in `seq 0 1023`; do echo "dl_vlan=$i,actions=drop"; done > add-flows.txt
+AT_CHECK([ovs-ofctl add-flows br0 add-flows.txt])
+
+# Dump them and compare against what we expect by hand, then with diff-flows.
+for i in `seq 0 1023`; do echo " dl_vlan=$i actions=drop"; done | sort > expout
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sed '/NXST_FLOW/d' | sort],
+  [0], [expout])
+AT_CHECK([ovs-ofctl diff-flows br0 add-flows.txt])
+
+# Remove even-numbered flows, compare again.
+for i in `seq 0 1023 2`; do echo "dl_vlan=$i"; done > del-flows.txt
+AT_CHECK([ovs-ofctl del-flows br0 - < del-flows.txt])
+for i in `seq 0 1023 2`; do echo "+dl_vlan=$i actions=drop"; done | sort > expout
+AT_CHECK([ovs-ofctl diff-flows br0 add-flows.txt | sort], [0], [expout])
+for i in `seq 0 1023 2`; do echo "-dl_vlan=$i actions=drop"; done | sort > expout
+AT_CHECK([ovs-ofctl diff-flows add-flows.txt br0 | sort], [0], [expout])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl ofpacts that differ bytewise don't necessarily differ when
+dnl converted to another representation, such as OpenFlow 1.0
+dnl or to a string.  "resubmit(,1)" is an example of an action
+dnl of this type: "ofpact_resubmit"s can differ in their "compat"
+dnl values even though this doesn't affect the string format.
+dnl
+dnl This test checks that "ovs-ofctl diff-flows" doesn't report
+dnl false ofpacts differences.
+AT_SETUP([ovs-ofctl diff-flows - suppress false differences])
+OVS_VSWITCHD_START
+AT_DATA([flows.txt], [actions=resubmit(,1)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl diff-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl add-flow br0 idle_timeout=60,dl_vlan=9,actions=output:1])
+AT_CHECK([ovs-ofctl diff-flows br0 flows.txt], [2], [dnl
+-dl_vlan=9 idle_timeout=60 actions=output:1
+])
+AT_CHECK([ovs-ofctl add-flow br0 hard_timeout=120,cookie=1234,dl_vlan=9,actions=output:1])
+AT_CHECK([ovs-ofctl diff-flows flows.txt br0], [2], [dnl
++dl_vlan=9 cookie=0x4d2 hard_timeout=120 actions=output:1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 584ee79..84cc272 100644 (file)
@@ -4,7 +4,7 @@ dnl Creates an empty database in the current directory and then starts
 dnl an ovsdb-server on it for ovs-vsctl to connect to.
 m4_define([OVS_VSCTL_SETUP],
   [OVSDB_INIT([db])
-   AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db >/dev/null 2>&1], [0], [ignore], [ignore])])
+   AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db >/dev/null 2>&1], [0], [ignore], [ignore])])
 
 dnl OVS_VSCTL_CLEANUP
 dnl
@@ -15,17 +15,17 @@ dnl RUN_OVS_VSCTL(COMMAND, ...)
 dnl
 dnl Executes each ovs-vsctl COMMAND.
 m4_define([RUN_OVS_VSCTL],
-  [m4_foreach([command], [$@], [ovs-vsctl --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket -- command
+  [m4_foreach([command], [$@], [ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket -- command
 ])])
 m4_define([RUN_OVS_VSCTL_ONELINE],
-  [m4_foreach([command], [$@], [ovs-vsctl --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket --oneline -- command
+  [m4_foreach([command], [$@], [ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket --oneline -- command
 ])])
 
 dnl RUN_OVS_VSCTL_TOGETHER(COMMAND, ...)
 dnl
 dnl Executes each ovs-vsctl COMMAND in a single run of ovs-vsctl.
 m4_define([RUN_OVS_VSCTL_TOGETHER],
-  [ovs-vsctl --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket --oneline dnl
+  [ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket --oneline dnl
 m4_foreach([command], [$@], [ -- command])])
 
 dnl CHECK_BRIDGES([BRIDGE, PARENT, VLAN], ...)
@@ -741,7 +741,7 @@ AT_CLEANUP
 
 AT_SETUP([database commands -- conditions])
 AT_KEYWORDS([ovs-vsctl])
-trap 'kill `cat pid`' 0
+ON_EXIT([kill `cat pid`])
 OVS_VSCTL_SETUP
 AT_CHECK(
   [RUN_OVS_VSCTL_TOGETHER(
@@ -769,7 +769,7 @@ AT_CHECK(
 
 ])
 m4_define([VSCTL_CHECK_FIND],
-  [AT_CHECK([ovs-vsctl --bare --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket -- --columns=name find bridge '$1' | sort | xargs echo], [0], [$2
+  [AT_CHECK([echo `ovs-vsctl --bare --timeout=5 --no-wait -vreconnect:emer --db=unix:socket -- --columns=name find bridge '$1' | sort`], [0], [$2
 ])])
 
 # Arithmetic relational operators without keys.
@@ -896,11 +896,6 @@ AT_CLEANUP
 AT_SETUP([database commands -- wait-until must wait])
 AT_KEYWORDS([ovs-vsctl])
 
-# Disable lcov for this test.  All the programs running in parallel
-# race badly on access to profiling data.
-DISABLE_LCOV=true
-export DISABLE_LCOV
-
 OVS_VSCTL_SETUP
 
 # Start ovs-vsctls in background.
@@ -985,19 +980,19 @@ AT_SETUP([unreferenced record warnings])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
 AT_CHECK(
-  [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket \
+  [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:emer --db=unix:socket \
      -- create Bridge name=br0 | $srcdir/uuidfilt.pl],
   [0], [<0>
 ], [vsctl|WARN|applying "create" command to table Bridge without --id option will have no effect
 ], [OVS_VSCTL_CLEANUP])
 AT_CHECK(
-  [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket \
+  [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:emer --db=unix:socket \
      -- --id=@br0 create Bridge name=br0 | $srcdir/uuidfilt.pl],
   [0], [<0>
 ], [vsctl|WARN|row id "@br0" was created but no reference to it was inserted, so it will not actually appear in the database
 ], [OVS_VSCTL_CLEANUP])
 AT_CHECK(
-  [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket \
+  [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:emer --db=unix:socket \
      -- --id=@eth0_iface create Interface name=eth0 \
      -- --id=@eth0 create Port name=eth0 interfaces=@eth0_iface \
      -- --id=@m0 create Mirror name=m0 output_port=@eth0 \
@@ -1041,3 +1036,51 @@ stp_enable          : false
 ]], [ignore], [test ! -e pid || kill `cat pid`])
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
+
+dnl This test will create a linux-htb QoS record that
+dnl points to a few queues and use it on a1 and a2 port.
+dnl It also destroys all records from Qos and Queue table.
+AT_SETUP([--all option on destroy command])
+AT_KEYWORDS([ovs-vsctl])
+OVS_VSCTL_SETUP
+AT_CHECK([RUN_OVS_VSCTL(
+   [add-br a],
+   [add-port a a1],
+   [add-port a a2])], [0], [], [], [OVS_VSCTL_CLEANUP])
+CHECK_BRIDGES([a, a, 0])
+CHECK_PORTS([a], [a1], [a2])
+CHECK_IFACES([a], [a1], [a2])
+AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
+   [set Port a1 qos=@newqos],
+   [set Port a2 qos=@newqos],
+   [--id=@newqos create QoS type=linux-htb other-config:max-rate=1000000000 queues=0=@q0,1=@q1],
+   [--id=@q0 create Queue other-config:min-rate=100000000 other-config:max-rate=100000000],
+   [--id=@q1 create Queue other-config:min-rate=500000000])], [0], [ignore], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+   [--columns=other_config,type list Qos])], [0],
+[other_config        : {max-rate="1000000000"}
+type                : linux-htb
+], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+   [--columns=other_config list Queue | sort | xargs echo])], [0],
+[other_config : {max-rate=100000000, min-rate=100000000} other_config : {min-rate=500000000}
+], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+   [clear Port a1 qos],
+   [clear Port a2 qos])], [0], [], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+   [--columns=qos list Port a1 a2])], [0],
+[[qos                 : []
+
+qos                 : []
+]], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+   [--all destroy Qos])], [0], [], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+   [-- list Qos])], [0], [], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+   [--all destroy Queue])], [0], [], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+   [-- list Queue])], [0], [], [], [OVS_VSCTL_CLEANUP])
+OVS_VSCTL_CLEANUP
+AT_CLEANUP
index c57bbc9..29d4737 100644 (file)
@@ -9,19 +9,20 @@ PYTHONPATH=`pwd`:$PYTHONPATH
 export PYTHONPATH
 
 OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
 OVS_PKGDATADIR=`pwd`; export OVS_PKGDATADIR
 cp "$top_srcdir/vswitchd/vswitch.ovsschema" .
 
 cp "$top_srcdir/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync" \
    ovs-xapi-sync
 
-trap 'kill `cat pid ovs-xapi-sync.pid`' 0
+ON_EXIT([kill `cat pid ovs-xapi-sync.pid`])
 
 mkdir var var/run
 touch var/run/xapi_init_complete.cookie
 
 ovs_vsctl () {
-    ovs-vsctl --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket "$@"
+    ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket "$@"
 }
 
 # Start ovsdb-server.
index 91f1671..48e7489 100644 (file)
@@ -21,7 +21,7 @@ m4_define([OVSDB_CHECK_IDL_C],
    AT_KEYWORDS([ovsdb server idl positive $5])
    AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
                   [0], [stdout], [ignore])
-   AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
    m4_if([$2], [], [],
      [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore], [kill `cat pid`])])
    AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl unix:socket $3],
@@ -38,7 +38,7 @@ m4_define([OVSDB_CHECK_IDL_PY],
    AT_KEYWORDS([ovsdb server idl positive Python $5])
    AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
                   [0], [stdout], [ignore])
-   AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
    m4_if([$2], [], [],
      [AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore], [kill `cat pid`])])
    AT_CHECK([$PYTHON $srcdir/test-ovsdb.py  -t10 idl $srcdir/idltest.ovsschema unix:socket $3],
index 1e319dd..167b44c 100644 (file)
@@ -24,9 +24,9 @@ m4_define([OVSDB_CHECK_MONITOR],
    m4_foreach([txn], [$3],
      [AT_CHECK([ovsdb-tool transact db 'txn'], [0], [ignore], [ignore])])
    AT_CAPTURE_FILE([ovsdb-server-log])
-   AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/server-pid --remote=punix:socket --unixctl="`pwd`"/unixctl --log-file="`pwd`"/ovsdb-server-log db >/dev/null 2>&1],
+   AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/server-pid --remote=punix:socket --unixctl="`pwd`"/unixctl --log-file="`pwd`"/ovsdb-server-log db >/dev/null 2>&1],
             [0], [], [])
-   AT_CHECK([ovsdb-client -vjsonrpc --detach --pidfile="`pwd`"/client-pid -d json monitor --format=csv unix:socket $4 $5 $8 > output], 
+   AT_CHECK([ovsdb-client -vjsonrpc --detach --no-chdir --pidfile="`pwd`"/client-pid -d json monitor --format=csv unix:socket $4 $5 $8 > output], 
             [0], [ignore], [ignore], [kill `cat server-pid`])
    m4_foreach([txn], [$6],
      [AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0],
index f5db1a8..b0a3377 100644 (file)
@@ -24,7 +24,7 @@ m4_define([OVSDB_CHECK_EXECUTION],
    AT_KEYWORDS([ovsdb server positive unix $5])
    $2 > schema
    AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
-   AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
    m4_foreach([txn], [$3], 
      [AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0], [stdout], [ignore],
      [test ! -e pid || kill `cat pid`])
@@ -134,7 +134,7 @@ AT_SETUP([ovsdb-client get-schema-version])
 AT_KEYWORDS([ovsdb server positive])
 ordinal_schema > schema
 AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
 AT_CHECK([ovsdb-client get-schema-version unix:socket ordinals], [0], [5.1.3
 ])
 OVSDB_SERVER_SHUTDOWN
@@ -144,7 +144,7 @@ AT_SETUP([database multiplexing implementation])
 AT_KEYWORDS([ovsdb server positive])
 ordinal_schema > schema
 AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
 AT_CHECK(
   [[ovsdb-client list-dbs unix:socket]], 
   [0], [ordinals
@@ -172,7 +172,7 @@ AT_CHECK(
        {"op": "insert",
         "table": "Manager",
         "row": {"manager": "punix:socket"}}]']], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --remote=db:Manager,manager --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=db:Manager,manager --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
 AT_CHECK(
   [[ovsdb-client transact unix:socket \
      '["mydb",
@@ -219,7 +219,7 @@ AT_CHECK(
 AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
 SSL_PORT=`cat stdout`
 AT_CHECK(
-  [ovsdb-server --detach --pidfile="`pwd`"/pid \
+  [ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid \
         --private-key=db:SSL,private_key \
         --certificate=db:SSL,certificate \
         --ca-cert=db:SSL,ca_cert \
@@ -248,9 +248,16 @@ AT_CLEANUP
 AT_SETUP([compacting online])
 AT_KEYWORDS([ovsdb server compact])
 ordinal_schema > schema
-touch .db.~lock~
+dnl Make sure that "ovsdb-tool create" works with a dangling symlink for
+dnl the database and the lockfile, creating the target of each symlink rather
+dnl than replacing the symlinks with regular files.
+mkdir dir
+ln -s dir/db db
+ln -s dir/.db.~lock~ .db.~lock~
+AT_SKIP_IF([test ! -h db || test ! -h .db.~lock~])
 AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket --log-file="`pwd`"/ovsdb-server.log db], [0], [ignore], [ignore])
+dnl Start ovsdb-server.
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket --log-file="`pwd`"/ovsdb-server.log db], [0], [ignore], [ignore])
 AT_CAPTURE_FILE([ovsdb-server.log])
 dnl Do a bunch of random transactions that put crap in the database log.
 AT_CHECK(
@@ -318,12 +325,18 @@ _uuid                                name  number
 dnl Now compact the database in-place.
 AT_CHECK([[ovs-appctl -t "`pwd`"/unixctl ovsdb-server/compact]],
   [0], [], [ignore], [test ! -e pid || kill `cat pid`])
+dnl Make sure that "db" is still a symlink to dir/db instead of getting
+dnl replaced by a regular file, ditto for .db.~lock~.
+AT_CHECK([test -h db])
+AT_CHECK([test -h .db.~lock~])
+AT_CHECK([test -f dir/db])
+AT_CHECK([test -f dir/.db.~lock~])
 dnl We can't fully re-check the contents of the database log, because the
 dnl order of the records is not predictable, but there should only be 4 lines
 dnl in it now.
 AT_CAPTURE_FILE([db])
-AT_CHECK([wc -l < db], [0], [4
-], [], [test ! -e pid || kill `cat pid`])
+AT_CHECK([test `wc -l < db` -eq 4], [0], [], [],
+  [test ! -e pid || kill `cat pid`])
 dnl And check that the dumped data is the same too:
 AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout], [ignore],
   [test ! -e pid || kill `cat pid`])
@@ -348,8 +361,8 @@ AT_CHECK(
   [0], [[[{"count":3}]
 ]], [ignore], [test ! -e pid || kill `cat pid`])
 dnl There should be 6 lines in the log now.
-AT_CHECK([wc -l < db], [0], [6
-], [], [test ! -e pid || kill `cat pid`])
+AT_CHECK([test `wc -l < db` -eq 6], [0], [], [],
+  [test ! -e pid || kill `cat pid`])
 dnl Then check that the dumped data is correct.
 AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout], [ignore],
   [test ! -e pid || kill `cat pid`])
@@ -389,7 +402,7 @@ m4_define([OVSDB_CHECK_EXECUTION],
    SSL_PORT=`cat stdout`
    PKIDIR=$abs_top_builddir/tests
    AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
-   AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:$SSL_PORT:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+   AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:$SSL_PORT:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
    m4_foreach([txn], [$3], 
      [AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:127.0.0.1:$SSL_PORT 'txn'], [0], [stdout], [ignore],
      [test ! -e pid || kill `cat pid`])
index 2d19b32..e4f4a29 100644 (file)
@@ -49,8 +49,18 @@ AT_CLEANUP
 AT_SETUP([ovsdb-tool compact])
 AT_KEYWORDS([ovsdb file positive])
 ordinal_schema > schema
-touch .db.~lock~
+dnl Make sure that "ovsdb-tool create" works with a dangling symlink,
+dnl creating the target of the symlink rather than replacing the symlink
+dnl with a regular file, and that the lockfile gets created relative to
+dnl the symlink's target.
+mkdir dir
+: > dir/.db.~lock~
+ln -s dir/db db
+AT_SKIP_IF([test ! -h db])
 AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore])
+AT_CHECK([test ! -e .db.~lock])
+AT_CHECK([test -h db])
+AT_CHECK([test -f dir/db])
 dnl Do a bunch of random transactions that put crap in the database log.
 AT_CHECK(
   [[for pair in 'zero 0' 'one 1' 'two 2' 'three 3' 'four 4' 'five 5'; do
@@ -117,12 +127,16 @@ _uuid                                name  number
 dnl Now compact the database in-place.
 touch .db.tmp.~lock~
 AT_CHECK([[ovsdb-tool compact db]], [0], [], [ignore])
+dnl Make sure that "db" is still a symlink to dir/db instead of getting
+dnl replaced by a regular file.
+AT_CHECK([test ! -e .db.~lock])
+AT_CHECK([test -h db])
+AT_CHECK([test -f dir/db])
 dnl We can't fully re-check the contents of the database log, because the
 dnl order of the records is not predictable, but there should only be 4 lines
 dnl in it now.
 AT_CAPTURE_FILE([db])
-AT_CHECK([wc -l < db], [0], [4
-])
+AT_CHECK([test `wc -l < db` -eq 4])
 dnl And check that the dumped data is the same too:
 AT_CHECK([[ovsdb-server --unixctl="`pwd`"/unixctl --remote=punix:socket --run "ovsdb-client dump unix:socket ordinals" db]],
   [0], [stdout], [ignore])
@@ -185,8 +199,7 @@ dnl We can't fully re-check the contents of the database log, because the
 dnl order of the records is not predictable, but there should only be 4 lines
 dnl in it now.
 AT_CAPTURE_FILE([db])
-AT_CHECK([wc -l < db], [0], [4
-])
+AT_CHECK([test `wc -l < db` -eq 4])
 dnl And check that the dumped data is the same except for the removed column:
 AT_CHECK([[ovsdb-server --unixctl="`pwd`"/unixctl --remote=punix:socket --run "ovsdb-client dump unix:socket ordinals" db]],
   [0], [stdout], [ignore])
@@ -248,8 +261,7 @@ dnl We can't fully re-check the contents of the database log, because the
 dnl order of the records is not predictable, but there should only be 4 lines
 dnl in it now.
 AT_CAPTURE_FILE([db])
-AT_CHECK([wc -l < db], [0], [4
-])
+AT_CHECK([test `wc -l < db` -eq 4])
 dnl And check that the dumped data is the same except for the added column:
 AT_CHECK([[ovsdb-server --unixctl="`pwd`"/unixctl --remote=punix:socket --run "ovsdb-client dump unix:socket ordinals" db]],
   [0], [stdout], [ignore])
index e7e990c..19874b1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
index 16e264d..f2d9b82 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 Nicira Networks.
+/* Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 
 #include "flow.h"
+#include "ofp-actions.h"
 #include "ofpbuf.h"
 #include "random.h"
 
@@ -64,22 +65,24 @@ slave_enabled_cb(uint16_t slave_id, void *aux)
     return slave ? slave->enabled : false;
 }
 
-static struct nx_action_bundle *
+static struct ofpact_bundle *
 parse_bundle_actions(char *actions)
 {
-    struct nx_action_bundle *nab;
-    struct ofpbuf b;
+    struct ofpact_bundle *bundle;
+    struct ofpbuf ofpacts;
+    struct ofpact *action;
 
-    ofpbuf_init(&b, 0);
-    bundle_parse_load(&b, actions);
-    nab = ofpbuf_steal_data(&b);
-    ofpbuf_uninit(&b);
+    ofpbuf_init(&ofpacts, 0);
+    bundle_parse_load(actions, &ofpacts);
+    action = ofpacts.data;
+    bundle = ofpact_get_BUNDLE(xmemdup(action, action->len));
+    ofpbuf_uninit(&ofpacts);
 
-    if (ntohs(nab->n_slaves) > MAX_SLAVES) {
+    if (bundle->n_slaves > MAX_SLAVES) {
         ovs_fatal(0, "At most %u slaves are supported", MAX_SLAVES);
     }
 
-    return nab;
+    return bundle;
 }
 
 static const char *
@@ -101,7 +104,7 @@ int
 main(int argc, char *argv[])
 {
     bool ok = true;
-    struct nx_action_bundle *nab;
+    struct ofpact_bundle *bundle;
     struct flow *flows;
     size_t i, n_permute, old_n_enabled;
     struct slave_group sg;
@@ -114,12 +117,12 @@ main(int argc, char *argv[])
         ovs_fatal(0, "usage: %s bundle_action", program_name);
     }
 
-    nab = parse_bundle_actions(argv[1]);
+    bundle = parse_bundle_actions(argv[1]);
 
     /* Generate 'slaves' array. */
     sg.n_slaves = 0;
-    for (i = 0; i < ntohs(nab->n_slaves); i++) {
-        uint16_t slave_id = bundle_get_slave(nab, i);
+    for (i = 0; i < bundle->n_slaves; i++) {
+        uint16_t slave_id = bundle->slaves[i];
 
         if (slave_lookup(&sg, slave_id)) {
             ovs_fatal(0, "Redundant slaves are not supported. ");
@@ -136,10 +139,6 @@ main(int argc, char *argv[])
         flows[i].regs[0] = OFPP_NONE;
     }
 
-    if (bundle_check(nab, 1024, flows)) {
-        ovs_fatal(0, "Bundle action fails to check.");
-    }
-
     /* Cycles through each possible liveness permutation for the given
      * n_slaves.  The initial state is equivalent to all slaves down, so we
      * skip it by starting at i = 1. We do one extra iteration to cover
@@ -188,23 +187,19 @@ main(int argc, char *argv[])
             uint16_t old_slave_id, ofp_port;
 
             old_slave_id = flow->regs[0];
-            ofp_port = bundle_execute(nab, flow, slave_enabled_cb, &sg);
-            bundle_execute_load(nab, flow, slave_enabled_cb, &sg);
-            if (flow->regs[0] != ofp_port) {
-                ovs_fatal(0, "bundle_execute_load() and bundle_execute() "
-                          "disagree");
-            }
+            ofp_port = bundle_execute(bundle, flow, slave_enabled_cb, &sg);
+            flow->regs[0] = ofp_port;
 
-            if (flow->regs[0] != OFPP_NONE) {
-                slave_lookup(&sg, flow->regs[0])->flow_count++;
+            if (ofp_port != OFPP_NONE) {
+                slave_lookup(&sg, ofp_port)->flow_count++;
             }
 
-            if (old_slave_id != flow->regs[0]) {
+            if (old_slave_id != ofp_port) {
                 changed++;
             }
         }
 
-        if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) {
+        if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) {
             perfect = active == old_active ? 0.0 : 1.0;
         } else {
             if (old_n_enabled || n_enabled) {
@@ -229,7 +224,7 @@ main(int argc, char *argv[])
             if (slave->enabled) {
                 double perfect_fp;
 
-                if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) {
+                if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) {
                     perfect_fp = j == active ? 1.0 : 0.0;
                 } else {
                     perfect_fp = 1.0 / n_enabled;
@@ -262,7 +257,7 @@ main(int argc, char *argv[])
         old_n_enabled = n_enabled;
     }
 
-    free(nab);
+    free(bundle);
     free(flows);
     return ok ? 0 : 1;
 }
index 1c5bf38..ab39627 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 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.
index 1a07790..e7cf734 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -45,6 +45,7 @@
     /*        FWW_* bit(s)                member name  name     */  \
     /*        --------------------------  -----------  -------- */  \
     CLS_FIELD(0,                          tun_id,      TUN_ID)      \
+    CLS_FIELD(0,                          metadata,    METADATA)    \
     CLS_FIELD(0,                          nw_src,      NW_SRC)      \
     CLS_FIELD(0,                          nw_dst,      NW_DST)      \
     CLS_FIELD(FWW_IN_PORT,                in_port,     IN_PORT)     \
@@ -52,8 +53,8 @@
     CLS_FIELD(FWW_DL_TYPE,                dl_type,     DL_TYPE)     \
     CLS_FIELD(0,                          tp_src,      TP_SRC)      \
     CLS_FIELD(0,                          tp_dst,      TP_DST)      \
-    CLS_FIELD(FWW_DL_SRC,                 dl_src,      DL_SRC)      \
-    CLS_FIELD(FWW_DL_DST | FWW_ETH_MCAST, dl_dst,      DL_DST)      \
+    CLS_FIELD(0,                          dl_src,      DL_SRC)      \
+    CLS_FIELD(0,                          dl_dst,      DL_DST)      \
     CLS_FIELD(FWW_NW_PROTO,               nw_proto,    NW_PROTO)    \
     CLS_FIELD(FWW_NW_DSCP,                nw_tos,      NW_DSCP)
 
@@ -202,11 +203,20 @@ match(const struct cls_rule *wild, const struct flow *fixed)
             eq = !((fixed->tp_src ^ wild->flow.tp_src) & wild->wc.tp_src_mask);
         } else if (f_idx == CLS_F_IDX_TP_DST) {
             eq = !((fixed->tp_dst ^ wild->flow.tp_dst) & wild->wc.tp_dst_mask);
+        } else if (f_idx == CLS_F_IDX_DL_SRC) {
+            eq = eth_addr_equal_except(fixed->dl_src, wild->flow.dl_src,
+                                       wild->wc.dl_src_mask);
+        } else if (f_idx == CLS_F_IDX_DL_DST) {
+            eq = eth_addr_equal_except(fixed->dl_dst, wild->flow.dl_dst,
+                                       wild->wc.dl_dst_mask);
         } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
             eq = !((fixed->vlan_tci ^ wild->flow.vlan_tci)
                    & wild->wc.vlan_tci_mask);
         } else if (f_idx == CLS_F_IDX_TUN_ID) {
             eq = !((fixed->tun_id ^ wild->flow.tun_id) & wild->wc.tun_id_mask);
+        } else if (f_idx == CLS_F_IDX_METADATA) {
+            eq = !((fixed->metadata ^ wild->flow.metadata)
+                   & wild->wc.metadata_mask);
         } else if (f_idx == CLS_F_IDX_NW_DSCP) {
             eq = !((fixed->nw_tos ^ wild->flow.nw_tos) & IP_DSCP_MASK);
         } else {
@@ -257,6 +267,9 @@ static ovs_be32 nw_dst_values[] = { CONSTANT_HTONL(0xc0a80002),
 static ovs_be64 tun_id_values[] = {
     0,
     CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) };
+static ovs_be64 metadata_values[] = {
+    0,
+    CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) };
 static uint16_t in_port_values[] = { 1, OFPP_LOCAL };
 static ovs_be16 vlan_tci_values[] = { CONSTANT_HTONS(101), CONSTANT_HTONS(0) };
 static ovs_be16 dl_type_values[]
@@ -279,6 +292,9 @@ init_values(void)
     values[CLS_F_IDX_TUN_ID][0] = &tun_id_values[0];
     values[CLS_F_IDX_TUN_ID][1] = &tun_id_values[1];
 
+    values[CLS_F_IDX_METADATA][0] = &metadata_values[0];
+    values[CLS_F_IDX_METADATA][1] = &metadata_values[1];
+
     values[CLS_F_IDX_IN_PORT][0] = &in_port_values[0];
     values[CLS_F_IDX_IN_PORT][1] = &in_port_values[1];
 
@@ -316,6 +332,7 @@ init_values(void)
 #define N_NW_SRC_VALUES ARRAY_SIZE(nw_src_values)
 #define N_NW_DST_VALUES ARRAY_SIZE(nw_dst_values)
 #define N_TUN_ID_VALUES ARRAY_SIZE(tun_id_values)
+#define N_METADATA_VALUES ARRAY_SIZE(metadata_values)
 #define N_IN_PORT_VALUES ARRAY_SIZE(in_port_values)
 #define N_VLAN_TCI_VALUES ARRAY_SIZE(vlan_tci_values)
 #define N_DL_TYPE_VALUES ARRAY_SIZE(dl_type_values)
@@ -363,6 +380,7 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls)
         flow.nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)];
         flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)];
         flow.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.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)];
@@ -471,10 +489,16 @@ make_rule(int wc_fields, unsigned int priority, int value_pat)
             rule->cls_rule.wc.tp_src_mask = htons(UINT16_MAX);
         } else if (f_idx == CLS_F_IDX_TP_DST) {
             rule->cls_rule.wc.tp_dst_mask = htons(UINT16_MAX);
+        } else if (f_idx == CLS_F_IDX_DL_SRC) {
+            memset(rule->cls_rule.wc.dl_src_mask, 0xff, ETH_ADDR_LEN);
+        } else if (f_idx == CLS_F_IDX_DL_DST) {
+            memset(rule->cls_rule.wc.dl_dst_mask, 0xff, ETH_ADDR_LEN);
         } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
             rule->cls_rule.wc.vlan_tci_mask = htons(UINT16_MAX);
         } else if (f_idx == CLS_F_IDX_TUN_ID) {
             rule->cls_rule.wc.tun_id_mask = htonll(UINT64_MAX);
+        } else if (f_idx == CLS_F_IDX_METADATA) {
+            rule->cls_rule.wc.metadata_mask = htonll(UINT64_MAX);
         } else {
             NOT_REACHED();
         }
index 86de430..5f736d6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 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.
index 4fa07ea..b759cf9 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2010, 2011 Nicira Networks.
+# Copyright (c) 2010, 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.
index a9d1ab9..3d4eaf1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
index 55f7dee..33417e0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@
 int
 main(int argc OVS_UNUSED, char *argv[])
 {
-    struct ofp_match expected_match;
+    struct ofp10_match expected_match;
     FILE *flows, *pcap;
     int retval;
     int n = 0, errors = 0;
@@ -55,7 +55,7 @@ main(int argc OVS_UNUSED, char *argv[])
 
     while (fread(&expected_match, sizeof expected_match, 1, flows)) {
         struct ofpbuf *packet;
-        struct ofp_match extracted_match;
+        struct ofp10_match extracted_match;
         struct cls_rule rule;
         struct flow flow;
 
@@ -70,11 +70,11 @@ main(int argc OVS_UNUSED, char *argv[])
 
         flow_extract(packet, 0, 0, 1, &flow);
         cls_rule_init_exact(&flow, 0, &rule);
-        ofputil_cls_rule_to_match(&rule, &extracted_match);
+        ofputil_cls_rule_to_ofp10_match(&rule, &extracted_match);
 
         if (memcmp(&expected_match, &extracted_match, sizeof expected_match)) {
-            char *exp_s = ofp_match_to_string(&expected_match, 2);
-            char *got_s = ofp_match_to_string(&extracted_match, 2);
+            char *exp_s = ofp10_match_to_string(&expected_match, 2);
+            char *got_s = ofp10_match_to_string(&extracted_match, 2);
             errors++;
             printf("mismatch on packet #%d (1-based).\n", n);
             printf("Packet:\n");
index 1acceac..bdf1435 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 0541b8d..dd5aaa9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index be6bcf4..c202eae 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * 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.
index f297dc2..c7c01c8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * 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.
index 0ec1cfc..d9f0bfe 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010 Nicira Networks.
+# 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.
index 9c585aa..7cc708a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 9a24933..86db4aa 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 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.
index b4ddd02..5074818 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 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.
index a9ac17f..b37fd22 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
 
 #include <errno.h>
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
@@ -53,7 +54,7 @@ run_lock_and_unlock(void)
 {
     struct lockfile *lockfile;
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
+    CHECK(lockfile_lock("file", &lockfile), 0);
     lockfile_unlock(lockfile);
 }
 
@@ -62,10 +63,10 @@ run_lock_and_unlock_twice(void)
 {
     struct lockfile *lockfile;
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
+    CHECK(lockfile_lock("file", &lockfile), 0);
     lockfile_unlock(lockfile);
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
+    CHECK(lockfile_lock("file", &lockfile), 0);
     lockfile_unlock(lockfile);
 }
 
@@ -74,8 +75,8 @@ run_lock_blocks_same_process(void)
 {
     struct lockfile *lockfile;
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
-    CHECK(lockfile_lock("file", 0, &lockfile), EDEADLK);
+    CHECK(lockfile_lock("file", &lockfile), 0);
+    CHECK(lockfile_lock("file", &lockfile), EDEADLK);
     lockfile_unlock(lockfile);
 }
 
@@ -84,9 +85,9 @@ run_lock_blocks_same_process_twice(void)
 {
     struct lockfile *lockfile;
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
-    CHECK(lockfile_lock("file", 0, &lockfile), EDEADLK);
-    CHECK(lockfile_lock("file", 0, &lockfile), EDEADLK);
+    CHECK(lockfile_lock("file", &lockfile), 0);
+    CHECK(lockfile_lock("file", &lockfile), EDEADLK);
+    CHECK(lockfile_lock("file", &lockfile), EDEADLK);
     lockfile_unlock(lockfile);
 }
 
@@ -117,10 +118,10 @@ run_lock_blocks_other_process(void)
      * this function that does the wait() call. */
     static struct lockfile *lockfile;
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
+    CHECK(lockfile_lock("file", &lockfile), 0);
     if (do_fork() == CHILD) {
         lockfile_unlock(lockfile);
-        CHECK(lockfile_lock("file", 0, &lockfile), EAGAIN);
+        CHECK(lockfile_lock("file", &lockfile), EAGAIN);
         exit(11);
     }
 }
@@ -130,10 +131,10 @@ run_lock_twice_blocks_other_process(void)
 {
     struct lockfile *lockfile, *dummy;
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
-    CHECK(lockfile_lock("file", 0, &dummy), EDEADLK);
+    CHECK(lockfile_lock("file", &lockfile), 0);
+    CHECK(lockfile_lock("file", &dummy), EDEADLK);
     if (do_fork() == CHILD) {
-        CHECK(lockfile_lock("file", 0, &dummy), EAGAIN);
+        CHECK(lockfile_lock("file", &dummy), EAGAIN);
         exit(11);
     }
 }
@@ -143,74 +144,96 @@ run_lock_and_unlock_allows_other_process(void)
 {
     struct lockfile *lockfile;
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
+    CHECK(lockfile_lock("file", &lockfile), 0);
     lockfile_unlock(lockfile);
 
     if (do_fork() == CHILD) {
-        CHECK(lockfile_lock("file", 0, &lockfile), 0);
+        CHECK(lockfile_lock("file", &lockfile), 0);
         exit(11);
     }
 }
 
 static void
-run_lock_timeout_gets_the_lock(void)
+run_lock_multiple(void)
 {
-    struct lockfile *lockfile;
+    struct lockfile *a, *b, *c, *dummy;
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
+    CHECK(lockfile_lock("a", &a), 0);
+    CHECK(lockfile_lock("b", &b), 0);
+    CHECK(lockfile_lock("c", &c), 0);
 
-    if (do_fork() == CHILD) {
-        lockfile_unlock(lockfile);
-        CHECK(lockfile_lock("file", TIME_UPDATE_INTERVAL * 3, &lockfile), 0);
-        exit(11);
-    } else {
-        long long int now = time_msec();
-        while (time_msec() < now + TIME_UPDATE_INTERVAL) {
-            pause();
-        }
-        lockfile_unlock(lockfile);
-    }
+    lockfile_unlock(a);
+    CHECK(lockfile_lock("a", &a), 0);
+    CHECK(lockfile_lock("a", &dummy), EDEADLK);
+    lockfile_unlock(a);
+
+    lockfile_unlock(b);
+    CHECK(lockfile_lock("a", &a), 0);
+
+    lockfile_unlock(c);
+    lockfile_unlock(a);
 }
 
+/* Checks that locking a dangling symlink works OK.  (It used to hang.) */
 static void
-run_lock_timeout_runs_out(void)
+run_lock_symlink(void)
 {
-    struct lockfile *lockfile;
+    struct lockfile *a, *b, *dummy;
+    struct stat s;
+
+    /* Create a symlink .a.~lock~ pointing to .b.~lock~. */
+    CHECK(symlink(".b.~lock~", ".a.~lock~"), 0);
+    CHECK(lstat(".a.~lock~", &s), 0);
+    CHECK(S_ISLNK(s.st_mode) != 0, 1);
+    CHECK(stat(".a.~lock~", &s), -1);
+    CHECK(errno, ENOENT);
+    CHECK(stat(".b.~lock~", &s), -1);
+    CHECK(errno, ENOENT);
+
+    CHECK(lockfile_lock("a", &a), 0);
+    CHECK(lockfile_lock("a", &dummy), EDEADLK);
+    CHECK(lockfile_lock("b", &dummy), EDEADLK);
+    lockfile_unlock(a);
 
-    CHECK(lockfile_lock("file", 0, &lockfile), 0);
+    CHECK(lockfile_lock("b", &b), 0);
+    CHECK(lockfile_lock("b", &dummy), EDEADLK);
+    CHECK(lockfile_lock("a", &dummy), EDEADLK);
+    lockfile_unlock(b);
 
-    if (do_fork() == CHILD) {
-        lockfile_unlock(lockfile);
-        CHECK(lockfile_lock("file", TIME_UPDATE_INTERVAL, &lockfile),
-              ETIMEDOUT);
-        exit(11);
-    } else {
-        long long int now = time_msec();
-        while (time_msec() < now + TIME_UPDATE_INTERVAL * 3) {
-            pause();
-        }
-        lockfile_unlock(lockfile);
-    }
+    CHECK(lstat(".a.~lock~", &s), 0);
+    CHECK(S_ISLNK(s.st_mode) != 0, 1);
+    CHECK(stat(".a.~lock~", &s), 0);
+    CHECK(S_ISREG(s.st_mode) != 0, 1);
+    CHECK(stat(".b.~lock~", &s), 0);
+    CHECK(S_ISREG(s.st_mode) != 0, 1);
 }
 
+/* Checks that locking a file that is itself a symlink yields a lockfile in the
+ * directory that the symlink points to, named for the target of the
+ * symlink.
+ *
+ * (That is, if "a" is a symlink to "dir/b", then "a"'s lockfile is named
+ * "dir/.b.~lock".) */
 static void
-run_lock_multiple(void)
+run_lock_symlink_to_dir(void)
 {
-    struct lockfile *a, *b, *c, *dummy;
-
-    CHECK(lockfile_lock("a", 0, &a), 0);
-    CHECK(lockfile_lock("b", 0, &b), 0);
-    CHECK(lockfile_lock("c", 0, &c), 0);
-
-    lockfile_unlock(a);
-    CHECK(lockfile_lock("a", 0, &a), 0);
-    CHECK(lockfile_lock("a", 0, &dummy), EDEADLK);
-    lockfile_unlock(a);
-
-    lockfile_unlock(b);
-    CHECK(lockfile_lock("a", 0, &a), 0);
+    struct lockfile *a, *dummy;
+    struct stat s;
+
+    /* Create a symlink "a" pointing to "dir/b". */
+    CHECK(mkdir("dir", 0700), 0);
+    CHECK(symlink("dir/b", "a"), 0);
+    CHECK(lstat("a", &s), 0);
+    CHECK(S_ISLNK(s.st_mode) != 0, 1);
+
+    /* Lock 'a'. */
+    CHECK(lockfile_lock("a", &a), 0);
+    CHECK(lstat("dir/.b.~lock~", &s), 0);
+    CHECK(S_ISREG(s.st_mode) != 0, 1);
+    CHECK(lstat(".a.~lock~", &s), -1);
+    CHECK(errno, ENOENT);
+    CHECK(lockfile_lock("dir/b", &dummy), EDEADLK);
 
-    lockfile_unlock(c);
     lockfile_unlock(a);
 }
 
@@ -236,9 +259,9 @@ static const struct test tests[] = {
     TEST(lock_blocks_other_process),
     TEST(lock_twice_blocks_other_process),
     TEST(lock_and_unlock_allows_other_process),
-    TEST(lock_timeout_gets_the_lock),
-    TEST(lock_timeout_runs_out),
     TEST(lock_multiple),
+    TEST(lock_symlink),
+    TEST(lock_symlink_to_dir),
     TEST(help),
     { NULL, NULL }
 #undef TEST
index 03a666f..8a35567 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 
 #include "flow.h"
+#include "ofp-actions.h"
 #include "random.h"
 #include "util.h"
 
@@ -32,7 +33,7 @@ int
 main(int argc, char *argv[])
 {
     enum { MP_MAX_LINKS = 63 };
-    struct nx_action_multipath mp;
+    struct ofpact_multipath mp;
     bool ok = true;
     int n;
 
@@ -60,11 +61,11 @@ main(int argc, char *argv[])
 
             random_bytes(&flow, sizeof flow);
 
-            mp.max_link = htons(n - 1);
+            mp.max_link = n - 1;
             multipath_execute(&mp, &flow);
             old_link = flow.regs[0];
 
-            mp.max_link = htons(n);
+            mp.max_link = n;
             multipath_execute(&mp, &flow);
             new_link = flow.regs[0];
 
@@ -91,7 +92,7 @@ main(int argc, char *argv[])
                "stddev/expected=%.4f\n",
                n, n + 1, disruption, perfect, distribution);
 
-        switch (ntohs(mp.algorithm)) {
+        switch (mp.algorithm) {
         case NX_MP_ALG_MODULO_N:
             if (disruption < (n < 2 ? .25 : .5)) {
                 fprintf(stderr, "%d -> %d: disruption=%.2f < .5\n",
index 5f30c63..85ab5e7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 9ae897c..dd80766 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "flow.h"
 #include "odp-util.h"
 #include "ofpbuf.h"
+#include "util.h"
 #include "vlog.h"
 
-int
-main(void)
+static int
+parse_keys(void)
 {
+    int exit_code = 0;
     struct ds in;
 
     ds_init(&in);
     vlog_set_levels_from_string("odp_util:console:dbg");
-    while (!ds_get_line(&in, stdin)) {
+    while (!ds_get_test_line(&in, stdin)) {
         enum odp_key_fitness fitness;
         struct ofpbuf odp_key;
         struct flow flow;
         struct ds out;
         int error;
-        char *s;
-
-        /* Delete comments, skip blank lines. */
-        s = ds_cstr(&in);
-        if (*s == '#') {
-            puts(s);
-            continue;
-        }
-        if (strchr(s, '#')) {
-            *strchr(s, '#') = '\0';
-        }
-        if (s[strspn(s, " ")] == '\0') {
-            putchar('\n');
-            continue;
-        }
 
         /* Convert string to OVS DP key. */
         ofpbuf_init(&odp_key, 0);
@@ -85,6 +72,12 @@ main(void)
         ofpbuf_init(&odp_key, 0);
         odp_flow_key_from_flow(&odp_key, &flow);
 
+        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);
@@ -96,5 +89,51 @@ main(void)
     }
     ds_destroy(&in);
 
+    return exit_code;
+}
+
+static int
+parse_actions(void)
+{
+    struct ds in;
+
+    ds_init(&in);
+    vlog_set_levels_from_string("odp_util:console:dbg");
+    while (!ds_get_test_line(&in, stdin)) {
+        struct ofpbuf odp_actions;
+        struct ds out;
+        int error;
+
+        /* Convert string to OVS DP actions. */
+        ofpbuf_init(&odp_actions, 0);
+        error = odp_actions_from_string(ds_cstr(&in), NULL, &odp_actions);
+        if (error) {
+            printf("odp_actions_from_string: error\n");
+            goto next;
+        }
+
+        /* Convert odp_actions back to string. */
+        ds_init(&out);
+        format_odp_actions(&out, odp_actions.data, odp_actions.size);
+        puts(ds_cstr(&out));
+        ds_destroy(&out);
+
+    next:
+        ofpbuf_uninit(&odp_actions);
+    }
+    ds_destroy(&in);
+
     return 0;
 }
+
+int
+main(int argc, char *argv[])
+{
+    if (argc == 2 &&!strcmp(argv[1], "parse-keys")) {
+        return parse_keys();
+    } else if (argc == 2 && !strcmp(argv[1], "parse-actions")) {
+        return parse_actions();
+    } else {
+        ovs_fatal(0, "usage: %s parse-keys | parse-actions", argv[0]);
+    }
+}
index 4e2e416..a1ad2cb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 77b3a2c..1350ccd 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
index dda4797..e60e463 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index 9fcc632..7b1c2ac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * 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.
index 494046c..14569db 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 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.
index 5556cee..44b4723 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010 Nicira Networks.
+# 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.
index c7aec4b..2be7942 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -108,8 +108,8 @@ test_one(const struct test_vector *vec)
         struct sha1_ctx sha1;
 
         sha1_init(&sha1);
-        sha1_update(&sha1, (const void *) vec->data, n0);
-        sha1_update(&sha1, (const void *) (vec->data + n0), n1);
+        sha1_update(&sha1, vec->data, n0);
+        sha1_update(&sha1, vec->data + n0, n1);
         sha1_final(&sha1, md);
         assert(!memcmp(md, vec->output, SHA1_DIGEST_SIZE));
     }
index 259dc74..0acc7e0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -656,7 +656,7 @@ main(int argc, char *argv[])
 
     for (i = 0; i < tc->n_lans; i++) {
         struct lan *lan = tc->lans[i];
-        free((char *) lan->name);
+        free(CONST_CAST(char *, lan->name));
         free(lan);
     }
     for (i = 0; i < tc->n_bridges; i++) {
index b302630..105a72a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 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.
index 442b27a..9896cf7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 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.
@@ -97,6 +97,12 @@ main(int argc, char *argv[])
     if (argc != 2) {
         usage();
     } else if (!strcmp(argv[1], "plain")) {
+        /* If we're not caching time there isn't much to test and SIGALRM won't
+         * be around to pull us out of the select() call, so just skip out */
+        if (!CACHE_TIME) {
+            exit (77);
+        }
+
         do_test();
     } else if (!strcmp(argv[1], "daemon")) {
         /* Test that time still advances even in a daemon.  This is an
@@ -104,6 +110,10 @@ main(int argc, char *argv[])
         char cwd[1024], *pidfile;
         FILE *success;
 
+        if (!CACHE_TIME) {
+            exit (77);
+        }
+
         assert(getcwd(cwd, sizeof cwd) == cwd);
 
         unlink("test-timeval.success");
index 3c5ff6a..3f24725 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * 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.
index ec90048..c5c6a2b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ main(int argc, char *argv[])
     alarm(5);
 
     /* Create a listening socket under name 'sockname1'. */
-    sock1 = make_unix_socket(SOCK_STREAM, false, false, sockname1, NULL);
+    sock1 = make_unix_socket(SOCK_STREAM, false, sockname1, NULL);
     if (sock1 < 0) {
         ovs_fatal(-sock1, "%s: bind failed", sockname1);
     }
@@ -52,7 +52,7 @@ main(int argc, char *argv[])
 
     /* Connect to 'sockname2' (which should be the same file, perhaps under a
      * different name). */
-    sock2 = make_unix_socket(SOCK_STREAM, false, false, NULL, sockname2);
+    sock2 = make_unix_socket(SOCK_STREAM, false, NULL, sockname2);
     if (sock2 < 0) {
         ovs_fatal(-sock2, "%s: connect failed", sockname2);
     }
index c262a95..ab03479 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 Nicira Networks.
+# Copyright (c) 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ import sys
 
 import ovs.daemon
 import ovs.unixctl
+import ovs.unixctl.server
 
 vlog = ovs.vlog.Vlog("test-unixctl")
 exiting = False
@@ -39,6 +40,11 @@ def unixctl_echo_error(conn, argv, aux):
     conn.reply_error(str(argv))
 
 
+def unixctl_log(conn, argv, unused_aux):
+    vlog.info(str(argv[0]))
+    conn.reply(None)
+
+
 def unixctl_block(conn, unused_argv, unused_aux):
     pass
 
@@ -55,7 +61,7 @@ def main():
     ovs.vlog.handle_args(args)
 
     ovs.daemon.daemonize_start()
-    error, server = ovs.unixctl.UnixctlServer.create(args.unixctl)
+    error, server = ovs.unixctl.server.UnixctlServer.create(args.unixctl)
     if error:
         ovs.util.ovs_fatal(error, "could not create unixctl server at %s"
                            % args.unixctl, vlog)
@@ -63,6 +69,7 @@ def main():
     ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, "aux_exit")
     ovs.unixctl.command_register("echo", "[arg ...]", 1, 2, unixctl_echo,
                                  "aux_echo")
+    ovs.unixctl.command_register("log", "[arg ...]", 1, 2, unixctl_log, None)
     ovs.unixctl.command_register("echo_error", "[arg ...]", 1, 2,
                                  unixctl_echo_error, "aux_echo_error")
     ovs.unixctl.command_register("block", "", 0, 0, unixctl_block, None)
index 23b86e8..71a7f21 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 
 #include "byte-order.h"
+#include "command-line.h"
 #include "random.h"
 #include "util.h"
 
@@ -35,6 +36,25 @@ check_log_2_floor(uint32_t x, int n)
     }
 }
 
+static void
+test_log_2_floor(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    int n;
+
+    for (n = 0; n < 32; n++) {
+        /* Check minimum x such that f(x) == n. */
+        check_log_2_floor(1 << n, n);
+
+        /* Check maximum x such that f(x) == n. */
+        check_log_2_floor((1 << n) | ((1 << n) - 1), n);
+
+        /* Check a random value in the middle. */
+        check_log_2_floor((random_uint32() & ((1 << n) - 1)) | (1 << n), n);
+    }
+
+    /* log_2_floor(0) is undefined, so don't check it. */
+}
+
 static void
 check_ctz(uint32_t x, int n)
 {
@@ -45,6 +65,26 @@ check_ctz(uint32_t x, int n)
     }
 }
 
+static void
+test_ctz(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    int n;
+
+    for (n = 0; n < 32; n++) {
+        /* Check minimum x such that f(x) == n. */
+        check_ctz(1 << n, n);
+
+        /* Check maximum x such that f(x) == n. */
+        check_ctz(UINT32_MAX << n, n);
+
+        /* Check a random value in the middle. */
+        check_ctz((random_uint32() | 1) << n, n);
+    }
+
+    /* Check ctz(0). */
+    check_ctz(0, 32);
+}
+
 /* Returns the sum of the squares of the first 'n' positive integers. */
 static unsigned int
 sum_of_squares(int n)
@@ -53,7 +93,7 @@ sum_of_squares(int n)
 }
 
 static void
-check_bitwise_copy(void)
+test_bitwise_copy(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     unsigned int n_loops;
     int src_ofs;
@@ -103,7 +143,7 @@ check_bitwise_copy(void)
 }
 
 static void
-check_bitwise_zero(void)
+test_bitwise_zero(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     unsigned int n_loops;
     int dst_ofs;
@@ -144,7 +184,7 @@ check_bitwise_zero(void)
 }
 
 static void
-check_bitwise_one(void)
+test_bitwise_one(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     unsigned int n_loops;
     int dst_ofs;
@@ -185,11 +225,10 @@ check_bitwise_one(void)
 }
 
 static void
-check_bitwise_is_all_zeros(void)
+test_bitwise_is_all_zeros(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     int n_loops;
 
-    n_loops = 0;
     for (n_loops = 0; n_loops < 100; n_loops++) {
         ovs_be64 x = htonll(0);
         int i;
@@ -229,36 +268,33 @@ check_bitwise_is_all_zeros(void)
     }
 }
 
-int
-main(void)
+static void
+test_follow_symlinks(int argc, char *argv[])
 {
-    int n;
-
-    for (n = 0; n < 32; n++) {
-        /* Check minimum x such that f(x) == n. */
-        check_log_2_floor(1 << n, n);
-        check_ctz(1 << n, n);
-
-        /* Check maximum x such that f(x) == n. */
-        check_log_2_floor((1 << n) | ((1 << n) - 1), n);
-        check_ctz(UINT32_MAX << n, n);
+    int i;
 
-        /* Check a random value in the middle. */
-        check_log_2_floor((random_uint32() & ((1 << n) - 1)) | (1 << n), n);
-        check_ctz((random_uint32() | 1) << n, n);
+    for (i = 1; i < argc; i++) {
+        char *target = follow_symlinks(argv[i]);
+        puts(target);
+        free(target);
     }
+}
+\f
+static const struct command commands[] = {
+    {"ctz", 0, 0, test_ctz},
+    {"log_2_floor", 0, 0, test_log_2_floor},
+    {"bitwise_copy", 0, 0, test_bitwise_copy},
+    {"bitwise_zero", 0, 0, test_bitwise_zero},
+    {"bitwise_one", 0, 0, test_bitwise_one},
+    {"bitwise_is_all_zeros", 0, 0, test_bitwise_is_all_zeros},
+    {"follow-symlinks", 1, INT_MAX, test_follow_symlinks},
+    {NULL, 0, 0, NULL},
+};
 
-    /* Check ctz(0).
-     * (log_2_floor(0) is undefined.) */
-    check_ctz(0, 32);
-
-    check_bitwise_copy();
-
-    check_bitwise_zero();
-
-    check_bitwise_one();
-
-    check_bitwise_is_all_zeros();
-
+int
+main(int argc, char *argv[])
+{
+    set_program_name(argv[0]);
+    run_command(argc - 1, argv + 1, commands);
     return 0;
 }
index 750b74e..a13d779 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 8aa5a8d..5dd38f0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,9 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include "command-line.h"
+#include "ofp-msgs.h"
+#include "ofp-util.h"
+#include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "poll-loop.h"
 #include "socket-util.h"
@@ -56,8 +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)));
         ovs_fatal(0, "%s:%d: %s is %d (%s) but should be %d (%s)",
-                  file, line, as, a, strerror(abs(a)), b, strerror(abs(b)));
+                  file, line, as, a, strerror(abs(a)), b, str_b);
     }
 }
 
@@ -139,20 +143,26 @@ static void
 test_refuse_connection(int argc OVS_UNUSED, char *argv[])
 {
     const char *type = argv[1];
-    int expected_error;
     struct fake_pvconn fpv;
     struct vconn *vconn;
-
-    expected_error = (!strcmp(type, "unix") ? EPIPE
-                      : !strcmp(type, "tcp") ? ECONNRESET
-                      : EPROTO);
+    int error;
 
     fpv_create(type, &fpv);
     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
                            DSCP_DEFAULT), 0);
     fpv_close(&fpv);
     vconn_run(vconn);
-    CHECK_ERRNO(vconn_connect(vconn), expected_error);
+
+    error = vconn_connect_block(vconn);
+    if (!strcmp(type, "tcp")) {
+        if (error != ECONNRESET && error != EPIPE) {
+            ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
+                      error, strerror(error));
+        }
+    } else {
+        CHECK_ERRNO(error, !strcmp(type, "unix") ? EPIPE : EPROTO);
+    }
+
     vconn_close(vconn);
     fpv_destroy(&fpv);
 }
@@ -164,13 +174,9 @@ static void
 test_accept_then_close(int argc OVS_UNUSED, char *argv[])
 {
     const char *type = argv[1];
-    int expected_error;
     struct fake_pvconn fpv;
     struct vconn *vconn;
-
-    expected_error = (!strcmp(type, "unix") ? EPIPE
-                      : !strcmp(type, "tcp") ? ECONNRESET
-                      : EPROTO);
+    int error;
 
     fpv_create(type, &fpv);
     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
@@ -178,7 +184,17 @@ test_accept_then_close(int argc OVS_UNUSED, char *argv[])
     vconn_run(vconn);
     stream_close(fpv_accept(&fpv));
     fpv_close(&fpv);
-    CHECK_ERRNO(vconn_connect(vconn), expected_error);
+
+    error = vconn_connect_block(vconn);
+    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));
+        }
+    } else {
+        CHECK_ERRNO(error, EPROTO);
+    }
+
     vconn_close(vconn);
     fpv_destroy(&fpv);
 }
@@ -193,6 +209,7 @@ test_read_hello(int argc OVS_UNUSED, char *argv[])
     struct fake_pvconn fpv;
     struct vconn *vconn;
     struct stream *stream;
+    int error;
 
     fpv_create(type, &fpv);
     CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
@@ -206,8 +223,11 @@ test_read_hello(int argc OVS_UNUSED, char *argv[])
 
        retval = stream_recv(stream, &hello, sizeof hello);
        if (retval == sizeof hello) {
+           enum ofpraw raw;
+
            CHECK(hello.version, OFP10_VERSION);
-           CHECK(hello.type, OFPT_HELLO);
+           CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0);
+           CHECK(raw, OFPRAW_OFPT_HELLO);
            CHECK(ntohs(hello.length), sizeof hello);
            break;
        } else {
@@ -222,7 +242,11 @@ test_read_hello(int argc OVS_UNUSED, char *argv[])
        poll_block();
     }
     stream_close(stream);
-    CHECK_ERRNO(vconn_connect(vconn), ECONNRESET);
+    error = vconn_connect_block(vconn);
+    if (error != ECONNRESET && error != EPIPE) {
+        ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
+                  error, strerror(error));
+    }
     vconn_close(vconn);
 }
 
@@ -273,8 +297,11 @@ test_send_hello(const char *type, const void *out, size_t out_size,
            struct ofp_header hello;
            int retval = stream_recv(stream, &hello, sizeof hello);
            if (retval == sizeof hello) {
+               enum ofpraw raw;
+
                CHECK(hello.version, OFP10_VERSION);
-               CHECK(hello.type, OFPT_HELLO);
+               CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0);
+               CHECK(raw, OFPRAW_OFPT_HELLO);
                CHECK(ntohs(hello.length), sizeof hello);
                read_hello = true;
            } else {
@@ -312,7 +339,7 @@ test_send_hello(const char *type, const void *out, size_t out_size,
        poll_block();
     }
     stream_close(stream);
-    CHECK_ERRNO(vconn_recv(vconn, &msg), EOF);
+    CHECK_ERRNO(vconn_recv_block(vconn, &msg), EOF);
     vconn_close(vconn);
 }
 
@@ -321,13 +348,12 @@ static void
 test_send_plain_hello(int argc OVS_UNUSED, char *argv[])
 {
     const char *type = argv[1];
-    struct ofp_header hello;
+    struct ofpbuf *hello;
 
-    hello.version = OFP10_VERSION;
-    hello.type = OFPT_HELLO;
-    hello.length = htons(sizeof hello);
-    hello.xid = htonl(0x12345678);
-    test_send_hello(type, &hello, sizeof hello, 0);
+    hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP10_VERSION,
+                             htonl(0x12345678), 0);
+    test_send_hello(type, hello->data, hello->size, 0);
+    ofpbuf_delete(hello);
 }
 
 /* Try connecting and sending an extra-long hello, which should succeed (since
@@ -337,16 +363,15 @@ static void
 test_send_long_hello(int argc OVS_UNUSED, char *argv[])
 {
     const char *type = argv[1];
-    struct ofp_header hello;
-    char buffer[sizeof hello * 2];
-
-    hello.version = OFP10_VERSION;
-    hello.type = OFPT_HELLO;
-    hello.length = htons(sizeof buffer);
-    hello.xid = htonl(0x12345678);
-    memset(buffer, 0, sizeof buffer);
-    memcpy(buffer, &hello, sizeof hello);
-    test_send_hello(type, buffer, sizeof buffer, 0);
+    struct ofpbuf *hello;
+    enum { EXTRA_BYTES = 8 };
+
+    hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP10_VERSION,
+                             htonl(0x12345678), EXTRA_BYTES);
+    ofpbuf_put_zeros(hello, EXTRA_BYTES);
+    ofpmsg_update_length(hello);
+    test_send_hello(type, hello->data, hello->size, 0);
+    ofpbuf_delete(hello);
 }
 
 /* Try connecting and sending an echo request instead of a hello, which should
@@ -355,13 +380,12 @@ static void
 test_send_echo_hello(int argc OVS_UNUSED, char *argv[])
 {
     const char *type = argv[1];
-    struct ofp_header echo;
+    struct ofpbuf *echo;
 
-    echo.version = OFP10_VERSION;
-    echo.type = OFPT_ECHO_REQUEST;
-    echo.length = htons(sizeof echo);
-    echo.xid = htonl(0x89abcdef);
-    test_send_hello(type, &echo, sizeof echo, EPROTO);
+    echo = ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+                             htonl(0x12345678), 0);
+    test_send_hello(type, echo->data, echo->size, EPROTO);
+    ofpbuf_delete(echo);
 }
 
 /* Try connecting and sending a hello packet that has its length field as 0,
@@ -382,13 +406,13 @@ static void
 test_send_invalid_version_hello(int argc OVS_UNUSED, char *argv[])
 {
     const char *type = argv[1];
-    struct ofp_header hello;
+    struct ofpbuf *hello;
 
-    hello.version = OFP10_VERSION - 1;
-    hello.type = OFPT_HELLO;
-    hello.length = htons(sizeof hello);
-    hello.xid = htonl(0x12345678);
-    test_send_hello(type, &hello, sizeof hello, EPROTO);
+    hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP10_VERSION,
+                             htonl(0x12345678), 0);
+    ((struct ofp_header *) hello->data)->version = 0;
+    test_send_hello(type, hello->data, hello->size, EPROTO);
+    ofpbuf_delete(hello);
 }
 
 static const struct command commands[] = {
index 1c6a2df..f6d0cec 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 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.
index 60401d6..2b4ccdb 100644 (file)
@@ -1,6 +1,6 @@
 AT_INIT
 
-AT_COPYRIGHT([Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+AT_COPYRIGHT([Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -33,6 +33,14 @@ ovs_wait () {
     done
     exit 1
 }
+
+# Prints the integers from $1 to $2, increasing by $3 (default 1) on stdout.
+seq () {
+    while test $1 -le $2; do
+        echo $1
+        set `expr $1 + ${3-1}` $2 $3
+    done
+}
 ]
 m4_divert_pop([PREPARE_TESTS])
 
@@ -45,6 +53,17 @@ m4_define([OVS_WAIT_UNTIL], [OVS_WAIT([$1], [$2])])
 m4_define([OVS_WAIT_WHILE],
   [OVS_WAIT([if $1; then return 1; else return 0; fi], [$2])])
 
+dnl ON_EXIT([COMMANDS])
+dnl
+dnl Adds the shell COMMANDS to a collection executed when the current test
+dnl completes, as a cleanup action.  (The most common use is to kill a
+dnl daemon started by the test.  This is important to prevent tests that
+dnl start daemons from hanging at exit.)
+m4_define([ON_EXIT], [trap '. ./cleanup' 0; cat >>cleanup <<'EOF'
+$1
+EOF
+])
+
 m4_include([tests/ovsdb-macros.at])
 m4_include([tests/ofproto-macros.at])
 
@@ -56,6 +75,7 @@ m4_include([tests/classifier.at])
 m4_include([tests/check-structs.at])
 m4_include([tests/daemon.at])
 m4_include([tests/daemon-py.at])
+m4_include([tests/ofp-actions.at])
 m4_include([tests/ofp-print.at])
 m4_include([tests/ofp-errors.at])
 m4_include([tests/ovs-ofctl.at])
index f9caa60..b54d409 100644 (file)
@@ -73,6 +73,7 @@ AT_CLEANUP
 
 AT_SETUP([unixctl bad target - Python])
 OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
 AT_SKIP_IF([test $HAVE_PYTHON = no])
 
 AT_CHECK([PYAPPCTL -t bogus doit], [1], [], [stderr])
@@ -91,8 +92,9 @@ AT_SETUP([unixctl server - Python])
 AT_SKIP_IF([test $HAVE_PYTHON = no])
 OVS_RUNDIR=`pwd`; export OVS_RUNDIR
 OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
 OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
-trap 'kill `cat test-unixctl.py.pid`' 0
+ON_EXIT([kill `cat test-unixctl.py.pid`])
 AT_CAPTURE_FILE([`pwd`/test-unixctl.py.log])
 AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file --pidfile --detach])
 
@@ -104,7 +106,11 @@ The available commands are:
   echo_error              [[arg ...]]
   exit
   help
+  log                     [[arg ...]]
   version
+  vlog/list
+  vlog/reopen
+  vlog/set                spec
 ])
 mv stdout expout
 AT_CHECK([PYAPPCTL -t test-unixctl.py help], [0], [expout])
@@ -153,7 +159,6 @@ sed 's/ovs-appctl/appctl.py/' stderr > experr
 AT_CHECK([PYAPPCTL -t test-unixctl.py bogus], [2], [], [experr])
 
 AT_CHECK([APPCTL -t test-unixctl.py exit])
-trap '' 0]
 AT_CLEANUP
 
 
index 24f7180..2a82fb1 100644 (file)
@@ -12,27 +12,7 @@ m4_define([CHECK_UUID],
      exit 1
    fi])
 
-# This test is a strict subset of the larger test down below, but it
-# completes in a realistic amount of time with the "lcov" wrapper.
-AT_SETUP([UUID generation])
-AT_KEYWORDS([UUID])
-AT_CHECK([test-uuid > uuid])
-AT_CHECK([
-  uuid=`cat uuid`
-  CHECK_UUID])
-AT_CLEANUP
-
-# This test is a strict subset of the larger test down below, but it
-# completes in a realistic amount of time with the "lcov" wrapper.
-AT_SETUP([UUID parsing and serialization])
-AT_KEYWORDS([UUID])
-AT_CHECK([test-uuid f47ac10b-58cc-4372-a567-0e02b2c3d479], [0],
-         [f47ac10b-58cc-4372-a567-0e02b2c3d479
-])
-AT_CLEANUP
-
 AT_SETUP([UUID generation, parsing, serialization])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov makes this test absurdly slow
 AT_KEYWORDS([UUID])
 AT_CHECK([
   uuids=
index bb3b3c1..957d872 100644 (file)
@@ -2,16 +2,15 @@ AT_BANNER([vlog])
 
 AT_SETUP([vlog - Python])
 AT_SKIP_IF([test $HAVE_PYTHON = no])
-AT_CHECK([$PYTHON $srcdir/test-vlog.py --log-file log_file \
--v ANY:ANY:dbg module_1:ANY:info module_2:ANY:warn ANY:syslog:off \
-2>stderr_log])
 AT_CAPTURE_FILE([log_file])
 AT_CAPTURE_FILE([stderr_log])
+AT_CHECK([$PYTHON $srcdir/test-vlog.py --log-file log_file \
+-v dbg module_1:info module_2:warn syslog:off 2>stderr_log])
 
 AT_CHECK([diff log_file stderr_log])
 
-AT_CHECK([sed -e 's/.* .* ..:..:..|//' \
--e 's/File "[[^"]]*", line [[0-9]][[0-9]]*,/File <name>, line <number>,/' \
+AT_CHECK([sed -e 's/.*-.*-.*T..:..:..Z|//' \
+-e 's/File ".*", line [[0-9]][[0-9]]*,/File <name>, line <number>,/' \
 stderr_log], [0], [dnl
 0|module_0|EMER|emergency
 1|module_0|ERR|error
@@ -103,3 +102,127 @@ AssertionError
 ])
 
 AT_CLEANUP
+
+AT_SETUP([vlog - vlog/reopen - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+ON_EXIT([kill `cat test-unixctl.py.pid`])
+
+AT_CAPTURE_FILE([log])
+AT_CAPTURE_FILE([log.old])
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach])
+
+AT_CHECK([APPCTL -t test-unixctl.py log message])
+mv log log.old
+AT_CHECK([APPCTL -t test-unixctl.py log message2])
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl.py log message3])
+AT_CHECK([APPCTL -t test-unixctl.py exit])
+
+AT_CHECK([sed 's/.*|//' log.old], [0], [dnl
+Entering run loop.
+message
+message2
+])
+AT_CHECK([sed 's/.*|//' log], [0], [dnl
+message3
+])
+AT_CLEANUP
+
+AT_SETUP([vlog - vlog/reopen without log file - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+ON_EXIT([kill `cat test-unixctl.py.pid`])
+
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --pidfile --detach])
+
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen], [0],
+  [Logging to file not configured
+])
+AT_CLEANUP
+
+dnl This checks that if vlog/reopen can't reopen the log file,
+dnl nothing particularly bad (e.g. Python throws an exception and
+dnl aborts the program) happens.
+AT_SETUP([vlog - vlog/reopen can't reopen log file - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+
+# Verify that /dev/full is a character device that fails writes.
+AT_SKIP_IF([test ! -c /dev/full])
+AT_SKIP_IF([echo > /dev/full])
+
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+ON_EXIT([kill `cat test-unixctl.py.pid`])
+
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach])
+AT_CHECK([APPCTL -t test-unixctl.py log message])
+mv log log.old
+ln -s /dev/full log
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl.py log message2])
+rm log
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl.py log message3])
+AT_CHECK([APPCTL -t test-unixctl.py exit])
+AT_CHECK([sed 's/.*|//' log.old], [0], [dnl
+Entering run loop.
+message
+])
+AT_CHECK([sed 's/.*|//' log], [0], [dnl
+message3
+])
+AT_CLEANUP
+
+AT_SETUP([vlog - vlog/set and vlog/list - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+ON_EXIT([kill `cat test-unixctl.py.pid`])
+
+AT_CAPTURE_FILE([log])
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach])
+
+AT_CHECK([APPCTL -t test-unixctl.py vlog/list], [0], [dnl
+                 console    syslog    file
+                 -------    ------    ------
+daemon            info       info       info
+fatal-signal      info       info       info
+jsonrpc           info       info       info
+poller            info       info       info
+reconnect         info       info       info
+socket_util       info       info       info
+stream            info       info       info
+test-unixctl      info       info       info
+unixctl_server    info       info       info
+])
+
+AT_CHECK([APPCTL -t test-unixctl.py vlog/set daemon:syslog:err])
+AT_CHECK([APPCTL -t test-unixctl.py vlog/set file:dbg])
+AT_CHECK([APPCTL -t test-unixctl.py vlog/set nonexistent], [0],
+  [no facility, level, or module "nonexistent"
+])
+AT_CHECK([APPCTL -t test-unixctl.py vlog/list], [0], [dnl
+                 console    syslog    file
+                 -------    ------    ------
+daemon            info        err        dbg
+fatal-signal      info       info        dbg
+jsonrpc           info       info        dbg
+poller            info       info        dbg
+reconnect         info       info        dbg
+socket_util       info       info        dbg
+stream            info       info        dbg
+test-unixctl      info       info        dbg
+unixctl_server    info       info        dbg
+])
+AT_CLEANUP
index 3b4144a..73bb344 100644 (file)
@@ -46,7 +46,7 @@
 --- tcpdump/print-openflow.c   1969-12-31 16:00:00.000000000 -0800
 +++ tcpdump/print-openflow.c   2009-05-11 15:38:41.000000000 -0700
 @@ -0,0 +1,46 @@
-+/* Copyright (C) 2007, 2008, 2009 Nicira Networks.
++/* Copyright (C) 2007, 2008, 2009 Nicira, Inc.
 +
 +   Redistribution and use in source and binary forms, with or without
 +   modification, are permitted provided that the following conditions
index d9d4419..4f96a4f 100644 (file)
@@ -7,11 +7,14 @@
 /ovs-benchmark.1
 /ovs-cfg-mod
 /ovs-cfg-mod.8
+/ovs-check-dead-ifs
 /ovs-controller
 /ovs-controller.8
 /ovs-ctl
 /ovs-dpctl
 /ovs-dpctl.8
+/ovs-l3ping
+/ovs-l3ping.8
 /ovs-lib
 /ovs-ofctl
 /ovs-ofctl.8
index 0e93ffb..fdd26b8 100644 (file)
@@ -4,24 +4,28 @@ bin_PROGRAMS += \
        utilities/ovs-dpctl \
        utilities/ovs-ofctl \
        utilities/ovs-vsctl
-bin_SCRIPTS += utilities/ovs-pki utilities/ovs-vsctl utilities/ovs-parse-leaks
+bin_SCRIPTS += utilities/ovs-pki utilities/ovs-parse-leaks
 if HAVE_PYTHON
 bin_SCRIPTS += \
+       utilities/ovs-l3ping \
        utilities/ovs-pcap \
        utilities/ovs-tcpundump \
        utilities/ovs-test \
        utilities/ovs-vlan-test
 endif
-noinst_SCRIPTS += utilities/ovs-pki-cgi
-scripts_SCRIPTS += utilities/ovs-ctl utilities/ovs-save
+scripts_SCRIPTS += \
+       utilities/ovs-check-dead-ifs \
+       utilities/ovs-ctl \
+       utilities/ovs-save
 scripts_DATA += utilities/ovs-lib
 
 EXTRA_DIST += \
+       utilities/ovs-check-dead-ifs.in \
        utilities/ovs-ctl.in \
+       utilities/ovs-l3ping.in \
        utilities/ovs-lib.in \
        utilities/ovs-parse-leaks.in \
        utilities/ovs-pcap.in \
-       utilities/ovs-pki-cgi.in \
        utilities/ovs-pki.in \
        utilities/ovs-save \
        utilities/ovs-tcpundump.in \
@@ -33,6 +37,7 @@ MAN_ROOTS += \
        utilities/ovs-controller.8.in \
        utilities/ovs-ctl.8 \
        utilities/ovs-dpctl.8.in \
+       utilities/ovs-l3ping.8.in \
        utilities/ovs-ofctl.8.in \
        utilities/ovs-parse-leaks.8 \
        utilities/ovs-pcap.1.in \
@@ -47,15 +52,17 @@ DISTCLEANFILES += \
        utilities/ovs-appctl.8 \
        utilities/ovs-ctl \
        utilities/ovs-benchmark.1 \
+       utilities/ovs-check-dead-ifs \
        utilities/ovs-controller.8 \
        utilities/ovs-dpctl.8 \
+       utilities/ovs-l3ping \
+       utilities/ovs-l3ping.8 \
        utilities/ovs-lib \
        utilities/ovs-ofctl.8 \
        utilities/ovs-parse-leaks \
        utilities/ovs-pcap \
        utilities/ovs-pcap.1 \
        utilities/ovs-pki \
-       utilities/ovs-pki-cgi \
        utilities/ovs-pki.8 \
        utilities/ovs-tcpundump \
        utilities/ovs-tcpundump.1 \
@@ -71,6 +78,7 @@ man_MANS += \
        utilities/ovs-benchmark.1 \
        utilities/ovs-controller.8 \
        utilities/ovs-dpctl.8 \
+       utilities/ovs-l3ping.8 \
        utilities/ovs-ofctl.8 \
        utilities/ovs-parse-leaks.8 \
        utilities/ovs-pcap.1 \
index e3ba0ad..cd422aa 100644 (file)
@@ -13,10 +13,14 @@ bugtool_plugins = \
 
 bugtool_scripts = \
        utilities/bugtool/ovs-bugtool-cfm-show \
+       utilities/bugtool/ovs-bugtool-coverage-show \
        utilities/bugtool/ovs-bugtool-lacp-show \
+       utilities/bugtool/ovs-bugtool-memory-show \
        utilities/bugtool/ovs-bugtool-tc-class-show \
+       utilities/bugtool/ovs-bugtool-vsctl-show \
        utilities/bugtool/ovs-bugtool-ovsdb-dump \
-       utilities/bugtool/ovs-bugtool-daemons-ver
+       utilities/bugtool/ovs-bugtool-daemons-ver \
+       utilities/bugtool/ovs-bugtool-bond-show
 scripts_SCRIPTS += $(bugtool_scripts)
 
 bugtoolpluginsdir = $(pkgdatadir)/bugtool-plugins
diff --git a/utilities/bugtool/ovs-bugtool-bond-show b/utilities/bugtool/ovs-bugtool-bond-show
new file mode 100755 (executable)
index 0000000..86ec97f
--- /dev/null
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2012 Nicira, Inc.
+
+ovs-appctl bond/show
index b137a2d..ccc058b 100755 (executable)
@@ -14,6 +14,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 # USA
 #
-# Copyright (C) 2011 Nicira Networks.
+# Copyright (C) 2011 Nicira, Inc.
 
 ovs-appctl cfm/show
diff --git a/utilities/bugtool/ovs-bugtool-coverage-show b/utilities/bugtool/ovs-bugtool-coverage-show
new file mode 100755 (executable)
index 0000000..b94fd96
--- /dev/null
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2012 Nicira, Inc.
+
+ovs-appctl coverage/show
index 672e7e1..952ed65 100755 (executable)
@@ -14,7 +14,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 # USA
 #
-# Copyright (C) 2012 Nicira Networks.
+# Copyright (C) 2012 Nicira, Inc.
 
 for f in `cd /var/run/openvswitch/; ls *.pid 2>/dev/null`
 do
index 2a2d72b..4c1728c 100755 (executable)
@@ -14,6 +14,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 # USA
 #
-# Copyright (C) 2011 Nicira Networks.
+# Copyright (C) 2011 Nicira, Inc.
 
 ovs-appctl lacp/show
diff --git a/utilities/bugtool/ovs-bugtool-memory-show b/utilities/bugtool/ovs-bugtool-memory-show
new file mode 100755 (executable)
index 0000000..3bad754
--- /dev/null
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2012 Nicira, Inc.
+
+ovs-appctl memory/show
index 9e4d662..4eef6f5 100755 (executable)
@@ -14,6 +14,6 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 # USA
 #
-# Copyright (C) 2011 Nicira Networks.
+# Copyright (C) 2011 Nicira, Inc.
 
 ovsdb-client -f csv dump unix:/var/run/openvswitch/db.sock Open_vSwitch
index df07f2a..5e6ae0a 100755 (executable)
@@ -14,7 +14,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 # USA
 #
-# Copyright (C) 2011 Nicira Networks.
+# Copyright (C) 2011 Nicira, Inc.
 
 for iface in $(cd /sys/class/net && echo *); do
     if [ -d /sys/class/net/$iface ]; then
diff --git a/utilities/bugtool/ovs-bugtool-vsctl-show b/utilities/bugtool/ovs-bugtool-vsctl-show
new file mode 100755 (executable)
index 0000000..fe433d8
--- /dev/null
@@ -0,0 +1,19 @@
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2012 Nicira, Inc.
+
+ovs-vsctl --timeout=5 show
index 6755d6a..18a8395 100644 (file)
@@ -23,6 +23,9 @@ system and places it in \fB/var/log/ovs-bugtool\fR.
 .IP "\fB\-\-all\fR"
 Use all available capabilities.
 .
+.IP "\fB\-\-ovs\fR"
+Use only Open vSwitch relevant capabilities.
+.
 .IP "\fB\-\-capabilities\fR"
 List \fBovs\-bugtool\fR capabilities.
 .
index 1e0fc7c..2074e23 100755 (executable)
@@ -14,7 +14,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
 # Copyright (c) 2005, 2007 XenSource Ltd.
-# Copyright (c) 2010, 2011 Nicira Networks.
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
 
 #
 # To add new entries to the bugtool, you need to:
@@ -115,7 +115,7 @@ OPENVSWITCH_LOG_DIR = '@LOGDIR@/'
 OPENVSWITCH_DEFAULT_SWITCH = '/etc/default/openvswitch-switch' # Debian
 OPENVSWITCH_SYSCONFIG_SWITCH = '/etc/sysconfig/openvswitch'    # RHEL
 OPENVSWITCH_DEFAULT_CONTROLLER = '/etc/default/openvswitch-controller'
-OPENVSWITCH_CONF_DB = '@sysconfdir@/openvswitch/conf.db'
+OPENVSWITCH_CONF_DB = '@DBDIR@/conf.db'
 OPENVSWITCH_VSWITCHD_PID = '@RUNDIR@/ovs-vswitchd.pid'
 COLLECTD_LOGS_DIR = '/var/lib/collectd/rrd'
 VAR_LOG_DIR = '/var/log/'
@@ -308,7 +308,7 @@ def output(x):
 def output_ts(x):
     output("[%s]  %s" % (time.strftime("%x %X %Z"), x))
 
-def cmd_output(cap, args, label = None, filter = None):
+def cmd_output(cap, args, label=None, filter=None):
     if cap in entries:
         if not label:
             if isinstance(args, list):
@@ -400,10 +400,14 @@ def collect_data():
     run_procs(process_lists.values())
 
 
-def main(argv = None):
+def main(argv=None):
     global ANSWER_YES_TO_ALL, SILENT_MODE
     global entries, data, dbg, unlimited_data
 
+    # Filter flags
+    only_ovs_info = False
+    collect_all_info = True
+
     # we need access to privileged files, exit if we are not running as root
     if os.getuid() != 0:
         print >>sys.stderr, "Error: ovs-bugtool must be run as root"
@@ -420,7 +424,7 @@ def main(argv = None):
         (options, params) = getopt.gnu_getopt(
             argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=',
                          'output=', 'outfd=', 'outfile=', 'all', 'unlimited',
-                         'debug'])
+                         'debug', 'ovs'])
     except getopt.GetoptError, opterr:
         print >>sys.stderr, opterr
         return 2
@@ -478,6 +482,10 @@ def main(argv = None):
             dbg = True
             ProcOutput.debug = True
 
+        if k == '--ovs':
+            only_ovs_info = True
+            collect_all_info = False
+
     if len(params) != 1:
         print >>sys.stderr, "Invalid additional arguments", str(params)
         return 2
@@ -547,7 +555,7 @@ exclude those logs from the archive.
     for d in disk_list():
         cmd_output(CAP_HDPARM_T, [HDPARM, '-tT', '/dev/%s' % d])
 
-    file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES, 
+    file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES,
                                   PROC_FILESYSTEMS, PROC_CMDLINE])
     cmd_output(CAP_KERNEL_INFO, [ZCAT, PROC_CONFIG], label='config')
     cmd_output(CAP_KERNEL_INFO, [SYSCTL, '-A'])
@@ -561,7 +569,7 @@ exclude those logs from the archive.
     cmd_output(CAP_MULTIPATH, [DMSETUP, 'table'])
     func_output(CAP_MULTIPATH, 'multipathd_topology', multipathd_topology)
     cmd_output(CAP_MULTIPATH, [MPPUTIL, '-a'])
-    if CAP_MULTIPATH in entries:
+    if CAP_MULTIPATH in entries and collect_all_info:
         dump_rdac_groups(CAP_MULTIPATH)
 
     tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, IFCFG_RE)
@@ -583,13 +591,14 @@ exclude those logs from the archive.
             f = open('/sys/class/net/%s/type' % p, 'r')
             t = f.readline()
             f.close()
-            if int(t) == 1:
+            if os.path.islink('/sys/class/net/%s/device' % p) and int(t) == 1:
                 # ARPHRD_ETHER
                 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, p])
                 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-S', p])
                 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-k', p])
                 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-i', p])
                 cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-c', p])
+            if int(t) == 1:
                 cmd_output(CAP_NETWORK_STATUS,
                            [TC, '-s', '-d', 'class', 'show', 'dev', p])
         except:
@@ -649,15 +658,44 @@ exclude those logs from the archive.
     tree_output(CAP_YUM, APT_SOURCES_LIST_D)
     cmd_output(CAP_YUM, [DPKG_QUERY, '-W', '-f=${Package} ${Version} ${Status}\n'], 'dpkg-packages')
 
+    # Filter out ovs relevant information if --ovs option passed
+    # else collect all information
+    filters = set()
+    if only_ovs_info:
+        filters.add('ovs')
+        ovs_info_caps = [CAP_NETWORK_STATUS, CAP_SYSTEM_LOGS,
+                         CAP_NETWORK_CONFIG]
+        ovs_info_list = ['process-tree']
+        # We cannot use iteritems, since we modify 'data' as we pass through
+        for (k, v) in data.items():
+            cap = v['cap']
+            if 'filename' in v:
+                info = k[0]
+            else:
+                info = k
+            if info not in ovs_info_list and cap not in ovs_info_caps:
+                del data[k]
+
+    if filters:
+        filter = ",".join(filters)
+    else:
+        filter = None
+
     try:
-        load_plugins()
+        load_plugins(filter=filter)
     except:
         pass
-    
+
     # permit the user to filter out data
-    for k in sorted(data.keys()):
-        if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % k):
-            del data[k]
+    # We cannot use iteritems, since we modify 'data' as we pass through
+    for (k, v) in sorted(data.items()):
+       cap = v['cap']
+       if 'filename' in v:
+           key = k[0]
+       else:
+           key = k
+       if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % key):
+           del data[k]
 
     # collect selected data now
     output_ts('Running commands to collect data')
@@ -772,7 +810,7 @@ def module_info(cap):
 
 
 def multipathd_topology(cap):
-    pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE, 
+    pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE,
                      stdout=PIPE, stderr=dev_null)
     stdout, stderr = pipe.communicate('show topology')
 
@@ -836,7 +874,7 @@ def dump_rdac_groups(cap):
                 group, _ = line.split(None, 1)
                 cmd_output(cap, [MPPUTIL, '-g', group])
 
-def load_plugins(just_capabilities = False):
+def load_plugins(just_capabilities=False, filter=None):
     def getText(nodelist):
         rc = ""
         for node in nodelist:
@@ -844,13 +882,13 @@ def load_plugins(just_capabilities = False):
                 rc += node.data
         return rc.encode()
 
-    def getBoolAttr(el, attr, default = False):
+    def getBoolAttr(el, attr, default=False):
         ret = default
         val = el.getAttribute(attr).lower()
         if val in ['true', 'false', 'yes', 'no']:
             ret = val in ['true', 'yes']
         return ret
-        
+
     for dir in [d for d in os.listdir(PLUGIN_DIR) if os.path.isdir(os.path.join(PLUGIN_DIR, d))]:
         if not caps.has_key(dir):
             if not os.path.exists("%s/%s.xml" % (PLUGIN_DIR, dir)):
@@ -880,13 +918,20 @@ def load_plugins(just_capabilities = False):
 
         if just_capabilities:
             continue
-                    
+
         plugdir = os.path.join(PLUGIN_DIR, dir)
         for file in [f for f in os.listdir(plugdir) if f.endswith('.xml')]:
             xmldoc = parse(os.path.join(plugdir, file))
             assert xmldoc.documentElement.tagName == "collect"
 
             for el in xmldoc.documentElement.getElementsByTagName("*"):
+                filters_tmp = el.getAttribute("filters")
+                if filters_tmp == '':
+                    filters = []
+                else:
+                    filters = filters_tmp.split(',')
+                if not(filter is None or filter in filters):
+                    continue
                 if el.tagName == "files":
                     newest_first = getBoolAttr(el, 'newest_first')
                     file_output(dir, getText(el.childNodes).split(),
@@ -896,7 +941,8 @@ def load_plugins(just_capabilities = False):
                     if pattern == '': pattern = None
                     negate = getBoolAttr(el, 'negate')
                     newest_first = getBoolAttr(el, 'newest_first')
-                    tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None,
+                    tree_output(dir, getText(el.childNodes),
+                                pattern and re.compile(pattern) or None,
                                 negate=negate, newest_first=newest_first)
                 elif el.tagName == "command":
                     label = el.getAttribute("label")
@@ -981,7 +1027,7 @@ def make_zip(subdir, output_file):
                 pass
     finally:
         zf.close()
-    
+
     output ('Writing archive %s successful.' % filename)
     if SILENT_MODE:
         print filename
@@ -1061,7 +1107,7 @@ def update_cap(cap, k, v):
     caps[cap] = tuple(l)
 
 
-def size_of_dir(d, pattern = None, negate = False):
+def size_of_dir(d, pattern=None, negate=False):
     if os.path.isdir(d):
         return size_of_all([os.path.join(d, fn) for fn in os.listdir(d)],
                            pattern, negate)
@@ -1069,7 +1115,7 @@ def size_of_dir(d, pattern = None, negate = False):
         return 0
 
 
-def size_of_all(files, pattern = None, negate = False):
+def size_of_all(files, pattern=None, negate=False):
     return sum([size_of(f, pattern, negate) for f in files])
 
 
@@ -1175,6 +1221,7 @@ class ProcOutput:
     def terminate(self):
         if self.running:
             try:
+                self.proc.stdout.close()
                 os.kill(self.proc.pid, SIGTERM)
             except:
                 pass
@@ -1187,6 +1234,7 @@ class ProcOutput:
         line = self.proc.stdout.readline()
         if line == '':
             # process exited
+            self.proc.stdout.close()
             self.status = self.proc.wait()
             self.proc = None
             self.running = False
@@ -1249,7 +1297,7 @@ def pidof(name):
 
 
 class StringIOmtime(StringIO.StringIO):
-    def __init__(self, buf = ''):
+    def __init__(self, buf=''):
         StringIO.StringIO.__init__(self, buf)
         self.mtime = time.time()
 
index 8432ba9..c571640 100644 (file)
@@ -13,7 +13,7 @@
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
  USA.
 
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011 Nicira, Inc.
 -->
 
 <collect>
index 42abfad..9539f7c 100644 (file)
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
  USA.
 
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011, 2012 Nicira, Inc.
 -->
 
 <collect>
-  <command label="tc-class-show">/usr/share/openvswitch/scripts/ovs-bugtool-tc-class-show</command>
-  <command label="dump-ovsdb">/usr/share/openvswitch/scripts/ovs-bugtool-ovsdb-dump</command>
-  <command label="ovs-lacp-show">/usr/share/openvswitch/scripts/ovs-bugtool-lacp-show</command>
-  <command label="ovs-cfm-show">/usr/share/openvswitch/scripts/ovs-bugtool-cfm-show</command>
+  <command label="tc-class-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-tc-class-show</command>
+  <command label="ovs-vsctl-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-vsctl-show</command>
+  <command label="dump-ovsdb" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-ovsdb-dump</command>
+  <command label="ovs-appctl-lacp-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-lacp-show</command>
+  <command label="ovs-appctl-cfm-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-cfm-show</command>
+  <command label="ovs-appctl-coverage-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-coverage-show</command>
+  <command label="ovs-appctl-bond-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-bond-show</command>
+  <command label="ovs-appctl-memory-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-memory-show</command>
 </collect>
index a948fd2..a59a698 100644 (file)
@@ -15,7 +15,7 @@
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
  USA.
 
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011 Nicira, Inc.
 -->
 <capability pii="no" min_size="-1" max_size="1000000" min_time="-1" max_time="60" mime="text/plain" checked="true" hidden="true">
 </capability>
index 260e6dc..d1d5a1a 100644 (file)
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
  USA.
 
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011 Nicira, Inc.
 -->
 
 <collect>
     <command label="timezone">date --rfc-3339=seconds</command>
-    <command label="ovs-daemons-ver">/usr/share/openvswitch/scripts/ovs-bugtool-daemons-ver</command>
+    <command label="ovs-daemons-ver" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-daemons-ver</command>
 </collect>
index 914bd53..354af34 100644 (file)
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
  USA.
 
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011, 2012 Nicira, Inc.
 -->
 
 <collect>
-  <directory label="ovsdb-backups" pattern=".*/conf.db.backup[0-9][^/]*$">/etc/openvswitch</directory>
+  <directory label="ovsdb-backups" filters="ovs" pattern=".*/conf.db.backup[0-9][^/]*$">/etc/openvswitch</directory>
+  <directory label="ovsdb-backups2" filters="ovs" pattern=".*/conf.db.backup[0-9][^/]*$">/var/lib/openvswitch</directory>
 </collect>
index e6cf023..99b060c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 38ed8c9..7edd182 100644 (file)
@@ -20,7 +20,8 @@ ovs\-appctl \- utility for configuring running Open vSwitch daemons
 .SH DESCRIPTION
 Open vSwitch daemons accept certain commands at runtime to control their
 behavior and query their settings.  Every daemon accepts a common set of
-commands documented under \fBCOMMON COMMANDS\fR below, and
+commands documented under \fBCOMMON COMMANDS\fR below.  Some daemons
+support additional commands documented in their own manpages.
 \fBovs\-vswitchd\fR in particular accepts a number of additional
 commands documented in \fBovs\-vswitchd\fR(8).
 .PP
@@ -186,6 +187,16 @@ The program's process ID (pid), as a decimal number.
 The number of milliseconds elapsed from the start of the application
 to the time the message was logged.
 .
+.IP \fB%t\fR
+The subprogram name, that is, an identifying name for the process or
+thread that emitted the log message, such as \fBmonitor\fR for the
+process used for \fB\-\-monitor\fR or \fBmain\fR for the primary
+process or thread in a program.
+.
+.IP \fB%T\fR
+The subprogram name enclosed in parentheses, e.g. \fB(monitor)\fR, or
+the empty string for the primary process or thread in a program.
+.
 .IP \fB%%\fR
 A literal \fB%\fR.
 .RE
@@ -213,6 +224,10 @@ width.  (A field wider than \fIwidth\fR is not truncated to fit.)
 The default pattern for console and file output is \fB%D{%Y-%m-%dT
 %H:%M:%SZ}|%05N|%c|%p|%m\fR; for syslog output, \fB%05N|%c|%p|%m\fR.
 .
+.IP
+Daemons written in Python (e.g. \fBovs\-xapi\-sync\fR,
+\fBovs\-monitor\-ipsec) do not allow control over the log pattern.
+.
 .IP "\fBvlog/reopen\fR"
 Causes the daemon to close and reopen its log file.  (This
 is useful after rotating log files, to cause a new log file to be
@@ -227,7 +242,7 @@ This has no effect if the target application was not invoked with the
 .
 .SH "SEE ALSO"
 .
-\fBovs\-appctl\fR can control the following daemons:
+\fBovs\-appctl\fR can control all Open vSwitch daemons, including:
 .BR ovs\-vswitchd (8),
-.BR ovs\-controller (8),
-.BR ovs\-brcompatd (8).
+and
+.BR ovsdb\-server (8).
index f761d5a..ddeeb1d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index e846902..162f7cf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/utilities/ovs-check-dead-ifs.in b/utilities/ovs-check-dead-ifs.in
new file mode 100755 (executable)
index 0000000..9b806ed
--- /dev/null
@@ -0,0 +1,101 @@
+#! @PYTHON@
+
+import os
+import re
+import stat
+import sys
+
+if "--help" in sys.argv:
+    sys.stdout.write("""\
+ovs-check-dead-ifs: Check for packet sockets for nonexistent network devices.
+
+One side effect of the "force-reload-kmod" command that reloads the
+Open vSwitch kernel module is that all the network devices that the
+Open vSwitch kernel module implemented get destroyed and then replaced
+by new instances with the same names.  Unfortunately, programs that
+are listening for packets on the original network devices will not
+receive packets that arrive on the new instances.  This causes some
+services, such as DHCP, to silently fail.  This program looks for such
+problems and, if it finds any, prints information about programs that
+are in such a state.  The system administrator should then take some
+action to fix the problem, such as restarting these programs.
+""")
+    sys.exit(0)
+elif len(sys.argv) > 1:
+    sys.stderr.write("ovs-check-dead-ifs: no arguments or options accepted "
+                     "(use --help for help)\n")
+    sys.exit(1)
+
+# Get the set of all valid ifindexes.
+#
+# 0 is always valid for our purposes because it means "any interface".
+valid_ifindexes = set([])
+for ifname in os.listdir("/sys/class/net"):
+    fn = "/sys/class/net/%s/ifindex" % ifname
+    try:
+        valid_ifindexes.add(int(open(fn).readline()))
+    except IOError:
+        pass
+    except ValueError:
+        print "%s: unexpected format\n" % fn
+
+# Get inodes for all packet sockets whose ifindexes don't exist.
+invalid_inodes = set()
+f = open("/proc/net/packet")
+f.readline()                    # Skip header line.
+for line in f:
+    fields = line.split()
+    ifindex = int(fields[4])
+    if ifindex not in valid_ifindexes:
+        invalid_inodes.add(int(fields[8]))
+f.close()
+
+if not invalid_inodes:
+    sys.exit(0)
+
+# Now find the processes that are using those packet sockets.
+inode_re = re.compile(r'socket:\[([0-9]+)\]$')
+bad_pids = set()
+for pid in os.listdir("/proc"):
+    try:
+        pid = int(pid)
+    except ValueError:
+        continue
+
+    try:
+        fds = os.listdir("/proc/%d/fd" % pid)
+    except OSError:
+        continue
+
+    for fd in fds:
+        try:
+            fd = int(fd)
+        except ValueError:
+            continue
+
+        try:
+            s = os.stat("/proc/%d/fd/%d" % (pid, fd))
+        except OSError:
+            continue
+
+        if not stat.S_ISSOCK(s.st_mode):
+            continue
+
+        try:
+            linkname = os.readlink("/proc/%d/fd/%d" % (pid, fd))
+        except OSError:
+            continue
+
+        m = inode_re.match(linkname)
+        if not m:
+            continue
+
+        inode = int(m.group(1))
+        if inode in invalid_inodes:
+            bad_pids.add(pid)
+
+if bad_pids:
+    print """
+The following processes are listening for packets to arrive on network devices
+that no longer exist. You may want to restart them."""
+    os.execvp("ps", ["ps"] + ["%s" % pid for pid in bad_pids])
index fa60f8f..07e300b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@
 #include "openflow/openflow.h"
 #include "poll-loop.h"
 #include "rconn.h"
-#include "shash.h"
+#include "simap.h"
 #include "stream-ssl.h"
 #include "timeval.h"
 #include "unixctl.h"
@@ -49,7 +49,6 @@ VLOG_DEFINE_THIS_MODULE(controller);
 
 struct switch_ {
     struct lswitch *lswitch;
-    struct rconn *rconn;
 };
 
 /* -H, --hub: Learn the ports on which MAC addresses appear? */
@@ -62,7 +61,7 @@ static bool set_up_flows = true;
 /* -N, --normal: Use "NORMAL" action instead of explicit port? */
 static bool action_normal = false;
 
-/* -w, --wildcard: 0 to disable wildcard flow entries, a OFPFW_* bitmask to
+/* -w, --wildcard: 0 to disable wildcard flow entries, an OFPFW10_* bitmask to
  * enable specific wildcards, or UINT32_MAX to use the default wildcards. */
 static uint32_t wildcards = 0;
 
@@ -76,8 +75,8 @@ static bool mute = false;
 /* -q, --queue: default OpenFlow queue, none if UINT32_MAX. */
 static uint32_t default_queue = UINT32_MAX;
 
-/* -Q, --port-queue: map from port name to port number (cast to void *). */
-static struct shash port_queues = SHASH_INITIALIZER(&port_queues);
+/* -Q, --port-queue: map from port name to port number. */
+static struct simap port_queues = SIMAP_INITIALIZER(&port_queues);
 
 /* --with-flows: Flows to send to switch. */
 static struct ofputil_flow_mod *default_flows;
@@ -86,7 +85,6 @@ static size_t n_default_flows;
 /* --unixctl: Name of unixctl socket, or null to use the default. */
 static char *unixctl_path = NULL;
 
-static int do_switching(struct switch_ *);
 static void new_switch(struct switch_ *, struct vconn *);
 static void parse_options(int argc, char *argv[]);
 static void usage(void) NO_RETURN;
@@ -151,8 +149,6 @@ main(int argc, char *argv[])
     daemonize_complete();
 
     while (n_switches > 0 || n_listeners > 0) {
-        int iteration;
-
         /* Accept connections on listening vconns. */
         for (i = 0; i < n_listeners && n_switches < MAX_SWITCHES; ) {
             struct vconn *new_vconn;
@@ -169,32 +165,16 @@ main(int argc, char *argv[])
             }
         }
 
-        /* Do some switching work.  Limit the number of iterations so that
-         * callbacks registered with the poll loop don't starve. */
-        for (iteration = 0; iteration < 50; iteration++) {
-            bool progress = false;
-            for (i = 0; i < n_switches; ) {
-                struct switch_ *this = &switches[i];
-
-                retval = do_switching(this);
-                if (!retval || retval == EAGAIN) {
-                    if (!retval) {
-                        progress = true;
-                    }
-                    i++;
-                } else {
-                    rconn_destroy(this->rconn);
-                    lswitch_destroy(this->lswitch);
-                    switches[i] = switches[--n_switches];
-                }
-            }
-            if (!progress) {
-                break;
-            }
-        }
-        for (i = 0; i < n_switches; i++) {
+        /* Do some switching work.  . */
+        for (i = 0; i < n_switches; ) {
             struct switch_ *this = &switches[i];
             lswitch_run(this->lswitch);
+            if (lswitch_is_alive(this->lswitch)) {
+                i++;
+            } else {
+                lswitch_destroy(this->lswitch);
+                switches[i] = switches[--n_switches];
+            }
         }
 
         unixctl_server_run(unixctl);
@@ -207,8 +187,6 @@ main(int argc, char *argv[])
         }
         for (i = 0; i < n_switches; i++) {
             struct switch_ *sw = &switches[i];
-            rconn_run_wait(sw->rconn);
-            rconn_recv_wait(sw->rconn);
             lswitch_wait(sw->lswitch);
         }
         unixctl_server_wait(unixctl);
@@ -222,9 +200,10 @@ static void
 new_switch(struct switch_ *sw, struct vconn *vconn)
 {
     struct lswitch_config cfg;
+    struct rconn *rconn;
 
-    sw->rconn = rconn_create(60, 0, DSCP_DEFAULT);
-    rconn_connect_unreliably(sw->rconn, vconn, NULL);
+    rconn = rconn_create(60, 0, DSCP_DEFAULT);
+    rconn_connect_unreliably(rconn, vconn, NULL);
 
     cfg.mode = (action_normal ? LSW_NORMAL
                 : learn_macs ? LSW_LEARN
@@ -235,29 +214,8 @@ new_switch(struct switch_ *sw, struct vconn *vconn)
     cfg.n_default_flows = n_default_flows;
     cfg.default_queue = default_queue;
     cfg.port_queues = &port_queues;
-    sw->lswitch = lswitch_create(sw->rconn, &cfg);
-}
-
-static int
-do_switching(struct switch_ *sw)
-{
-    unsigned int packets_sent;
-    struct ofpbuf *msg;
-
-    packets_sent = rconn_packets_sent(sw->rconn);
-
-    msg = rconn_recv(sw->rconn);
-    if (msg) {
-        if (!mute) {
-            lswitch_process_packet(sw->lswitch, sw->rconn, msg);
-        }
-        ofpbuf_delete(msg);
-    }
-    rconn_run(sw->rconn);
-
-    return (!rconn_is_alive(sw->rconn) ? EOF
-            : rconn_packets_sent(sw->rconn) != packets_sent ? 0
-            : EAGAIN);
+    cfg.mute = mute;
+    sw->lswitch = lswitch_create(rconn, &cfg);
 }
 
 static void
@@ -274,8 +232,7 @@ add_port_queue(char *s)
                   "\"<port-name>:<queue-id>\"");
     }
 
-    if (!shash_add_once(&port_queues, port_name,
-                        (void *) (uintptr_t) atoi(queue_id))) {
+    if (!simap_put(&port_queues, port_name, atoi(queue_id))) {
         ovs_fatal(0, "<port-name> arguments for -Q or --port-queue must "
                   "be unique");
     }
@@ -398,7 +355,7 @@ parse_options(int argc, char *argv[])
     }
     free(short_options);
 
-    if (!shash_is_empty(&port_queues) || default_queue != UINT32_MAX) {
+    if (!simap_is_empty(&port_queues) || default_queue != UINT32_MAX) {
         if (action_normal) {
             ovs_error(0, "queue IDs are incompatible with -N or --normal; "
                       "not using OFPP_NORMAL");
index 06ccfec..c730778 100644 (file)
@@ -179,8 +179,34 @@ suppresses that behavior.
 .
 .IP "\fB\-\-ovsdb\-server\-priority=\fIniceness\fR"
 .IQ "\fB\-\-ovs\-vswitchd\-priority=\fIniceness\fR"
-Sets the \fBnice\fR(1) level used for \fBovsdb\-server\fR and
-\fBovs\-vswitchd\fR, respectively.  Both default to \fB\-10\fR.
+.IQ "\fB\-\-ovs\-brcompatd\-priority=\fIniceness\fR"
+Sets the \fBnice\fR(1) level used for each daemon.  All of them
+default to \fB\-10\fR.
+.
+.IP "\fB\-\-ovsdb\-server\-wrapper=\fIwrapper\fR"
+.IQ "\fB\-\-ovs\-vswitchd\-wrapper=\fIwrapper\fR"
+.IQ "\fB\-\-ovs\-brcompatd\-wrapper=\fIwrapper\fR"
+.
+Configures the specified daemon to run under \fIwrapper\fR, which is
+one of the following:
+.
+.RS
+.IP "\fBvalgrind\fR"
+Run the daemon under \fBvalgrind\fR(1), if it is installed, logging to
+\fIdaemon\fB.valgrind.log.\fIpid\fR in the log directory.
+.
+.IP "\fBstrace\fR"
+Run the daemon under \fBstrace\fR(1), if it is installed, logging to
+\fIdaemon\fB.strace.log.\fIpid\fR in the log directory.
+.RE
+.
+.IP
+By default, no wrapper is used.
+.
+.IP
+Wrappers greatly slow daemon operations so they should not be used in
+production.  They also produce voluminous logs that can quickly fill
+small disk partitions.
 .
 .PP
 The following options control file locations.  They should only be
@@ -261,11 +287,15 @@ reloads the kernel module and restarts the OVS daemons (including
 .IP 6.
 Restores the kernel configuration state that was saved in step 3.
 .
-.PP
-The steps above are often enough to hot-upgrade a new kernel module
-with only a few seconds of downtime.  DHCP is a common problem: if the
-ISC DHCP client is running on an OVS internal interface, then it will
-have to be restarted after completing the above procedure.
+.IP 7.
+Checks for daemons that may need to be restarted because they have
+packet sockets that are listening on old instances of Open vSwitch
+kernel interfaces and, if it finds any, prints a warning on stdout.
+DHCP is a common example: if the ISC DHCP client is running on an OVS
+internal interface, then it will have to be restarted after completing
+the above procedure.  (It would be nice if \fBovs\-ctl\fR could restart
+daemons automatically, but the details are far too specific to a
+particular distribution and installation.)
 .
 .PP
 \fBforce\-kmod\-reload\fR internally stops and starts OVS, so it
@@ -365,6 +395,7 @@ that it runs.  \fBovs\-ctl\fR will add the \fIsbindir\fR and
 .
 .IP "\fBOVS_LOGDIR\fR"
 .IQ "\fBOVS_RUNDIR\fR"
+.IQ "\fBOVS_DBDIR\fR"
 .IQ "\fBOVS_SYSCONFDIR\fR"
 .IQ "\fBOVS_PKGDATADIR\fR"
 .IQ "\fBOVS_BINDIR\fR"
@@ -392,7 +423,7 @@ process ID.
 The OVS database schema used to initialize the database (use
 \fB\-\-db\-schema to override this location).
 .
-.IP "\fIsysconfdir\fB/openvswitch/conf.db\fR"
+.IP "\fIdbdir\fB/conf.db\fR"
 The OVS database (use \fB\-\-db\-file\fR to override this location).
 .
 .IP "\fIrundir\fB/openvswitch/db.sock\fR"
index ed55834..674c3c3 100755 (executable)
@@ -1,5 +1,5 @@
 #! /bin/sh
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -64,7 +64,12 @@ insert_brcompat_mod_if_required () {
 insert_mod_if_required () {
     insert_openvswitch_mod_if_required || return 1
     if test X"$BRCOMPAT" = Xyes; then
-        insert_brcompat_mod_if_required || return 1
+        if insert_brcompat_mod_if_required; then
+            :
+        else
+            log_warning_msg "could not load brcompat module, disabling bridge compatibility"
+            BRCOMPAT=no
+        fi
     fi
 }
 
@@ -73,7 +78,7 @@ ovs_vsctl () {
 }
 
 ovsdb_tool () {
-    ovsdb-tool -vANY:console:off "$@"
+    ovsdb-tool -vconsole:off "$@"
 }
 
 create_db () {
@@ -167,63 +172,72 @@ set_system_ids () {
     action "Configuring Open vSwitch system IDs" "$@" $extra_ids
 }
 
-start () {
+check_force_cores () {
     if test X"$FORCE_COREFILES" = Xyes; then
         ulimit -Sc 67108864
     fi
+}
 
-    insert_mod_if_required || return 1
+start_ovsdb () {
+    check_force_cores
 
     if daemon_is_running ovsdb-server; then
-       log_success_msg "ovsdb-server is already running"
+        log_success_msg "ovsdb-server is already running"
     else
-       # Create initial database or upgrade database schema.
-       upgrade_db || return 1
-
-       # Start ovsdb-server.
-       set ovsdb-server "$DB_FILE"
-       set "$@" -vANY:CONSOLE:EMER -vANY:SYSLOG:ERR -vANY:FILE:INFO
-       set "$@" --remote=punix:"$DB_SOCK"
-       set "$@" --remote=db:Open_vSwitch,manager_options
-       set "$@" --private-key=db:SSL,private_key
-       set "$@" --certificate=db:SSL,certificate
-       set "$@" --bootstrap-ca-cert=db:SSL,ca_cert
-       start_daemon "$OVSDB_SERVER_PRIORITY" "$@" || return 1
-
-       # Initialize database settings.
-       ovs_vsctl -- init -- set Open_vSwitch . db-version="$schemaver" \
-           || return 1
-       set_system_ids || return 1
-       if test X"$DELETE_BRIDGES" = Xyes; then
+        # Create initial database or upgrade database schema.
+        upgrade_db || return 1
+
+        # Start ovsdb-server.
+        set ovsdb-server "$DB_FILE"
+        set "$@" -vconsole:emer -vsyslog:err -vfile:info
+        set "$@" --remote=punix:"$DB_SOCK"
+        set "$@" --remote=db:Open_vSwitch,manager_options
+        set "$@" --private-key=db:SSL,private_key
+        set "$@" --certificate=db:SSL,certificate
+        set "$@" --bootstrap-ca-cert=db:SSL,ca_cert
+        start_daemon "$OVSDB_SERVER_PRIORITY" "$OVSDB_SERVER_WRAPPER" "$@" \
+            || return 1
+
+        # Initialize database settings.
+        ovs_vsctl -- init -- set Open_vSwitch . db-version="$schemaver" \
+            || return 1
+        set_system_ids || return 1
+        if test X"$DELETE_BRIDGES" = Xyes; then
             for bridge in `ovs_vsctl list-br`; do
-               ovs_vsctl del-br $bridge
+        ovs_vsctl del-br $bridge
             done
-       fi
+        fi
     fi
+}
+
+start_forwarding () {
+    check_force_cores
+
+    insert_mod_if_required || return 1
 
     if daemon_is_running ovs-vswitchd; then
-       log_success_msg "ovs-vswitchd is already running"
+        log_success_msg "ovs-vswitchd is already running"
     else
-       # Increase the limit on the number of open file descriptors.
-       # ovs-vswitchd needs 16 per datapath, plus a few extra, so this
-       # should allow for 256 (or more) bridges.
-       ulimit -n 5000
-
-       # Start ovs-vswitchd.
-       set ovs-vswitchd unix:"$DB_SOCK"
-       set "$@" -vANY:CONSOLE:EMER -vANY:SYSLOG:ERR -vANY:FILE:INFO
-       if test X"$MLOCKALL" != Xno; then
-           set "$@" --mlockall
-       fi
-       start_daemon "$OVS_VSWITCHD_PRIORITY" "$@"
+        # Increase the limit on the number of open file descriptors.
+        # ovs-vswitchd needs 16 per datapath, plus a few extra, so this
+        # should allow for 256 (or more) bridges.
+        ulimit -n 5000
+
+           # Start ovs-vswitchd.
+           set ovs-vswitchd unix:"$DB_SOCK"
+           set "$@" -vconsole:emer -vsyslog:err -vfile:info
+           if test X"$MLOCKALL" != Xno; then
+               set "$@" --mlockall
+           fi
+           start_daemon "$OVS_VSWITCHD_PRIORITY" "$OVS_VSWITCHD_WRAPPER" "$@"
     fi
 
     if daemon_is_running ovs-brcompatd; then
-       log_success_msg "ovs-brcompatd is already running"
+           log_success_msg "ovs-brcompatd is already running"
     elif test X"$BRCOMPAT" = Xyes; then
         set ovs-brcompatd
-       set "$@" -vANY:CONSOLE:EMER -vANY:SYSLOG:ERR -vANY:FILE:INFO
-       start_daemon "$OVS_BRCOMPATD_PRIORITY" "$@"
+           set "$@" -vconsole:emer -vsyslog:err -vfile:info
+           start_daemon "$OVS_BRCOMPATD_PRIORITY" "$OVS_BRCOMPATD_WRAPPER" "$@"
     fi
 }
 
@@ -231,10 +245,13 @@ start () {
 ## stop ##
 ## ---- ##
 
-stop () {
+stop_ovsdb () {
+    stop_daemon ovsdb-server
+}
+
+stop_forwarding () {
     stop_daemon ovs-brcompatd
     stop_daemon ovs-vswitchd
-    stop_daemon ovsdb-server
 }
 
 ## ----------------- ##
@@ -254,11 +271,11 @@ internal_interfaces () {
     # But ignore interfaces that don't really exist.
     for d in `(ovs_vsctl --bare \
                 -- --columns=name find Interface type=internal \
-               -- list-br) | sort -u`
+                   -- list-br) | sort -u`
     do
         if test -e "/sys/class/net/$d"; then
-           printf "%s " "$d"
-       fi
+               printf "%s " "$d"
+           fi
     done
 }
 
@@ -270,7 +287,12 @@ force_reload_kmod () {
     ifaces=`internal_interfaces`
     action "Detected internal interfaces: $ifaces" true
 
-    stop
+    # Restart the database first, since a large database may take a
+    # while to load, and we want to minimize forwarding disruption.
+    stop_ovsdb
+    start_ovsdb
+
+    stop_forwarding
 
     script=`mktemp`
     trap 'rm -f "$script"' 0 1 2 13 15
@@ -278,7 +300,7 @@ force_reload_kmod () {
         :
     else
         log_warning_msg "Failed to save configuration, not replacing kernel module"
-        start
+        start_forwarding
         exit 1
     fi
     chmod +x "$script"
@@ -299,7 +321,7 @@ force_reload_kmod () {
         action "Removing openvswitch module" rmmod openvswitch
     fi
 
-    start
+    start_forwarding
 
     action "Restoring interface configuration" "$script"
     rc=$?
@@ -311,6 +333,8 @@ force_reload_kmod () {
     log="logger -p daemon.$level -t ovs-save"
     $log "force-reload-kmod interface restore script exited with status $rc:"
     $log -f "$script"
+
+    "$datadir/scripts/ovs-check-dead-ifs"
 }
 
 ## --------------- ##
@@ -375,8 +399,11 @@ set_defaults () {
     OVSDB_SERVER_PRIORITY=-10
     OVS_VSWITCHD_PRIORITY=-10
     OVS_BRCOMPATD_PRIORITY=-10
+    OVSDB_SERVER_WRAPPER=
+    OVS_VSWITCHD_WRAPPER=
+    OVS_BRCOMPATD_WRAPPER=
 
-    DB_FILE=$etcdir/conf.db
+    DB_FILE=$dbdir/conf.db
     DB_SOCK=$rundir/db.sock
     DB_SCHEMA=$datadir/vswitch.ovsschema
 
@@ -440,6 +467,12 @@ Less important options for "start" and "force-reload-kmod":
   --ovs-vswitchd-priority=NICE   set ovs-vswitchd's niceness (default: $OVS_VSWITCHD_PRIORITY)
   --ovs-brcompatd-priority=NICE  set ovs-brcompatd's niceness (default: $OVS_BRCOMPATD_PRIORITY)
 
+Debugging options for "start" and "force-reload-kmod":
+  --ovsdb-server-wrapper=WRAPPER
+  --ovs-vswitchd-wrapper=WRAPPER
+  --ovs-vswitchd-wrapper=WRAPPER
+     run specified daemon under WRAPPER (either 'valgrind' or 'strace')
+
 Options for "start", "force-reload-kmod", "load-kmod", "status", and "version":
   --brcompat         enable Linux bridge compatibility module and daemon
 
@@ -458,8 +491,9 @@ Other options:
   -V, --version               display version information
 
 Default directories with "configure" option and environment variable override:
-  logs: @LOGDIR@ (--log-dir, OVS_LOGDIR)
-  pidfiles and sockets: @RUNDIR@ (--run-dir, OVS_RUNDIR)
+  logs: @LOGDIR@ (--with-logdir, OVS_LOGDIR)
+  pidfiles and sockets: @RUNDIR@ (--with-rundir, OVS_RUNDIR)
+  conf.db: @DBDIR@ (--with-dbdir, OVS_DBDIR)
   system configuration: @sysconfdir@ (--sysconfdir, OVS_SYSCONFDIR)
   data files: @pkgdatadir@ (--pkgdatadir, OVS_PKGDATADIR)
   user binaries: @bindir@ (--bindir, OVS_BINDIR)
@@ -550,10 +584,12 @@ do
 done
 case $command in
     start)
-        start
+        start_ovsdb
+        start_forwarding
         ;;
     stop)
-        stop
+        stop_forwarding
+        stop_ovsdb
         ;;
     status)
         rc=0
@@ -568,7 +604,7 @@ case $command in
         done
         ;;
     force-reload-kmod)
-       force_reload_kmod
+           force_reload_kmod
         ;;
     load-kmod)
         insert_mod_if_required
index b49dd8a..042bcd5 100644 (file)
@@ -16,8 +16,7 @@ ovs\-dpctl \- administer Open vSwitch datapaths
 .SH DESCRIPTION
 .PP
 The \fBovs\-dpctl\fR program can create, modify, and delete Open vSwitch
-datapaths.  A single machine may host up to 256 datapaths (numbered 0
-to 255).
+datapaths.  A single machine may host any number of datapaths.
 .PP
 A newly created datapath is associated with only one network device, a
 virtual network device sometimes called the datapath's ``local port''.
@@ -67,6 +66,9 @@ The following options are currently supported:
 .RS
 .IP "\fBtype=\fItype\fR"
 Specifies the type of port to add.  The default type is \fBsystem\fR.
+.IP "\fBport_no=\fIport\fR"
+Requests a specific port number within the datapath.  If this option is
+not specified then one will be automatically assigned.
 .IP "\fIkey\fB=\fIvalue\fR"
 Adds an arbitrary key-value option to the port's configuration.
 .RE
@@ -79,9 +81,9 @@ Reconfigures each \fIport\fR in \fIdp\fR as specified.  An
 \fIoption\fR of the form \fIkey\fB=\fIvalue\fR adds the specified
 key-value option to the port or overrides an existing key's value.  An
 \fIoption\fR of the form \fIkey\fB=\fR, that is, without a value,
-deletes the key-value named \fIkey\fR.  The type of a port cannot be
-changed, so \fBtype=\fItype\fR is only allowed if \fItype\fR is the
-port's existing type.
+deletes the key-value named \fIkey\fR.  The type and port number of a
+port cannot be changed, so \fBtype\fR and \fBport_no\fR are only allowed if
+they match the existing configuration.
 .TP
 \fBdel\-if \fIdp netdev\fR...
 Removes each \fInetdev\fR from the list of network devices datapath
index 56d648c..6224237 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -43,6 +43,8 @@
 #include "ofpbuf.h"
 #include "packets.h"
 #include "shash.h"
+#include "simap.h"
+#include "smap.h"
 #include "sset.h"
 #include "timeval.h"
 #include "util.h"
@@ -179,7 +181,7 @@ static void run(int retval, const char *message, ...)
     }
 }
 \f
-static void do_add_if(int argc, char *argv[]);
+static void dpctl_add_if(int argc, char *argv[]);
 
 static int if_up(const char *netdev_name)
 {
@@ -214,18 +216,18 @@ parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp)
 }
 
 static void
-do_add_dp(int argc OVS_UNUSED, char *argv[])
+dpctl_add_dp(int argc OVS_UNUSED, char *argv[])
 {
     struct dpif *dpif;
     run(parsed_dpif_open(argv[1], true, &dpif), "add_dp");
     dpif_close(dpif);
     if (argc > 2) {
-        do_add_if(argc, argv);
+        dpctl_add_if(argc, argv);
     }
 }
 
 static void
-do_del_dp(int argc OVS_UNUSED, char *argv[])
+dpctl_del_dp(int argc OVS_UNUSED, char *argv[])
 {
     struct dpif *dpif;
     run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
@@ -234,7 +236,7 @@ do_del_dp(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-do_add_if(int argc OVS_UNUSED, char *argv[])
+dpctl_add_if(int argc OVS_UNUSED, char *argv[])
 {
     bool failure = false;
     struct dpif *dpif;
@@ -245,7 +247,8 @@ do_add_if(int argc OVS_UNUSED, char *argv[])
         const char *name, *type;
         char *save_ptr = NULL;
         struct netdev *netdev = NULL;
-        struct shash args;
+        struct smap args;
+        uint16_t port_no = UINT16_MAX;
         char *option;
         int error;
 
@@ -258,7 +261,7 @@ do_add_if(int argc OVS_UNUSED, char *argv[])
             continue;
         }
 
-        shash_init(&args);
+        smap_init(&args);
         while ((option = strtok_r(NULL, ",", &save_ptr)) != NULL) {
             char *save_ptr_2 = NULL;
             char *key, *value;
@@ -271,7 +274,9 @@ do_add_if(int argc OVS_UNUSED, char *argv[])
 
             if (!strcmp(key, "type")) {
                 type = value;
-            } else if (!shash_add_once(&args, key, value)) {
+            } else if (!strcmp(key, "port_no")) {
+                port_no = atoi(value);
+            } else if (!smap_add_once(&args, key, value)) {
                 ovs_error(0, "duplicate \"%s\" option", key);
             }
         }
@@ -288,7 +293,7 @@ do_add_if(int argc OVS_UNUSED, char *argv[])
             goto next;
         }
 
-        error = dpif_port_add(dpif, netdev, NULL);
+        error = dpif_port_add(dpif, netdev, &port_no);
         if (error) {
             ovs_error(error, "adding %s to %s failed", name, argv[1]);
             goto next;
@@ -309,7 +314,7 @@ next:
 }
 
 static void
-do_set_if(int argc, char *argv[])
+dpctl_set_if(int argc, char *argv[])
 {
     bool failure = false;
     struct dpif *dpif;
@@ -322,7 +327,8 @@ do_set_if(int argc, char *argv[])
         char *save_ptr = NULL;
         char *type = NULL;
         const char *name;
-        struct shash args;
+        struct smap args;
+        uint32_t port_no;
         char *option;
         int error;
 
@@ -340,6 +346,7 @@ do_set_if(int argc, char *argv[])
             goto next;
         }
         type = xstrdup(dpif_port.type);
+        port_no = dpif_port.port_no;
         dpif_port_destroy(&dpif_port);
 
         /* Retrieve its existing configuration. */
@@ -349,7 +356,7 @@ do_set_if(int argc, char *argv[])
             goto next;
         }
 
-        shash_init(&args);
+        smap_init(&args);
         error = netdev_get_config(netdev, &args);
         if (error) {
             ovs_error(error, "%s: failed to fetch configuration", name);
@@ -373,10 +380,17 @@ do_set_if(int argc, char *argv[])
                               name, type, value);
                     failure = true;
                 }
+            } else if (!strcmp(key, "port_no")) {
+                if (port_no != atoi(value)) {
+                    ovs_error(0, "%s: can't change port number from "
+                              "%"PRIu32" to %d",
+                              name, port_no, atoi(value));
+                    failure = true;
+                }
             } else if (value[0] == '\0') {
-                free(shash_find_and_delete(&args, key));
+                smap_remove(&args, key);
             } else {
-                free(shash_replace(&args, key, xstrdup(value)));
+                smap_replace(&args, key, value);
             }
         }
 
@@ -417,7 +431,7 @@ get_port_number(struct dpif *dpif, const char *name, uint16_t *port)
 }
 
 static void
-do_del_if(int argc OVS_UNUSED, char *argv[])
+dpctl_del_if(int argc OVS_UNUSED, char *argv[])
 {
     bool failure = false;
     struct dpif *dpif;
@@ -499,26 +513,26 @@ show_dpif(struct dpif *dpif)
 
             error = netdev_open(dpif_port.name, dpif_port.type, &netdev);
             if (!error) {
-                struct shash config;
+                struct smap config;
 
-                shash_init(&config);
+                smap_init(&config);
                 error = netdev_get_config(netdev, &config);
                 if (!error) {
-                    const struct shash_node **nodes;
+                    const struct smap_node **nodes;
                     size_t i;
 
-                    nodes = shash_sort(&config);
-                    for (i = 0; i < shash_count(&config); i++) {
-                        const struct shash_node *node = nodes[i];
-                        printf("%c %s=%s", i ? ',' : ':',
-                               node->name, (char *) node->data);
+                    nodes = smap_sort(&config);
+                    for (i = 0; i < smap_count(&config); i++) {
+                        const struct smap_node *node = nodes[i];
+                        printf("%c %s=%s", i ? ',' : ':', node->key,
+                               node->value);
                     }
                     free(nodes);
                 } else {
                     printf(", could not retrieve configuration (%s)",
                            strerror(error));
                 }
-                shash_destroy_free_data(&config);
+                smap_destroy(&config);
 
                 netdev_close(netdev);
             } else {
@@ -572,7 +586,7 @@ show_dpif(struct dpif *dpif)
 }
 
 static void
-do_show(int argc, char *argv[])
+dpctl_show(int argc, char *argv[])
 {
     bool failure = false;
     if (argc > 1) {
@@ -627,7 +641,7 @@ do_show(int argc, char *argv[])
 }
 
 static void
-do_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+dpctl_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     struct sset dpif_names, dpif_types;
     const char *type;
@@ -663,7 +677,7 @@ do_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 }
 
 static void
-do_dump_flows(int argc OVS_UNUSED, char *argv[])
+dpctl_dump_flows(int argc OVS_UNUSED, char *argv[])
 {
     const struct dpif_flow_stats *stats;
     const struct nlattr *actions;
@@ -694,7 +708,7 @@ do_dump_flows(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-do_del_flows(int argc OVS_UNUSED, char *argv[])
+dpctl_del_flows(int argc OVS_UNUSED, char *argv[])
 {
     struct dpif *dpif;
 
@@ -704,7 +718,7 @@ do_del_flows(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+dpctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     usage();
 }
@@ -712,7 +726,7 @@ do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 /* Undocumented commands for unit testing. */
 
 static void
-do_parse_actions(int argc, char *argv[])
+dpctl_parse_actions(int argc, char *argv[])
 {
     int i;
 
@@ -824,9 +838,9 @@ sort_output_actions(struct nlattr *actions, size_t length)
  * The idea here generalizes beyond VLANs (e.g. to setting other fields) but
  * so far the implementation only covers VLANs. */
 static void
-do_normalize_actions(int argc, char *argv[])
+dpctl_normalize_actions(int argc, char *argv[])
 {
-    struct shash port_names;
+    struct simap port_names;
     struct ofpbuf keybuf;
     struct flow flow;
     struct ofpbuf odp_actions;
@@ -841,7 +855,7 @@ do_normalize_actions(int argc, char *argv[])
 
     ds_init(&s);
 
-    shash_init(&port_names);
+    simap_init(&port_names);
     for (i = 3; i < argc; i++) {
         char name[16];
         int number;
@@ -849,7 +863,7 @@ do_normalize_actions(int argc, char *argv[])
 
         if (sscanf(argv[i], "%15[^=]=%d%n", name, &number, &n) > 0 && n > 0) {
             uintptr_t n = number;
-            shash_add(&port_names, name, (void *) n);
+            simap_put(&port_names, name, n);
         } else {
             ovs_fatal(0, "%s: expected NAME=NUMBER", argv[i]);
         }
@@ -930,20 +944,20 @@ do_normalize_actions(int argc, char *argv[])
 }
 
 static const struct command all_commands[] = {
-    { "add-dp", 1, INT_MAX, do_add_dp },
-    { "del-dp", 1, 1, do_del_dp },
-    { "add-if", 2, INT_MAX, do_add_if },
-    { "del-if", 2, INT_MAX, do_del_if },
-    { "set-if", 2, INT_MAX, do_set_if },
-    { "dump-dps", 0, 0, do_dump_dps },
-    { "show", 0, INT_MAX, do_show },
-    { "dump-flows", 1, 1, do_dump_flows },
-    { "del-flows", 1, 1, do_del_flows },
-    { "help", 0, INT_MAX, do_help },
+    { "add-dp", 1, INT_MAX, dpctl_add_dp },
+    { "del-dp", 1, 1, dpctl_del_dp },
+    { "add-if", 2, INT_MAX, dpctl_add_if },
+    { "del-if", 2, INT_MAX, dpctl_del_if },
+    { "set-if", 2, INT_MAX, dpctl_set_if },
+    { "dump-dps", 0, 0, dpctl_dump_dps },
+    { "show", 0, INT_MAX, dpctl_show },
+    { "dump-flows", 1, 1, dpctl_dump_flows },
+    { "del-flows", 1, 1, dpctl_del_flows },
+    { "help", 0, INT_MAX, dpctl_help },
 
     /* Undocumented commands for testing. */
-    { "parse-actions", 1, INT_MAX, do_parse_actions },
-    { "normalize-actions", 2, INT_MAX, do_normalize_actions },
+    { "parse-actions", 1, INT_MAX, dpctl_parse_actions },
+    { "normalize-actions", 2, INT_MAX, dpctl_normalize_actions },
 
     { NULL, 0, 0, NULL },
 };
diff --git a/utilities/ovs-l3ping.8.in b/utilities/ovs-l3ping.8.in
new file mode 100644 (file)
index 0000000..d3ce3cb
--- /dev/null
@@ -0,0 +1,114 @@
+.de IQ
+.  br
+.  ns
+.  IP "\\$1"
+..
+.TH ovs\-l3ping 1 "June 2012" "Open vSwitch" "Open vSwitch Manual"
+.
+.SH NAME
+\fBovs\-l3ping\fR \- check network deployment for L3 tunneling
+problems
+.
+.SH SYNOPSIS
+\fBovs\-l3ping\fR \fB\-s\fR \fITunnelRemoteIP,InnerIP[/mask]\fR \fB\-t\fR \fItunnelmode\fR
+.br
+\fBovs\-l3ping\fR \fB\-s\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR \fB\-t\fR \fItunnelmode\fR
+.PP
+\fBovs\-l3ping\fR \fB\-c\fR \fITunnelRemoteIP,InnerIP[/mask],RemoteInnerIP\fR \fB\-t\fR \fItunnelmode\fR
+.br
+\fBovs\-l3ping\fR \fB\-c\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\
+[:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR
+[\fB\-b\fR \fItargetbandwidth\fR] [\fB\-i\fR \fItestinterval\fR]
+\fB\-t\fR \fItunnelmode\fR
+.so lib/common-syn.man
+.
+.SH DESCRIPTION
+The \fBovs\-l3ping\fR program may be used to check for problems that could
+be caused by invalid routing policy, misconfigured firewall in the tunnel
+path or a bad NIC driver.  On one of the nodes, run \fBovs\-l3ping\fR in
+server mode and on the other node run it in client mode.  The client and
+server will establish L3 tunnel, over which client will give further testing
+instructions. The \fBovs\-l3ping\fR client will perform UDP and TCP tests.
+This tool is different from \fBovs\-test\fR that it encapsulates XML/RPC
+control connection over the tunnel, so there is no need to open special holes
+in firewall.
+.PP
+UDP tests can report packet loss and achieved bandwidth for various
+datagram sizes. By default target bandwidth for UDP tests is 1Mbit/s.
+.PP
+TCP tests report only achieved bandwidth, because kernel TCP stack
+takes care of flow control and packet loss.
+.
+.SS "Client Mode"
+An \fBovs\-l3ping\fR client will create a L3 tunnel and connect over it to the
+\fBovs\-l3ping\fR server to schedule the tests. \fITunnelRemoteIP\fR is the
+peer's IP address, where tunnel will be terminated. \fIInnerIP\fR is the
+address that will be temporarily assigned during testing.  All test traffic
+originating from this IP address to the \fIRemoteInnerIP\fR will be tunneled.
+It is possible to override default \fIControlPort\fR and \fIDataPort\fR, if
+there is any other application that already listens on those two ports.
+.
+.SS "Server Mode"
+To conduct tests, \fBovs\-l3ping\fR server must be running.  It is required
+that both client and server \fIInnerIP\fR addresses are in the same subnet.
+It is possible to specify \fIInnerIP\fR with netmask in CIDR format.
+.
+.SH OPTIONS
+One of \fB\-s\fR or \fB\-c\fR is required.  The \fB\-t\fR option is
+also required.
+.
+.IP "\fB\-s \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR"
+.IQ "\fB\-\-server\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR"
+Run in server mode and create L3 tunnel with the client that will be
+accepting tunnel at \fITunnelRemoteIP\fR address.  The socket on
+\fIInnerIP[:ControlPort]\fR will be used to receive further instructions
+from the client.
+.
+.IP "\fB\-c \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\
+[:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR"
+.IQ "\fB\-\-client \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\
+[:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR"
+Run in client mode and create L3 tunnel with the server on
+\fITunnelRemoteIP\fR.  The client will use \fIInnerIP\fR to generate test
+traffic with the server's \fIRemoteInnerIP\fR.
+.
+.IP "\fB\-b \fItargetbandwidth\fR"
+.IQ "\fB\-\-bandwidth\fR \fItargetbandwidth\fR"
+Target bandwidth for UDP tests. The \fItargetbandwidth\fR must be given in
+bits per second. It is possible to use postfix M or K to alter the target
+bandwidth magnitude.
+.
+.IP "\fB\-i \fItestinterval\fR"
+.IQ "\fB\-\-interval\fR \fItestinterval\fR"
+How long each test should run. By default 5 seconds.
+.
+.IP "\fB\-t \fItunnelmode\fR"
+.IQ "\fB\-\-tunnel\-mode\fR \fItunnelmode\fR"
+Specify the tunnel type. This option must match on server and client.
+.
+.so lib/common.man
+.
+.SH EXAMPLES
+.PP
+On host 192.168.122.220 start \fBovs\-l3ping\fR in server mode.  This command
+will create a temporary GRE tunnel with the host 192.168.122.236 and assign
+10.1.1.1/28 as the inner IP address, where client will have to connect:
+.IP
+.B ovs\-l3ping -s 192.168.122.236,10.1.1.1/28 -t gre
+.
+.PP
+On host 192.168.122.236 start \fBovs\-l3ping\fR in client mode.  This command
+will use 10.1.1.2/28 as the local inner IP address and will connect over the
+L3 tunnel to the server's inner IP address at 10.1.1.1.
+.IP
+.B ovs\-l3ping -c 192.168.122.220,10.1.1.2/28,10.1.1.1 -t gre
+.
+.SH SEE ALSO
+.
+.BR ovs\-vswitchd (8),
+.BR ovs\-ofctl (8),
+.BR ovs\-vsctl (8),
+.BR ovs\-vlan\-test (8),
+.BR ovs\-test (8),
+.BR ethtool (8),
+.BR uname (1)
diff --git a/utilities/ovs-l3ping.in b/utilities/ovs-l3ping.in
new file mode 100644 (file)
index 0000000..1b07972
--- /dev/null
@@ -0,0 +1,76 @@
+#! @PYTHON@
+#
+# 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.
+
+"""
+ovs L3 ping utility allows to do tests between two remote hosts without
+opening holes in the firewall for the XML RPC control connection.  This is
+achieved by tunneling the control connection inside the tunnel itself.
+"""
+
+import socket
+import xmlrpclib
+
+import ovstest.args as args
+import ovstest.tests as tests
+import ovstest.util as util
+
+
+def get_packet_sizes(me, he, remote_ip):
+    """
+    This function retrieves MTUs from both hosts and returns a list of
+    packet sizes, that are more likely to uncover possible configuration
+    issues.
+    """
+    mtu_node1 = 1500
+    mtu_node2 = 1500
+    server1 = util.rpc_client(me[0], me[1])
+    server2 = util.rpc_client(he[0], he[1])
+    iface1 = server2.get_interface(remote_ip)
+    iface2 = server1.get_interface_from_routing_decision(remote_ip)
+    if iface1:
+        mtu_node1 = server2.get_interface_mtu(iface1)
+    if iface2:
+        mtu_node2 = server1.get_interface_mtu(iface2)
+    return util.get_datagram_sizes(mtu_node1, mtu_node2)
+
+
+if __name__ == '__main__':
+    local_server = None
+    try:
+        args = args.l3_initialize_args()
+        tunnel_mode = args.tunnelMode
+        if args.server is not None:  # Start in server mode
+            local_server = tests.configure_l3(args.server, tunnel_mode)
+            local_server.wait()
+        elif args.client is not None:  # Run in client mode
+            bandwidth = args.targetBandwidth
+            interval = args.testInterval
+            me = (util.ip_from_cidr(args.client[1][0]), args.client[1][1],
+                  args.client[1][0], args.client[1][2])
+            he = (args.client[2][0], args.client[2][1],
+                  args.client[2][0], args.client[2][2])
+            local_server = tests. configure_l3(args.client, tunnel_mode)
+            ps = get_packet_sizes(me, he, args.client[0])
+            tests.do_direct_tests(me, he, bandwidth, interval, ps)
+    except KeyboardInterrupt:
+        print "Terminating"
+    except xmlrpclib.Fault:
+        print "Couldn't contact peer"
+    except socket.error:
+        print "Couldn't contact peer"
+    except xmlrpclib.ProtocolError:
+        print "XMLRPC control channel was abruptly terminated"
+    finally:
+        if local_server is not None:
+            local_server.terminate()
index 7a15aae..3c63ddd 100644 (file)
@@ -1,7 +1,7 @@
 # This is a shell function library sourced by some Open vSwitch scripts.
 # It is not intended to be invoked on its own.
 
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -28,6 +28,15 @@ datadir=${OVS_PKGDATADIR-'@pkgdatadir@'}        # /usr/share/openvswitch
 bindir=${OVS_BINDIR-'@bindir@'}                 # /usr/bin
 sbindir=${OVS_SBINDIR-'@sbindir@'}              # /usr/sbin
 
+# /etc/openvswitch or /var/lib/openvswitch
+if test X"$OVS_DBDIR" != X; then
+    dbdir=$OVS_DBDIR
+elif test X"$OVS_SYSCONFDIR" != X; then
+    dbdir=$OVS_SYSCONFDIR/openvswitch
+else
+    dbdir='@DBDIR@'
+fi
+
 VERSION='@VERSION@'
 
 LC_ALL=C; export LC_ALL
@@ -88,8 +97,10 @@ pid_exists () {
 
 start_daemon () {
     priority=$1
-    shift
+    wrapper=$2
+    shift; shift
     daemon=$1
+    strace=""
 
     # drop core files in a sensible place
     test -d "$DAEMON_CWD" || install -d -m 755 -o root -g root "$DAEMON_CWD"
@@ -105,12 +116,47 @@ start_daemon () {
     set "$@" --pidfile="$rundir/$daemon.pid"
     set "$@" --detach --monitor
 
+    # wrapper
+    case $wrapper in
+        valgrind)
+            if (valgrind --version) > /dev/null 2>&1; then
+                set valgrind -q --leak-check=full --time-stamp=yes \
+                    --log-file="$logdir/$daemon.valgrind.log.%p" "$@"
+            else
+                log_failure_msg "valgrind not installed, running $daemon without it"
+            fi
+            ;;
+        strace)
+            if (strace -V) > /dev/null 2>&1; then
+                strace="strace -tt -T -s 256 -ff"
+                if (strace -DV) > /dev/null 2>&1; then
+                    # Has the -D option.
+                    set $strace -D -o "$logdir/$daemon.strace.log" "$@"
+                    strace=""
+                fi
+            else
+                log_failure_msg "strace not installed, running $daemon without it"
+            fi
+            ;;
+        '')
+            ;;
+        *)
+            log_failure_msg "unknown wrapper $wrapper, running $daemon without it"
+            ;;
+    esac
+
     # priority
     if test X"$priority" != X; then
         set nice -n "$priority" "$@"
     fi
 
     action "Starting $daemon" "$@"
+
+    if test X"$strace" != X; then
+        # Strace doesn't have the -D option so we attach after the fact.
+        setsid $strace -o "$logdir/$daemon.strace.log" \
+            -p `cat $rundir/$daemon.pid` > /dev/null 2>&1 &
+    fi
 }
 
 DAEMON_CWD=/
index 1876929..aad9325 100644 (file)
@@ -66,6 +66,12 @@ associated with that device will be printed.  \fInetdev\fR can be an
 OpenFlow assigned port number or device name, e.g. \fBeth0\fR.
 .
 .TP
+\fBdump\-ports\-desc \fIswitch\fR
+Prints to the console detailed information about network devices
+associated with \fIswitch\fR (version 1.7 or later).  This is a subset
+of the information provided by the \fBshow\fR command.
+.
+.TP
 \fBmod\-port \fIswitch\fR \fInetdev\fR \fIaction\fR
 Modify characteristics of an interface monitored by \fIswitch\fR.  
 \fInetdev\fR can be referred to by its OpenFlow assigned port number or 
@@ -73,31 +79,49 @@ the device name, e.g. \fBeth0\fR.  The \fIaction\fR may be any one of the
 following:
 .
 .RS
-.IP \fBup\fR
-Enables the interface.  This is equivalent to ``ifconfig up'' on a Unix
-system.
-.
-.IP \fBdown\fR
-Disables the interface.  This is equivalent to ``ifconfig down'' on a Unix
-system.
+.IQ \fBup\fR
+.IQ \fBdown\fR
+Enable or disable the interface.  This is equivalent to \fBifconfig
+up\fR or \fBifconfig down\fR on a Unix system.
+.
+.IP \fBstp\fR
+.IQ \fBno\-stp\fR
+Enable or disable 802.1D spanning tree protocol (STP) on the
+interface.  OpenFlow implementations that don't support STP will
+refuse to enable it.
+.
+.IP \fBreceive\fR
+.IQ \fBno\-receive\fR
+.IQ \fBreceive\-stp\fR
+.IQ \fBno\-receive\-stp\fR
+Enable or disable OpenFlow processing of packets received on this
+interface.  When packet processing is disabled, packets will be
+dropped instead of being processed through the OpenFlow table.  The
+\fBreceive\fR or \fBno\-receive\fR setting applies to all packets
+except 802.1D spanning tree packets, which are separately controlled
+by \fBreceive\-stp\fR or \fBno\-receive\-stp\fR.
 .
 .IP \fBforward\fR
-Allows forwarding of traffic on this interface.  This is the default posture
-for all ports.
-.
-.IP \fBnoforward\fR
-Disallows forwarding of traffic on this interface.
+.IQ \fBno\-forward\fR
+Allow or disallow forwarding of traffic to this interface.  By
+default, forwarding is enabled.
 .
 .IP \fBflood\fR
-When a \fIflood\fR action is specified, traffic will be sent out this
-interface.  This is the default posture for monitored ports.
-.
-.IP \fBnoflood\fR
-When a \fIflood\fR action is specified, traffic will not be sent out 
-this interface.  This is primarily useful to prevent loops when a
-spanning tree protocol is not in use.
-.
+.IQ \fBno\-flood\fR
+Controls whether an OpenFlow \fBflood\fR action will send traffic out
+this interface.  By default, flooding is enabled.  Disabling flooding
+is primarily useful to prevent loops when a spanning tree protocol is
+not in use.
+.
+.IP \fBpacket\-in\fR
+.IQ \fBno\-packet\-in\fR
+Controls whether packets received on this interface that do not match
+a flow table entry generate a ``packet in'' message to the OpenFlow
+controller.  By default, ``packet in'' messages are enabled.
 .RE
+.IP
+The \fBshow\fR command displays (among other information) the
+configuration that \fBmod\-port\fR changes.
 .
 .IP "\fBget\-frags \fIswitch\fR"
 Prints \fIswitch\fR's fragment handling mode.  See \fBset\-frags\fR,
@@ -137,12 +161,18 @@ whether a packet is a fragment and on its fragment offset.
 Prints to the console all flow entries in \fIswitch\fR's
 tables that match \fIflows\fR.  If \fIflows\fR is omitted, all flows
 in the switch are retrieved.  See \fBFlow Syntax\fR, below, for the
-syntax of \fIflows\fR.  The output format is described in 
+syntax of \fIflows\fR.  The output format is described in
 \fBTable Entry Output\fR.
 .
+.IP
+By default, \fBovs\-ofctl\fR prints flow entries in the same order
+that the switch sends them, which is unlikely to be intuitive or
+consistent.  See the description of \fB\-\-sort\fR and \fB\-\-rsort\fR,
+under \fBOPTIONS\fR below, to influence the display order.
+.
 .TP
 \fBdump\-aggregate \fIswitch \fR[\fIflows\fR]
-Prints to the console aggregate statistics for flows in 
+Prints to the console aggregate statistics for flows in
 \fIswitch\fR's tables that match \fIflows\fR.  If \fIflows\fR is omitted, 
 the statistics are aggregated across all flows in the switch's flow
 tables.  See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR.
@@ -252,7 +282,7 @@ If a switch has no controller configured, or if
 the configured controller is disconnected, no traffic is sent, so
 monitoring will not show any traffic.
 .
-.IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR] [\fIinvalid_ttl\fR]"
+.IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR] [\fBinvalid_ttl\fR] [\fBwatch:\fR[\fIspec\fR...]]"
 Connects to \fIswitch\fR and prints to the console all OpenFlow
 messages received.  Usually, \fIswitch\fR should specify the name of a
 bridge in the \fBovs\-vswitchd\fR database.
@@ -265,15 +295,46 @@ does not send these and other asynchronous messages to an
 specified on this argument.  (Thus, if \fImiss\-len\fR is not
 specified, very little traffic will ordinarily be printed.)
 .IP
-.IP
 If \fBinvalid_ttl\fR is passed, \fBovs\-ofctl\fR sends an OpenFlow ``set
 configuration'' message at connection setup time that requests
-\fIINVALID_TTL_TO_CONTROLLER\fR, so that \fBovs\-ofctl monitor\fR can
-receive ``packets-in'' messages when TTL reaches zero on \fBdec_ttl\fR action.
+\fBINVALID_TTL_TO_CONTROLLER\fR, so that \fBovs\-ofctl monitor\fR can
+receive ``packet-in'' messages when TTL reaches zero on \fBdec_ttl\fR action.
+.IP
+\fBwatch:\fR[\fB\fIspec\fR...] causes \fBovs\-ofctl\fR to send a
+``monitor request'' Nicira extension message to the switch at
+connection setup time.  This message causes the switch to send
+information about flow table changes as they occur.  The following
+comma-separated \fIspec\fR syntax is available:
+.RS
+.IP "\fB!initial\fR"
+Do not report the switch's initial flow table contents.
+.IP "\fB!add\fR"
+Do not report newly added flows.
+.IP "\fB!delete\fR"
+Do not report deleted flows.
+.IP "\fB!modify\fR"
+Do not report modifications to existing flows.
+.IP "\fB!own\fR"
+Abbreviate changes made to the flow table by \fBovs\-ofctl\fR's own
+connection to the switch.  (These could only occur using the
+\fBofctl/send\fR command described below under \fBRUNTIME MANAGEMENT
+COMMANDS\fR.)
+.IP "\fB!actions\fR"
+Do not report actions as part of flow updates.
+.IP "\fBtable=\fInumber\fR"
+Limits the monitoring to the table with the given \fInumber\fR between
+0 and 254.  By default, all tables are monitored.
+.IP "\fBout_port=\fIport\fR"
+If set, only flows that output to \fIport\fR are monitored.
+.IP "\fIfield\fB=\fIvalue\fR"
+Monitors only flows that have \fIfield\fR specified as the given
+\fIvalue\fR.  Any syntax valid for matching on \fBdump\-flows\fR may
+be used.
+.RE
 .IP
-
 This command may be useful for debugging switch or controller
-implementations.
+implementations.  With \fBwatch:\fR, it is particularly useful for
+observing how a controller updates flow tables.
 .
 .SS "OpenFlow Switch and Controller Commands"
 .
@@ -357,11 +418,13 @@ Matches an Ethernet source (or destination) address specified as 6
 pairs of hexadecimal digits delimited by colons
 (e.g. \fB00:0A:E4:25:6B:B0\fR).
 .
-.IP \fBdl_dst=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
+.IP \fBdl_src=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
+.IQ \fBdl_dst=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
 Matches an Ethernet destination address specified as 6 pairs of
 hexadecimal digits delimited by colons (e.g. \fB00:0A:E4:25:6B:B0\fR),
-with a wildcard mask following the slash.  Only
-the following masks are allowed:
+with a wildcard mask following the slash. Open vSwitch 1.8 and later
+support arbitrary masks for source and/or destination. Earlier
+versions only support masking the destination with the following masks:
 .RS
 .IP \fB01:00:00:00:00:00\fR
 Match only the multicast bit.  Thus,
@@ -392,7 +455,9 @@ which may be specified as an IP address or host name
 \fInetmask\fR allows restricting a match to an IPv4 address prefix.
 The netmask may be specified as a dotted quad
 (e.g. \fB192.168.1.0/255.255.255.0\fR) or as a CIDR block
-(e.g. \fB192.168.1.0/24\fR).
+(e.g. \fB192.168.1.0/24\fR).  Open vSwitch 1.8 and later support
+arbitrary dotted quad masks; earlier versions support only CIDR masks,
+that is, the dotted quads that are equivalent to some CIDR block.
 .IP
 When \fBdl_type=0x0806\fR or \fBarp\fR is specified, matches the
 \fBar_spa\fR or \fBar_tpa\fR field, respectively, in ARP packets for
@@ -549,6 +614,14 @@ When this field is specified in \fBadd-flow\fR, \fBadd-flows\fR,
 extension to OpenFlow, which as of this writing is only known to be
 implemented by Open vSwitch.
 .
+.IP \fBmetadata=\fIvalue\fR[\fB/\fImask\fR]
+Matches \fIvalue\fR either exactly or with optional \fImask\fR in the metadata
+field. \fIvalue\fR and \fImask\fR are 64-bit integers, by default in decimal
+(use a \fB0x\fR prefix to specify hexadecimal). Arbitrary \fImask\fR values
+are allowed: a 1-bit in \fImask\fR indicates that the corresponding bit in
+\fIvalue\fR must match exactly, and a 0-bit wildcards that bit. Matching on
+metadata was added in Open vSwitch 1.8.
+.
 .PP
 The following shorthand notations are also available:
 .
@@ -648,7 +721,11 @@ which may be specified as defined in RFC 2373.  The preferred format is
 address.  A single instance of \fB::\fR may be used to indicate multiple
 groups of 16-bits of zeros.  The optional \fInetmask\fR allows
 restricting a match to an IPv6 address prefix.  A netmask is specified
-as a CIDR block (e.g. \fB2001:db8:3c4d:1::/64\fR).
+as an IPv6 address (e.g. \fB2001:db8:3c4d:1::/ffff:ffff:ffff:ffff::\fR)
+or a CIDR block (e.g. \fB2001:db8:3c4d:1::/64\fR).  Open vSwitch 1.8
+and later support arbitrary masks; earlier versions support only CIDR
+masks, that is, CIDR block and IPv6 addresses that are equivalent to
+CIDR blocks.
 .
 .IP \fBipv6_label=\fIlabel\fR
 When \fBdl_type\fR is 0x86dd (possibly via shorthand, e.g., \fBipv6\fR
@@ -873,19 +950,29 @@ Restores the queue to the value it was before any \fBset_queue\fR
 actions were applied.
 .
 .IP \fBdec_ttl\fR
+.IQ \fBdec_ttl\fB[\fR(\fIid1,id2\fI)\fR]\fR
 Decrement TTL of IPv4 packet or hop limit of IPv6 packet.  If the
 TTL or hop limit is initially zero, no decrement occurs.  Instead,
 a ``packet-in'' message with reason code \fBOFPR_INVALID_TTL\fR is
 sent to each connected controller that has enabled receiving them,
-if any.  Processing the current set of actions then stops.
-However, if the current set of actions was reached through
-``resubmit'' then remaining actions in outer levels resume
-processing.
+if any.  Processing the current set of actions then stops.  However,
+if the current set of actions was reached through ``resubmit'' then
+remaining actions in outer levels resume processing.  This action
+also optionally supports the ability to specify a list of valid
+controller ids.  Each of controllers in the list will receive the
+``packet_in'' message only if they have registered to receive the
+invalid ttl packets.  If controller ids are not specified, the
+``packet_in'' message will be sent only to the controllers having
+controller id zero which have registered for the invalid ttl packets.
 .
 .IP \fBnote:\fR[\fIhh\fR]...
 Does nothing at all.  Any number of bytes represented as hex digits
 \fIhh\fR may be included.  Pairs of hex digits may be separated by
 periods for readability.
+The \fBnote\fR action's format doesn't include an exact length for its
+payload, so the provided bytes will be padded on the right by enough
+bytes with value 0 to make the total number 6 more than a multiple of
+8.
 .
 .IP "\fBmove:\fIsrc\fB[\fIstart\fB..\fIend\fB]\->\fIdst\fB[\fIstart\fB..\fIend\fB]\fR"
 Copies the named bits from field \fIsrc\fR to field \fIdst\fR.
@@ -926,6 +1013,8 @@ the \fBiter_hash\fR algorithm uses \fIarg\fR.
 Refer to \fBnicira\-ext.h\fR for more details.
 .
 .IP "\fBautopath(\fIid\fB, \fIdst\fB[\fIstart\fB..\fIend\fB])\fR"
+Deprecated and slated for removal in Feburary 2013.
+.IP
 Given \fIid\fR, chooses an OpenFlow port and populates it in
 \fIdst\fB[\fIstart\fB..\fIend\fB]\fR, which must be an NXM field as
 described above.
@@ -1066,26 +1155,42 @@ levels of the \fBresubmit\fR call stack, are ignored.
 An opaque identifier called a cookie can be used as a handle to identify
 a set of flows:
 .
-.IP \fBcookie=\fIvalue\fR[\fB/\fImask\fR]
+.IP \fBcookie=\fIvalue\fR
+.
+A cookie can be associated with a flow using the \fBadd\-flow\fR,
+\fBadd\-flows\fR, and \fBmod\-flows\fR commands.  \fIvalue\fR can be any
+64-bit number and need not be unique among flows.  If this field is
+omitted, a default cookie value of 0 is used.
+.
+.IP \fBcookie=\fIvalue\fR\fB/\fImask\fR
 .
-A cookie can be associated with a flow using the \fBadd-flow\fR and
-\fBadd-flows\fR commands.  \fIvalue\fR can be any 64-bit number and need
-not be unique among flows.  If this field is omitted, a default cookie
-value of 0 is used.
-.IP
 When using NXM, the cookie can be used as a handle for querying,
-modifying, and deleting flows.  In addition to \fIvalue\fR, an optional
-\fImask\fR may be supplied for the \fBdel-flows\fR, \fBmod-flows\fR,
-\fBdump-flows\fR, and \fBdump-aggregate\fR commands to limit matching
-cookies.  A 1-bit in \fImask\fR indicates that the corresponding bit in
-\fIcookie\fR must match exactly, and a 0-bit wildcards that bit.
+modifying, and deleting flows.  \fIvalue\fR and \fImask\fR may be
+supplied for the \fBdel\-flows\fR, \fBmod\-flows\fR, \fBdump\-flows\fR, and
+\fBdump\-aggregate\fR commands to limit matching cookies.  A 1-bit in
+\fImask\fR indicates that the corresponding bit in \fIcookie\fR must
+match exactly, and a 0-bit wildcards that bit.  A mask of \-1 may be used
+to exactly match a cookie.
+.IP
+The \fBmod\-flows\fR command can update the cookies of flows that
+match a cookie by specifying the \fIcookie\fR field twice (once with a
+mask for matching and once without to indicate the new value):
+.RS
+.IP "\fBovs\-ofctl mod\-flows br0 cookie=1,actions=normal\fR"
+Change all flows' cookies to 1 and change their actions to \fBnormal\fR.
+.IP "\fBovs\-ofctl mod\-flows br0 cookie=1/\-1,cookie=2,actions=normal\fR"
+Update cookies with a value of 1 to 2 and change their actions to
+\fBnormal\fR.
+.RE
+.IP
+The ability to match on cookies was added in Open vSwitch 1.5.0.
 .
 .PP
 The following additional field sets the priority for flows added by
 the \fBadd\-flow\fR and \fBadd\-flows\fR commands.  For
 \fBmod\-flows\fR and \fBdel\-flows\fR when \fB\-\-strict\fR is
 specified, priority must match along with the rest of the flow
-specification.  For \fBmod\-flows\fR without \fB\-\-strict\fR,
+specification.  For \fBmod-flows\fR without \fB\-\-strict\fR,
 priority is only significant if the command creates a new flow, that
 is, non-strict \fBmod\-flows\fR does not match on priority and will
 not change the priority of existing flows.  Other commands do not
@@ -1098,6 +1203,13 @@ others.  \fIvalue\fR is a number between 0 and 65535, inclusive.  A higher
 have priority over an entry containing wildcards, so it has an implicit 
 priority value of 65535.  When adding a flow, if the field is not specified, 
 the flow's priority will default to 32768.
+.IP
+OpenFlow leaves behavior undefined when two or more flows with the
+same priority can match a single packet.  Some users expect
+``sensible'' behavior, such as more specific flows taking precedence
+over less specific flows, but OpenFlow does not specify this and Open
+vSwitch does not implement it.  Users should therefore take care to
+use priorities to ensure the behavior that they expect.
 .
 .PP
 The \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands
@@ -1258,6 +1370,33 @@ Increases the verbosity of OpenFlow messages printed and logged by
 \fBovs\-ofctl\fR commands.  Specify this option more than once to
 increase verbosity further.
 .
+.IP \fB\-\-sort\fR[\fB=\fIfield\fR]
+.IQ \fB\-\-rsort\fR[\fB=\fIfield\fR]
+Display output sorted by flow \fIfield\fR in ascending
+(\fB\-\-sort\fR) or descending (\fB\-\-rsort\fR) order, where
+\fIfield\fR is any of the fields that are allowed for matching or
+\fBpriority\fR to sort by priority.  When \fIfield\fR is omitted, the
+output is sorted by priority.  Specify these options multiple times to
+sort by multiple fields.
+.IP
+Any given flow will not necessarily specify a value for a given
+field.  This requires special treatement:
+.RS
+.IP \(bu
+A flow that does not specify any part of a field that is used for sorting is
+sorted after all the flows that do specify the field.  For example,
+\fB\-\-sort=tcp_src\fR will sort all the flows that specify a TCP
+source port in ascending order, followed by the flows that do not
+specify a TCP source port at all.  
+.IP \(bu
+A flow that only specifies some bits in a field is sorted as if the
+wildcarded bits were zero.  For example, \fB\-\-sort=nw_src\fR would
+sort a flow that specifies \fBnw_src=192.168.0.0/24\fR the same as
+\fBnw_src=192.168.0.0\fR.
+.RE
+.IP
+These options currently affect only \fBdump\-flows\fR output.
+.
 .ds DD \
 \fBovs\-ofctl\fR detaches only when executing the \fBmonitor\fR or \
 \fBsnoop\fR commands.
index 2b91a28..5d10b37 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
  */
 
 #include <config.h>
+#include <ctype.h>
 #include <errno.h>
 #include <getopt.h>
 #include <inttypes.h>
 #include "compiler.h"
 #include "dirs.h"
 #include "dynamic-string.h"
-#include "netlink.h"
 #include "nx-match.h"
 #include "odp-util.h"
+#include "ofp-actions.h"
 #include "ofp-errors.h"
+#include "ofp-msgs.h"
 #include "ofp-parse.h"
 #include "ofp-print.h"
 #include "ofp-util.h"
@@ -55,6 +57,8 @@
 #include "util.h"
 #include "vconn.h"
 #include "vlog.h"
+#include "meta-flow.h"
+#include "sort.h"
 
 VLOG_DEFINE_THIS_MODULE(ofctl);
 
@@ -83,11 +87,24 @@ static int verbosity;
  * "snoop" command? */
 static bool timestamp;
 
+/* --sort, --rsort: Sort order. */
+enum sort_order { SORT_ASC, SORT_DESC };
+struct sort_criterion {
+    const struct mf_field *field; /* NULL means to sort by priority. */
+    enum sort_order order;
+};
+static struct sort_criterion *criteria;
+static size_t n_criteria, allocated_criteria;
+
 static const struct command all_commands[];
 
 static void usage(void) NO_RETURN;
 static void parse_options(int argc, char *argv[]);
 
+static bool recv_flow_stats_reply(struct vconn *, ovs_be32 send_xid,
+                                  struct ofpbuf **replyp,
+                                  struct ofputil_flow_stats *,
+                                  struct ofpbuf *ofpacts);
 int
 main(int argc, char *argv[])
 {
@@ -98,6 +115,27 @@ main(int argc, char *argv[])
     return 0;
 }
 
+static void
+add_sort_criterion(enum sort_order order, const char *field)
+{
+    struct sort_criterion *sc;
+
+    if (n_criteria >= allocated_criteria) {
+        criteria = x2nrealloc(criteria, &allocated_criteria, sizeof *criteria);
+    }
+
+    sc = &criteria[n_criteria++];
+    if (!field || !strcasecmp(field, "priority")) {
+        sc->field = NULL;
+    } else {
+        sc->field = mf_from_name(field);
+        if (!sc->field) {
+            ovs_fatal(0, "%s: unknown field name", field);
+        }
+    }
+    sc->order = order;
+}
+
 static void
 parse_options(int argc, char *argv[])
 {
@@ -105,6 +143,8 @@ parse_options(int argc, char *argv[])
         OPT_STRICT = UCHAR_MAX + 1,
         OPT_READD,
         OPT_TIMESTAMP,
+        OPT_SORT,
+        OPT_RSORT,
         DAEMON_OPTION_ENUMS,
         VLOG_OPTION_ENUMS
     };
@@ -116,6 +156,8 @@ parse_options(int argc, char *argv[])
         {"packet-in-format", required_argument, NULL, 'P'},
         {"more", no_argument, NULL, 'm'},
         {"timestamp", no_argument, NULL, OPT_TIMESTAMP},
+        {"sort", optional_argument, NULL, OPT_SORT},
+        {"rsort", optional_argument, NULL, OPT_RSORT},
         {"help", no_argument, NULL, 'h'},
         {"version", no_argument, NULL, 'V'},
         DAEMON_LONG_OPTIONS,
@@ -183,6 +225,14 @@ parse_options(int argc, char *argv[])
             timestamp = true;
             break;
 
+        case OPT_SORT:
+            add_sort_criterion(SORT_ASC, optarg);
+            break;
+
+        case OPT_RSORT:
+            add_sort_criterion(SORT_DESC, optarg);
+            break;
+
         DAEMON_OPTION_HANDLERS
         VLOG_OPTION_HANDLERS
         STREAM_SSL_OPTION_HANDLERS
@@ -194,6 +244,12 @@ parse_options(int argc, char *argv[])
             abort();
         }
     }
+
+    if (n_criteria) {
+        /* Always do a final sort pass based on priority. */
+        add_sort_criterion(SORT_DESC, "priority");
+    }
+
     free(short_options);
 }
 
@@ -210,6 +266,7 @@ usage(void)
            "  get-frags SWITCH            print fragment handling behavior\n"
            "  set-frags SWITCH FRAG_MODE  set fragment handling behavior\n"
            "  dump-ports SWITCH [PORT]    print port statistics\n"
+           "  dump-ports-desc SWITCH      print port descriptions\n"
            "  dump-flows SWITCH           print all flow entries\n"
            "  dump-flows SWITCH FLOW      print matching FLOWs\n"
            "  dump-aggregate SWITCH       print aggregate flow statistics\n"
@@ -223,7 +280,7 @@ usage(void)
            "  diff-flows SOURCE1 SOURCE2  compare flows from two sources\n"
            "  packet-out SWITCH IN_PORT ACTIONS PACKET...\n"
            "                              execute ACTIONS on PACKET\n"
-           "  monitor SWITCH [MISSLEN] [invalid_ttl]\n"
+           "  monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n"
            "                              print packets received from SWITCH\n"
            "  snoop SWITCH                snoop on SWITCH and its controller\n"
            "\nFor OpenFlow switches and controllers:\n"
@@ -243,6 +300,8 @@ usage(void)
            "  -m, --more                  be more verbose printing OpenFlow\n"
            "  --timestamp                 (monitor, snoop) print timestamps\n"
            "  -t, --timeout=SECS          give up after SECS seconds\n"
+           "  --sort[=field]              sort in ascending order\n"
+           "  --rsort[=field]             sort in descending order\n"
            "  -h, --help                  display this help message\n"
            "  -V, --version               display version information\n");
     exit(EXIT_SUCCESS);
@@ -335,21 +394,10 @@ open_vconn(const char *name, struct vconn **vconnp)
     return open_vconn__(name, "mgmt", vconnp);
 }
 
-static void *
-alloc_stats_request(size_t rq_len, uint16_t type, struct ofpbuf **bufferp)
-{
-    struct ofp_stats_msg *rq;
-
-    rq = make_openflow(rq_len, OFPT10_STATS_REQUEST, bufferp);
-    rq->type = htons(type);
-    rq->flags = htons(0);
-    return rq;
-}
-
 static void
 send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer)
 {
-    update_openflow_length(buffer);
+    ofpmsg_update_length(buffer);
     run(vconn_send_block(vconn, buffer), "failed to send packet to switch");
 }
 
@@ -359,7 +407,7 @@ dump_transaction(const char *vconn_name, struct ofpbuf *request)
     struct vconn *vconn;
     struct ofpbuf *reply;
 
-    update_openflow_length(request);
+    ofpmsg_update_length(request);
     open_vconn(vconn_name, &vconn);
     run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
     ofp_print(stdout, reply->data, reply->size, verbosity + 1);
@@ -368,21 +416,26 @@ dump_transaction(const char *vconn_name, struct ofpbuf *request)
 }
 
 static void
-dump_trivial_transaction(const char *vconn_name, uint8_t request_type)
+dump_trivial_transaction(const char *vconn_name, enum ofpraw raw)
 {
     struct ofpbuf *request;
-    make_openflow(sizeof(struct ofp_header), request_type, &request);
+    request = ofpraw_alloc(raw, OFP10_VERSION, 0);
     dump_transaction(vconn_name, request);
 }
 
 static void
-dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
+dump_stats_transaction(struct vconn *vconn, struct ofpbuf *request)
 {
-    ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid;
-    struct vconn *vconn;
+    const struct ofp_header *request_oh = request->data;
+    ovs_be32 send_xid = request_oh->xid;
+    enum ofpraw request_raw;
+    enum ofpraw reply_raw;
     bool done = false;
 
-    open_vconn(vconn_name, &vconn);
+    ofpraw_decode_partial(&request_raw, request->data, request->size);
+    reply_raw = ofpraw_stats_request_to_reply(request_raw,
+                                              request_oh->version);
+
     send_openflow_buffer(vconn, request);
     while (!done) {
         ovs_be32 recv_xid;
@@ -391,27 +444,38 @@ dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
         run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
         recv_xid = ((struct ofp_header *) reply->data)->xid;
         if (send_xid == recv_xid) {
-            struct ofp_stats_msg *osm;
+            enum ofpraw raw;
 
             ofp_print(stdout, reply->data, reply->size, verbosity + 1);
 
-            osm = ofpbuf_at(reply, 0, sizeof *osm);
-            done = !osm || !(ntohs(osm->flags) & OFPSF_REPLY_MORE);
+            ofpraw_decode(&raw, reply->data);
+            if (ofptype_from_ofpraw(raw) == OFPTYPE_ERROR) {
+                done = true;
+            } else if (raw == reply_raw) {
+                done = !ofpmp_more(reply->data);
+            } else {
+                ovs_fatal(0, "received bad reply: %s",
+                          ofp_to_string(reply->data, reply->size,
+                                        verbosity + 1));
+            }
         } else {
             VLOG_DBG("received reply with xid %08"PRIx32" "
                      "!= expected %08"PRIx32, recv_xid, send_xid);
         }
         ofpbuf_delete(reply);
     }
-    vconn_close(vconn);
 }
 
 static void
-dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
+dump_trivial_stats_transaction(const char *vconn_name, enum ofpraw raw)
 {
     struct ofpbuf *request;
-    alloc_stats_request(sizeof(struct ofp_stats_msg), stats_type, &request);
-    dump_stats_transaction(vconn_name, request);
+    struct vconn *vconn;
+
+    open_vconn(vconn_name, &vconn);
+    request = ofpraw_alloc(raw, vconn_get_version(vconn), 0);
+    dump_stats_transaction(vconn, request);
+    vconn_close(vconn);
 }
 
 /* Sends 'request', which should be a request that only has a reply if an error
@@ -425,7 +489,7 @@ transact_multiple_noreply(struct vconn *vconn, struct list *requests)
     struct ofpbuf *request, *reply;
 
     LIST_FOR_EACH (request, list_node, requests) {
-        update_openflow_length(request);
+        ofpmsg_update_length(request);
     }
 
     run(vconn_transact_multiple_noreply(vconn, requests, &reply),
@@ -456,107 +520,220 @@ static void
 fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
 {
     struct ofp_switch_config *config;
-    struct ofp_header *header;
     struct ofpbuf *request;
     struct ofpbuf *reply;
+    enum ofptype type;
 
-    make_openflow(sizeof(struct ofp_header), OFPT_GET_CONFIG_REQUEST,
-                  &request);
+    request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST, OFP10_VERSION, 0);
     run(vconn_transact(vconn, request, &reply),
         "talking to %s", vconn_get_name(vconn));
 
-    header = reply->data;
-    if (header->type != OFPT_GET_CONFIG_REPLY ||
-        header->length != htons(sizeof *config)) {
+    if (ofptype_pull(&type, reply) || type != OFPTYPE_GET_CONFIG_REPLY) {
         ovs_fatal(0, "%s: bad reply to config request", vconn_get_name(vconn));
     }
 
-    config = reply->data;
+    config = ofpbuf_pull(reply, sizeof *config);
     *config_ = *config;
 
     ofpbuf_delete(reply);
 }
 
 static void
-set_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
+set_switch_config(struct vconn *vconn, const struct ofp_switch_config *config)
 {
-    struct ofp_switch_config *config;
-    struct ofp_header save_header;
     struct ofpbuf *request;
 
-    config = make_openflow(sizeof *config, OFPT_SET_CONFIG, &request);
-    save_header = config->header;
-    *config = *config_;
-    config->header = save_header;
+    request = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, OFP10_VERSION, 0);
+    ofpbuf_put(request, config, sizeof *config);
 
     transact_noreply(vconn, request);
 }
 
 static void
-do_show(int argc OVS_UNUSED, char *argv[])
+ofctl_show(int argc OVS_UNUSED, char *argv[])
 {
-    dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
-    dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
+    const char *vconn_name = argv[1];
+    struct vconn *vconn;
+    struct ofpbuf *request;
+    struct ofpbuf *reply;
+    bool trunc;
+
+    request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, OFP10_VERSION, 0);
+    open_vconn(vconn_name, &vconn);
+    run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
+
+    trunc = ofputil_switch_features_ports_trunc(reply);
+    ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+
+    ofpbuf_delete(reply);
+    vconn_close(vconn);
+
+    if (trunc) {
+        /* The Features Reply may not contain all the ports, so send a
+         * Port Description stats request, which doesn't have size
+         * constraints. */
+        dump_trivial_stats_transaction(vconn_name,
+                                       OFPRAW_OFPST_PORT_DESC_REQUEST);
+    }
+    dump_trivial_transaction(vconn_name, OFPRAW_OFPT_GET_CONFIG_REQUEST);
 }
 
 static void
-do_dump_desc(int argc OVS_UNUSED, char *argv[])
+ofctl_dump_desc(int argc OVS_UNUSED, char *argv[])
 {
-    dump_trivial_stats_transaction(argv[1], OFPST_DESC);
+    dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_DESC_REQUEST);
 }
 
 static void
-do_dump_tables(int argc OVS_UNUSED, char *argv[])
+ofctl_dump_tables(int argc OVS_UNUSED, char *argv[])
 {
-    dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
+    dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_TABLE_REQUEST);
 }
 
-/* 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
- * '*oppp'. */
-static void
-fetch_ofputil_phy_port(const char *vconn_name, const char *port_name,
-                       struct ofputil_phy_port *pp)
+static bool
+fetch_port_by_features(const char *vconn_name,
+                       const char *port_name, unsigned int port_no,
+                       struct ofputil_phy_port *pp, bool *trunc)
 {
     struct ofputil_switch_features features;
-    const struct ofp_switch_features *osf;
+    const struct ofp_header *oh;
     struct ofpbuf *request, *reply;
-    unsigned int port_no;
     struct vconn *vconn;
     enum ofperr error;
+    enum ofptype type;
     struct ofpbuf b;
-
-    /* Try to interpret the argument as a port number. */
-    if (!str_to_uint(port_name, 10, &port_no)) {
-        port_no = UINT_MAX;
-    }
+    bool found = false;
 
     /* Fetch the switch's ofp_switch_features. */
-    make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request);
+    request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, OFP10_VERSION, 0);
     open_vconn(vconn_name, &vconn);
     run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
+    vconn_close(vconn);
+
+    oh = reply->data;
+    if (ofptype_decode(&type, reply->data)
+        || type != OFPTYPE_FEATURES_REPLY) {
+        ovs_fatal(0, "%s: received bad features reply", vconn_name);
+    }
 
-    osf = reply->data;
-    if (reply->size < sizeof *osf) {
-        ovs_fatal(0, "%s: received too-short features reply (only %zu bytes)",
-                  vconn_name, reply->size);
+    *trunc = false;
+    if (ofputil_switch_features_ports_trunc(reply)) {
+        *trunc = true;
+        goto exit;
     }
-    error = ofputil_decode_switch_features(osf, &features, &b);
+
+    error = ofputil_decode_switch_features(oh, &features, &b);
     if (error) {
         ovs_fatal(0, "%s: failed to decode features reply (%s)",
                   vconn_name, ofperr_to_string(error));
     }
 
-    while (!ofputil_pull_switch_features_port(&b, pp)) {
+    while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
         if (port_no != UINT_MAX
             ? port_no == pp->port_no
             : !strcmp(pp->name, port_name)) {
-            ofpbuf_delete(reply);
-            vconn_close(vconn);
-            return;
+            found = true;
+            goto exit;
+        }
+    }
+
+exit:
+    ofpbuf_delete(reply);
+    return found;
+}
+
+static bool
+fetch_port_by_stats(const char *vconn_name,
+                    const char *port_name, unsigned int port_no,
+                    struct ofputil_phy_port *pp)
+{
+    struct ofpbuf *request;
+    struct vconn *vconn;
+    ovs_be32 send_xid;
+    bool done = false;
+    bool found = false;
+
+    request = ofpraw_alloc(OFPRAW_OFPST_PORT_DESC_REQUEST, OFP10_VERSION, 0);
+    send_xid = ((struct ofp_header *) request->data)->xid;
+
+    open_vconn(vconn_name, &vconn);
+    send_openflow_buffer(vconn, request);
+    while (!done) {
+        ovs_be32 recv_xid;
+        struct ofpbuf *reply;
+
+        run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
+        recv_xid = ((struct ofp_header *) reply->data)->xid;
+        if (send_xid == recv_xid) {
+            struct ofp_header *oh = reply->data;
+            enum ofptype type;
+            struct ofpbuf b;
+            uint16_t flags;
+
+            ofpbuf_use_const(&b, oh, ntohs(oh->length));
+            if (ofptype_pull(&type, &b)
+                || type != OFPTYPE_PORT_DESC_STATS_REPLY) {
+                ovs_fatal(0, "received bad reply: %s",
+                          ofp_to_string(reply->data, reply->size,
+                                        verbosity + 1));
+            }
+
+            flags = ofpmp_flags(oh);
+            done = !(flags & OFPSF_REPLY_MORE);
+
+            if (found) {
+                /* We've already found the port, but we need to drain
+                 * the queue of any other replies for this request. */
+                continue;
+            }
+
+            while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
+                if (port_no != UINT_MAX ? port_no == pp->port_no
+                                        : !strcmp(pp->name, port_name)) {
+                    found = true;
+                    break;
+                }
+            }
+        } else {
+            VLOG_DBG("received reply with xid %08"PRIx32" "
+                     "!= expected %08"PRIx32, recv_xid, send_xid);
         }
+        ofpbuf_delete(reply);
+    }
+    vconn_close(vconn);
+
+    return found;
+}
+
+
+/* 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
+ * '*pp'. */
+static void
+fetch_ofputil_phy_port(const char *vconn_name, const char *port_name,
+                       struct ofputil_phy_port *pp)
+{
+    unsigned int 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;
+    }
+
+    /* Try to find the port based on the Features Reply.  If it looks
+     * like the results may be truncated, then use the Port Description
+     * stats message introduced in OVS 1.7. */
+    found = fetch_port_by_features(vconn_name, port_name, port_no, pp,
+                                   &trunc);
+    if (trunc) {
+        found = fetch_port_by_stats(vconn_name, port_name, port_no, pp);
+    }
+
+    if (!found) {
+        ovs_fatal(0, "%s: couldn't find port `%s'", vconn_name, port_name);
     }
-    ovs_fatal(0, "%s: couldn't find port `%s'", vconn_name, port_name);
 }
 
 /* Returns the port number corresponding to 'port_name' (which may be a port
@@ -631,12 +808,12 @@ set_protocol_for_flow_dump(struct vconn *vconn,
     }
 }
 
-static void
-do_dump_flows__(int argc, char *argv[], bool aggregate)
+static struct vconn *
+prepare_dump_flows(int argc, char *argv[], bool aggregate,
+                   struct ofpbuf **requestp)
 {
     enum ofputil_protocol usable_protocols, protocol;
     struct ofputil_flow_stats_request fsr;
-    struct ofpbuf *request;
     struct vconn *vconn;
 
     parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "");
@@ -644,30 +821,140 @@ do_dump_flows__(int argc, char *argv[], bool aggregate)
 
     protocol = open_vconn(argv[1], &vconn);
     protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
-    request = ofputil_encode_flow_stats_request(&fsr, protocol);
-    dump_stats_transaction(argv[1], request);
+    *requestp = ofputil_encode_flow_stats_request(&fsr, protocol);
+    return vconn;
+}
+
+static void
+ofctl_dump_flows__(int argc, char *argv[], bool aggregate)
+{
+    struct ofpbuf *request;
+    struct vconn *vconn;
+
+    vconn = prepare_dump_flows(argc, argv, aggregate, &request);
+    dump_stats_transaction(vconn, request);
     vconn_close(vconn);
 }
 
+static int
+compare_flows(const void *afs_, const void *bfs_)
+{
+    const struct ofputil_flow_stats *afs = afs_;
+    const struct ofputil_flow_stats *bfs = bfs_;
+    const struct cls_rule *a = &afs->rule;
+    const struct cls_rule *b = &bfs->rule;
+    const struct sort_criterion *sc;
+
+    for (sc = criteria; sc < &criteria[n_criteria]; sc++) {
+        const struct mf_field *f = sc->field;
+        int ret;
+
+        if (!f) {
+            ret = a->priority < b->priority ? -1 : a->priority > b->priority;
+        } else {
+            bool ina, inb;
+
+            ina = mf_are_prereqs_ok(f, &a->flow) && !mf_is_all_wild(f, &a->wc);
+            inb = mf_are_prereqs_ok(f, &b->flow) && !mf_is_all_wild(f, &b->wc);
+            if (ina != inb) {
+                /* Skip the test for sc->order, so that missing fields always
+                 * sort to the end whether we're sorting in ascending or
+                 * descending order. */
+                return ina ? -1 : 1;
+            } else {
+                union mf_value aval, bval;
+
+                mf_get_value(f, &a->flow, &aval);
+                mf_get_value(f, &b->flow, &bval);
+                ret = memcmp(&aval, &bval, f->n_bytes);
+            }
+        }
+
+        if (ret) {
+            return sc->order == SORT_ASC ? ret : -ret;
+        }
+    }
+
+    return 0;
+}
+
 static void
-do_dump_flows(int argc, char *argv[])
+ofctl_dump_flows(int argc, char *argv[])
 {
-    return do_dump_flows__(argc, argv, false);
+    if (!n_criteria) {
+        return ofctl_dump_flows__(argc, argv, false);
+    } else {
+        struct ofputil_flow_stats *fses;
+        size_t n_fses, allocated_fses;
+        struct ofpbuf *request;
+        struct ofpbuf ofpacts;
+        struct ofpbuf *reply;
+        struct vconn *vconn;
+        ovs_be32 send_xid;
+        struct ds s;
+        size_t i;
+
+        vconn = prepare_dump_flows(argc, argv, false, &request);
+        send_xid = ((struct ofp_header *) request->data)->xid;
+        send_openflow_buffer(vconn, request);
+
+        fses = NULL;
+        n_fses = allocated_fses = 0;
+        reply = NULL;
+        ofpbuf_init(&ofpacts, 0);
+        for (;;) {
+            struct ofputil_flow_stats *fs;
+
+            if (n_fses >= allocated_fses) {
+                fses = x2nrealloc(fses, &allocated_fses, sizeof *fses);
+            }
+
+            fs = &fses[n_fses];
+            if (!recv_flow_stats_reply(vconn, send_xid, &reply, fs,
+                                       &ofpacts)) {
+                break;
+            }
+            fs->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len);
+            n_fses++;
+        }
+        ofpbuf_uninit(&ofpacts);
+
+        qsort(fses, n_fses, sizeof *fses, compare_flows);
+
+        ds_init(&s);
+        for (i = 0; i < n_fses; i++) {
+            ds_clear(&s);
+            ofp_print_flow_stats(&s, &fses[i]);
+            puts(ds_cstr(&s));
+        }
+        ds_destroy(&s);
+
+        for (i = 0; i < n_fses; i++) {
+            free(fses[i].ofpacts);
+        }
+        free(fses);
+
+        vconn_close(vconn);
+    }
 }
 
 static void
-do_dump_aggregate(int argc, char *argv[])
+ofctl_dump_aggregate(int argc, char *argv[])
 {
-    return do_dump_flows__(argc, argv, true);
+    return ofctl_dump_flows__(argc, argv, true);
 }
 
 static void
-do_queue_stats(int argc, char *argv[])
+ofctl_queue_stats(int argc, char *argv[])
 {
-    struct ofp_queue_stats_request *req;
+    struct ofp10_queue_stats_request *req;
     struct ofpbuf *request;
+    struct vconn *vconn;
 
-    req = alloc_stats_request(sizeof *req, OFPST_QUEUE, &request);
+    open_vconn(argv[1], &vconn);
+    request = ofpraw_alloc(OFPRAW_OFPST_QUEUE_REQUEST,
+                           vconn_get_version(vconn), 0);
+    req = ofpbuf_put_zeros(request, sizeof *req);
 
     if (argc > 2 && argv[2][0] && strcasecmp(argv[2], "all")) {
         req->port_no = htons(str_to_port_no(argv[1], argv[2]));
@@ -682,7 +969,8 @@ do_queue_stats(int argc, char *argv[])
 
     memset(req->pad, 0, sizeof req->pad);
 
-    dump_stats_transaction(argv[1], request);
+    dump_stats_transaction(vconn, request);
+    vconn_close(vconn);
 }
 
 static enum ofputil_protocol
@@ -727,7 +1015,8 @@ open_vconn_for_flow_mod(const char *remote,
 }
 
 static void
-do_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms)
+ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
+                 size_t n_fms)
 {
     enum ofputil_protocol protocol;
     struct vconn *vconn;
@@ -739,56 +1028,56 @@ do_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms)
         struct ofputil_flow_mod *fm = &fms[i];
 
         transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
-        free(fm->actions);
+        free(fm->ofpacts);
     }
     vconn_close(vconn);
 }
 
 static void
-do_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
+ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
 {
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
 
     parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
-    do_flow_mod__(argv[1], fms, n_fms);
+    ofctl_flow_mod__(argv[1], fms, n_fms);
     free(fms);
 }
 
 static void
-do_flow_mod(int argc, char *argv[], uint16_t command)
+ofctl_flow_mod(int argc, char *argv[], uint16_t command)
 {
     if (argc > 2 && !strcmp(argv[2], "-")) {
-        do_flow_mod_file(argc, argv, command);
+        ofctl_flow_mod_file(argc, argv, command);
     } else {
         struct ofputil_flow_mod fm;
         parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, false);
-        do_flow_mod__(argv[1], &fm, 1);
+        ofctl_flow_mod__(argv[1], &fm, 1);
     }
 }
 
 static void
-do_add_flow(int argc, char *argv[])
+ofctl_add_flow(int argc, char *argv[])
 {
-    do_flow_mod(argc, argv, OFPFC_ADD);
+    ofctl_flow_mod(argc, argv, OFPFC_ADD);
 }
 
 static void
-do_add_flows(int argc, char *argv[])
+ofctl_add_flows(int argc, char *argv[])
 {
-    do_flow_mod_file(argc, argv, OFPFC_ADD);
+    ofctl_flow_mod_file(argc, argv, OFPFC_ADD);
 }
 
 static void
-do_mod_flows(int argc, char *argv[])
+ofctl_mod_flows(int argc, char *argv[])
 {
-    do_flow_mod(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
+    ofctl_flow_mod(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
 }
 
 static void
-do_del_flows(int argc, char *argv[])
+ofctl_del_flows(int argc, char *argv[])
 {
-    do_flow_mod(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
+    ofctl_flow_mod(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
 }
 
 static void
@@ -925,7 +1214,7 @@ ofctl_barrier(struct unixctl_conn *conn, int argc OVS_UNUSED,
         return;
     }
 
-    msg = ofputil_encode_barrier_request();
+    msg = ofputil_encode_barrier_request(vconn_get_version(aux->vconn));
     error = vconn_send_block(aux->vconn, msg);
     if (error) {
         ofpbuf_delete(msg);
@@ -953,12 +1242,41 @@ ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED,
     unixctl_command_reply(conn, NULL);
 }
 
+static void
+ofctl_block(struct unixctl_conn *conn, int argc OVS_UNUSED,
+            const char *argv[] OVS_UNUSED, void *blocked_)
+{
+    bool *blocked = blocked_;
+
+    if (!*blocked) {
+        *blocked = true;
+        unixctl_command_reply(conn, NULL);
+    } else {
+        unixctl_command_reply(conn, "already blocking");
+    }
+}
+
+static void
+ofctl_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED,
+              const char *argv[] OVS_UNUSED, void *blocked_)
+{
+    bool *blocked = blocked_;
+
+    if (*blocked) {
+        *blocked = false;
+        unixctl_command_reply(conn, NULL);
+    } else {
+        unixctl_command_reply(conn, "already unblocked");
+    }
+}
+
 static void
 monitor_vconn(struct vconn *vconn)
 {
     struct barrier_aux barrier_aux = { vconn, NULL };
     struct unixctl_server *server;
     bool exiting = false;
+    bool blocked = false;
     int error;
 
     daemon_save_fd(STDERR_FILENO);
@@ -974,6 +1292,11 @@ monitor_vconn(struct vconn *vconn)
                              ofctl_barrier, &barrier_aux);
     unixctl_command_register("ofctl/set-output-file", "FILE", 1, 1,
                              ofctl_set_output_file, NULL);
+
+    unixctl_command_register("ofctl/block", "", 0, 0, ofctl_block, &blocked);
+    unixctl_command_register("ofctl/unblock", "", 0, 0, ofctl_unblock,
+                             &blocked);
+
     daemonize_complete();
 
     for (;;) {
@@ -982,8 +1305,8 @@ monitor_vconn(struct vconn *vconn)
 
         unixctl_server_run(server);
 
-        for (;;) {
-            uint8_t msg_type;
+        while (!blocked) {
+            enum ofptype type;
 
             retval = vconn_recv(vconn, &b);
             if (retval == EAGAIN) {
@@ -995,15 +1318,15 @@ monitor_vconn(struct vconn *vconn)
                 time_t now = time_wall();
                 char s[32];
 
-                strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S: ", localtime(&now));
+                strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S: ", gmtime(&now));
                 fputs(s, stderr);
             }
 
-            msg_type = ((const struct ofp_header *) b->data)->type;
+            ofptype_decode(&type, b->data);
             ofp_print(stderr, b->data, b->size, verbosity + 2);
             ofpbuf_delete(b);
 
-            if (barrier_aux.conn && msg_type == OFPT10_BARRIER_REPLY) {
+            if (barrier_aux.conn && type == OFPTYPE_BARRIER_REPLY) {
                 unixctl_command_reply(barrier_aux.conn, NULL);
                 barrier_aux.conn = NULL;
             }
@@ -1015,7 +1338,9 @@ monitor_vconn(struct vconn *vconn)
 
         vconn_run(vconn);
         vconn_run_wait(vconn);
-        vconn_recv_wait(vconn);
+        if (!blocked) {
+            vconn_recv_wait(vconn);
+        }
         unixctl_server_wait(server);
         poll_block();
     }
@@ -1024,23 +1349,37 @@ monitor_vconn(struct vconn *vconn)
 }
 
 static void
-do_monitor(int argc, char *argv[])
+ofctl_monitor(int argc, char *argv[])
 {
     struct vconn *vconn;
+    int i;
 
     open_vconn(argv[1], &vconn);
-    if (argc > 2) {
-        struct ofp_switch_config config;
+    for (i = 2; i < argc; i++) {
+        const char *arg = argv[i];
 
-        fetch_switch_config(vconn, &config);
-        config.miss_send_len = htons(atoi(argv[2]));
-        set_switch_config(vconn, &config);
-    }
-    if (argc > 3) {
-        if (!strcmp(argv[3], "invalid_ttl")) {
+        if (isdigit((unsigned char) *arg)) {
+            struct ofp_switch_config config;
+
+            fetch_switch_config(vconn, &config);
+            config.miss_send_len = htons(atoi(arg));
+            set_switch_config(vconn, &config);
+        } else if (!strcmp(arg, "invalid_ttl")) {
             monitor_set_invalid_ttl_to_controller(vconn);
+        } else if (!strncmp(arg, "watch:", 6)) {
+            struct ofputil_flow_monitor_request fmr;
+            struct ofpbuf *msg;
+
+            parse_flow_monitor_request(&fmr, arg + 6);
+
+            msg = ofpbuf_new(0);
+            ofputil_append_flow_monitor_request(&fmr, msg);
+            dump_stats_transaction(vconn, msg);
+        } else {
+            ovs_fatal(0, "%s: unsupported \"monitor\" argument", arg);
         }
     }
+
     if (preferred_packet_in_format >= 0) {
         set_packet_in_format(vconn, preferred_packet_in_format);
     } else {
@@ -1063,7 +1402,7 @@ do_monitor(int argc, char *argv[])
 }
 
 static void
-do_snoop(int argc OVS_UNUSED, char *argv[])
+ofctl_snoop(int argc OVS_UNUSED, char *argv[])
 {
     struct vconn *vconn;
 
@@ -1072,27 +1411,38 @@ do_snoop(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-do_dump_ports(int argc, char *argv[])
+ofctl_dump_ports(int argc, char *argv[])
 {
-    struct ofp_port_stats_request *req;
+    struct ofp10_port_stats_request *req;
     struct ofpbuf *request;
+    struct vconn *vconn;
     uint16_t port;
 
-    req = alloc_stats_request(sizeof *req, OFPST_PORT, &request);
+    open_vconn(argv[1], &vconn);
+    request = ofpraw_alloc(OFPRAW_OFPST_PORT_REQUEST,
+                           vconn_get_version(vconn), 0);
+    req = ofpbuf_put_zeros(request, sizeof *req);
     port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_NONE;
     req->port_no = htons(port);
-    dump_stats_transaction(argv[1], request);
+    dump_stats_transaction(vconn, request);
+    vconn_close(vconn);
 }
 
 static void
-do_probe(int argc OVS_UNUSED, char *argv[])
+ofctl_dump_ports_desc(int argc OVS_UNUSED, char *argv[])
+{
+    dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_PORT_DESC_REQUEST);
+}
+
+static void
+ofctl_probe(int argc OVS_UNUSED, char *argv[])
 {
     struct ofpbuf *request;
     struct vconn *vconn;
     struct ofpbuf *reply;
 
-    make_openflow(sizeof(struct ofp_header), OFPT_ECHO_REQUEST, &request);
     open_vconn(argv[1], &vconn);
+    request = make_echo_request(vconn_get_version(vconn));
     run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
     if (reply->size != sizeof(struct ofp_header)) {
         ovs_fatal(0, "reply does not match request");
@@ -1102,24 +1452,25 @@ do_probe(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-do_packet_out(int argc, char *argv[])
+ofctl_packet_out(int argc, char *argv[])
 {
+    enum ofputil_protocol protocol;
     struct ofputil_packet_out po;
-    struct ofpbuf actions;
+    struct ofpbuf ofpacts;
     struct vconn *vconn;
     int i;
 
-    ofpbuf_init(&actions, sizeof(union ofp_action));
-    parse_ofp_actions(argv[3], &actions);
+    ofpbuf_init(&ofpacts, 64);
+    parse_ofpacts(argv[3], &ofpacts);
 
     po.buffer_id = UINT32_MAX;
     po.in_port = (!strcasecmp(argv[2], "none") ? OFPP_NONE
                   : !strcasecmp(argv[2], "local") ? OFPP_LOCAL
                   : str_to_port_no(argv[1], argv[2]));
-    po.actions = actions.data;
-    po.n_actions = actions.size / sizeof(union ofp_action);
+    po.ofpacts = ofpacts.data;
+    po.ofpacts_len = ofpacts.size;
 
-    open_vconn(argv[1], &vconn);
+    protocol = open_vconn(argv[1], &vconn);
     for (i = 4; i < argc; i++) {
         struct ofpbuf *packet, *opo;
         const char *error_msg;
@@ -1131,21 +1482,40 @@ do_packet_out(int argc, char *argv[])
 
         po.packet = packet->data;
         po.packet_len = packet->size;
-        opo = ofputil_encode_packet_out(&po);
+        opo = ofputil_encode_packet_out(&po, protocol);
         transact_noreply(vconn, opo);
         ofpbuf_delete(packet);
     }
     vconn_close(vconn);
-    ofpbuf_uninit(&actions);
+    ofpbuf_uninit(&ofpacts);
 }
 
 static void
-do_mod_port(int argc OVS_UNUSED, char *argv[])
+ofctl_mod_port(int argc OVS_UNUSED, char *argv[])
 {
+    struct ofp_config_flag {
+        const char *name;             /* The flag's name. */
+        enum ofputil_port_config bit; /* Bit to turn on or off. */
+        bool on;                      /* Value to set the bit to. */
+    };
+    static const struct ofp_config_flag flags[] = {
+        { "up",          OFPUTIL_PC_PORT_DOWN,    false },
+        { "down",        OFPUTIL_PC_PORT_DOWN,    true  },
+        { "stp",         OFPUTIL_PC_NO_STP,       false },
+        { "receive",     OFPUTIL_PC_NO_RECV,      false },
+        { "receive-stp", OFPUTIL_PC_NO_RECV_STP,  false },
+        { "flood",       OFPUTIL_PC_NO_FLOOD,     false },
+        { "forward",     OFPUTIL_PC_NO_FWD,       false },
+        { "packet-in",   OFPUTIL_PC_NO_PACKET_IN, false },
+    };
+
+    const struct ofp_config_flag *flag;
     enum ofputil_protocol protocol;
     struct ofputil_port_mod pm;
     struct ofputil_phy_port pp;
     struct vconn *vconn;
+    const char *command;
+    bool not;
 
     fetch_ofputil_phy_port(argv[1], argv[2], &pp);
 
@@ -1155,32 +1525,33 @@ do_mod_port(int argc OVS_UNUSED, char *argv[])
     pm.mask = 0;
     pm.advertise = 0;
 
-    if (!strcasecmp(argv[3], "up")) {
-        pm.mask |= OFPUTIL_PC_PORT_DOWN;
-    } else if (!strcasecmp(argv[3], "down")) {
-        pm.mask |= OFPUTIL_PC_PORT_DOWN;
-        pm.config |= OFPUTIL_PC_PORT_DOWN;
-    } else if (!strcasecmp(argv[3], "flood")) {
-        pm.mask |= OFPUTIL_PC_NO_FLOOD;
-    } else if (!strcasecmp(argv[3], "noflood")) {
-        pm.mask |= OFPUTIL_PC_NO_FLOOD;
-        pm.config |= OFPUTIL_PC_NO_FLOOD;
-    } else if (!strcasecmp(argv[3], "forward")) {
-        pm.mask |= OFPUTIL_PC_NO_FWD;
-    } else if (!strcasecmp(argv[3], "noforward")) {
-        pm.mask |= OFPUTIL_PC_NO_FWD;
-        pm.config |= OFPUTIL_PC_NO_FWD;
+    if (!strncasecmp(argv[3], "no-", 3)) {
+        command = argv[3] + 3;
+        not = true;
+    } else if (!strncasecmp(argv[3], "no", 2)) {
+        command = argv[3] + 2;
+        not = true;
     } else {
-        ovs_fatal(0, "unknown mod-port command '%s'", argv[3]);
+        command = argv[3];
+        not = false;
+    }
+    for (flag = flags; flag < &flags[ARRAY_SIZE(flags)]; flag++) {
+        if (!strcasecmp(command, flag->name)) {
+            pm.mask = flag->bit;
+            pm.config = flag->on ^ not ? flag->bit : 0;
+            goto found;
+        }
     }
+    ovs_fatal(0, "unknown mod-port command '%s'", argv[3]);
 
+found:
     protocol = open_vconn(argv[1], &vconn);
     transact_noreply(vconn, ofputil_encode_port_mod(&pm, protocol));
     vconn_close(vconn);
 }
 
 static void
-do_get_frags(int argc OVS_UNUSED, char *argv[])
+ofctl_get_frags(int argc OVS_UNUSED, char *argv[])
 {
     struct ofp_switch_config config;
     struct vconn *vconn;
@@ -1192,7 +1563,7 @@ do_get_frags(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-do_set_frags(int argc OVS_UNUSED, char *argv[])
+ofctl_set_frags(int argc OVS_UNUSED, char *argv[])
 {
     struct ofp_switch_config config;
     enum ofp_config_flags mode;
@@ -1225,7 +1596,7 @@ do_set_frags(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-do_ping(int argc, char *argv[])
+ofctl_ping(int argc, char *argv[])
 {
     size_t max_payload = 65535 - sizeof(struct ofp_header);
     unsigned int payload;
@@ -1241,28 +1612,29 @@ do_ping(int argc, char *argv[])
     for (i = 0; i < 10; i++) {
         struct timeval start, end;
         struct ofpbuf *request, *reply;
-        struct ofp_header *rq_hdr, *rpy_hdr;
+        const struct ofp_header *rpy_hdr;
+        enum ofptype type;
 
-        rq_hdr = make_openflow(sizeof(struct ofp_header) + payload,
-                               OFPT_ECHO_REQUEST, &request);
-        random_bytes(rq_hdr + 1, payload);
+        request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+                               payload);
+        random_bytes(ofpbuf_put_uninit(request, payload), payload);
 
         xgettimeofday(&start);
         run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact");
         xgettimeofday(&end);
 
         rpy_hdr = reply->data;
-        if (reply->size != request->size
-            || memcmp(rpy_hdr + 1, rq_hdr + 1, payload)
-            || rpy_hdr->xid != rq_hdr->xid
-            || rpy_hdr->type != OFPT_ECHO_REPLY) {
+        if (ofptype_pull(&type, reply)
+            || type != OFPTYPE_ECHO_REPLY
+            || reply->size != payload
+            || memcmp(request->l3, reply->l3, payload)) {
             printf("Reply does not match request.  Request:\n");
             ofp_print(stdout, request, request->size, verbosity + 2);
             printf("Reply:\n");
             ofp_print(stdout, reply, reply->size, verbosity + 2);
         }
         printf("%zu bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
-               reply->size - sizeof *rpy_hdr, argv[1], ntohl(rpy_hdr->xid),
+               reply->size, argv[1], ntohl(rpy_hdr->xid),
                    (1000*(double)(end.tv_sec - start.tv_sec))
                    + (.001*(end.tv_usec - start.tv_usec)));
         ofpbuf_delete(request);
@@ -1272,7 +1644,7 @@ do_ping(int argc, char *argv[])
 }
 
 static void
-do_benchmark(int argc OVS_UNUSED, char *argv[])
+ofctl_benchmark(int argc OVS_UNUSED, char *argv[])
 {
     size_t max_payload = 65535 - sizeof(struct ofp_header);
     struct timeval start, end;
@@ -1297,10 +1669,10 @@ do_benchmark(int argc OVS_UNUSED, char *argv[])
     xgettimeofday(&start);
     for (i = 0; i < count; i++) {
         struct ofpbuf *request, *reply;
-        struct ofp_header *rq_hdr;
 
-        rq_hdr = make_openflow(message_size, OFPT_ECHO_REQUEST, &request);
-        memset(rq_hdr + 1, 0, payload_size);
+        request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+                               payload_size);
+        ofpbuf_put_zeros(request, payload_size);
         run(vconn_transact(vconn, request, &reply), "transact");
         ofpbuf_delete(reply);
     }
@@ -1315,7 +1687,7 @@ do_benchmark(int argc OVS_UNUSED, char *argv[])
 }
 
 static void
-do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     usage();
 }
@@ -1334,8 +1706,8 @@ struct fte_version {
     uint16_t idle_timeout;
     uint16_t hard_timeout;
     uint16_t flags;
-    union ofp_action *actions;
-    size_t n_actions;
+    struct ofpact *ofpacts;
+    size_t ofpacts_len;
 };
 
 /* Frees 'version' and the data that it owns. */
@@ -1343,7 +1715,7 @@ static void
 fte_version_free(struct fte_version *version)
 {
     if (version) {
-        free(version->actions);
+        free(version->ofpacts);
         free(version);
     }
 }
@@ -1358,32 +1730,37 @@ fte_version_equals(const struct fte_version *a, const struct fte_version *b)
     return (a->cookie == b->cookie
             && a->idle_timeout == b->idle_timeout
             && a->hard_timeout == b->hard_timeout
-            && a->n_actions == b->n_actions
-            && !memcmp(a->actions, b->actions,
-                       a->n_actions * sizeof *a->actions));
+            && ofpacts_equal(a->ofpacts, a->ofpacts_len,
+                             b->ofpacts, b->ofpacts_len));
 }
 
-/* Prints 'version' on stdout.  Expects the caller to have printed the rule
- * associated with the version. */
+/* Clears 's', then if 's' has a version 'index', formats 'fte' and version
+ * 'index' into 's', followed by a new-line. */
 static void
-fte_version_print(const struct fte_version *version)
+fte_version_format(const struct fte *fte, int index, struct ds *s)
 {
-    struct ds s;
+    const struct fte_version *version = fte->versions[index];
+
+    ds_clear(s);
+    if (!version) {
+        return;
+    }
 
+    cls_rule_format(&fte->rule, s);
     if (version->cookie != htonll(0)) {
-        printf(" cookie=0x%"PRIx64, ntohll(version->cookie));
+        ds_put_format(s, " cookie=0x%"PRIx64, ntohll(version->cookie));
     }
     if (version->idle_timeout != OFP_FLOW_PERMANENT) {
-        printf(" idle_timeout=%"PRIu16, version->idle_timeout);
+        ds_put_format(s, " idle_timeout=%"PRIu16, version->idle_timeout);
     }
     if (version->hard_timeout != OFP_FLOW_PERMANENT) {
-        printf(" hard_timeout=%"PRIu16, version->hard_timeout);
+        ds_put_format(s, " hard_timeout=%"PRIu16, version->hard_timeout);
     }
 
-    ds_init(&s);
-    ofp_print_actions(&s, version->actions, version->n_actions);
-    printf(" %s\n", ds_cstr(&s));
-    ds_destroy(&s);
+    ds_put_char(s, ' ');
+    ofpacts_format(version->ofpacts, version->ofpacts_len, s);
+
+    ds_put_char(s, '\n');
 }
 
 static struct fte *
@@ -1465,12 +1842,12 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
         parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
 
         version = xmalloc(sizeof *version);
-        version->cookie = fm.cookie;
+        version->cookie = fm.new_cookie;
         version->idle_timeout = fm.idle_timeout;
         version->hard_timeout = fm.hard_timeout;
-        version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF_EMERG);
-        version->actions = fm.actions;
-        version->n_actions = fm.n_actions;
+        version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF10_EMERG);
+        version->ofpacts = fm.ofpacts;
+        version->ofpacts_len = fm.ofpacts_len;
 
         usable_protocols &= ofputil_usable_protocols(&fm.cr);
 
@@ -1485,6 +1862,59 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
     return usable_protocols;
 }
 
+static bool
+recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
+                      struct ofpbuf **replyp,
+                      struct ofputil_flow_stats *fs, struct ofpbuf *ofpacts)
+{
+    struct ofpbuf *reply = *replyp;
+
+    for (;;) {
+        int retval;
+        bool more;
+
+        /* Get a flow stats reply message, if we don't already have one. */
+        if (!reply) {
+            enum ofptype type;
+            enum ofperr error;
+
+            do {
+                run(vconn_recv_block(vconn, &reply),
+                    "OpenFlow packet receive failed");
+            } while (((struct ofp_header *) reply->data)->xid != send_xid);
+
+            error = ofptype_decode(&type, reply->data);
+            if (error || type != OFPTYPE_FLOW_STATS_REPLY) {
+                ovs_fatal(0, "received bad reply: %s",
+                          ofp_to_string(reply->data, reply->size,
+                                        verbosity + 1));
+            }
+        }
+
+        /* Pull an individual flow stats reply out of the message. */
+        retval = ofputil_decode_flow_stats_reply(fs, reply, false, ofpacts);
+        switch (retval) {
+        case 0:
+            *replyp = reply;
+            return true;
+
+        case EOF:
+            more = ofpmp_more(reply->l2);
+            ofpbuf_delete(reply);
+            reply = NULL;
+            if (!more) {
+                *replyp = NULL;
+                return false;
+            }
+            break;
+
+        default:
+            ovs_fatal(0, "parse error in reply (%s)",
+                      ofperr_to_string(retval));
+        }
+    }
+}
+
 /* Reads the OpenFlow flow table from 'vconn', which has currently active flow
  * format 'protocol', and adds them as flow table entries in 'cls' for the
  * version with the specified 'index'. */
@@ -1494,9 +1924,11 @@ read_flows_from_switch(struct vconn *vconn,
                        struct classifier *cls, int index)
 {
     struct ofputil_flow_stats_request fsr;
+    struct ofputil_flow_stats fs;
     struct ofpbuf *request;
+    struct ofpbuf ofpacts;
+    struct ofpbuf *reply;
     ovs_be32 send_xid;
-    bool done;
 
     fsr.aggregate = false;
     cls_rule_init_catchall(&fsr.match, 0);
@@ -1507,62 +1939,22 @@ read_flows_from_switch(struct vconn *vconn,
     send_xid = ((struct ofp_header *) request->data)->xid;
     send_openflow_buffer(vconn, request);
 
-    done = false;
-    while (!done) {
-        ovs_be32 recv_xid;
-        struct ofpbuf *reply;
-
-        run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
-        recv_xid = ((struct ofp_header *) reply->data)->xid;
-        if (send_xid == recv_xid) {
-            const struct ofputil_msg_type *type;
-            const struct ofp_stats_msg *osm;
-            enum ofputil_msg_code code;
-
-            ofputil_decode_msg_type(reply->data, &type);
-            code = ofputil_msg_type_code(type);
-            if (code != OFPUTIL_OFPST_FLOW_REPLY &&
-                code != OFPUTIL_NXST_FLOW_REPLY) {
-                ovs_fatal(0, "received bad reply: %s",
-                          ofp_to_string(reply->data, reply->size,
-                                        verbosity + 1));
-            }
-
-            osm = reply->data;
-            if (!(osm->flags & htons(OFPSF_REPLY_MORE))) {
-                done = true;
-            }
-
-            for (;;) {
-                struct fte_version *version;
-                struct ofputil_flow_stats fs;
-                int retval;
-
-                retval = ofputil_decode_flow_stats_reply(&fs, reply, false);
-                if (retval) {
-                    if (retval != EOF) {
-                        ovs_fatal(0, "parse error in reply");
-                    }
-                    break;
-                }
+    reply = NULL;
+    ofpbuf_init(&ofpacts, 0);
+    while (recv_flow_stats_reply(vconn, send_xid, &reply, &fs, &ofpacts)) {
+        struct fte_version *version;
 
-                version = xmalloc(sizeof *version);
-                version->cookie = fs.cookie;
-                version->idle_timeout = fs.idle_timeout;
-                version->hard_timeout = fs.hard_timeout;
-                version->flags = 0;
-                version->n_actions = fs.n_actions;
-                version->actions = xmemdup(fs.actions,
-                                           fs.n_actions * sizeof *fs.actions);
+        version = xmalloc(sizeof *version);
+        version->cookie = fs.cookie;
+        version->idle_timeout = fs.idle_timeout;
+        version->hard_timeout = fs.hard_timeout;
+        version->flags = 0;
+        version->ofpacts_len = fs.ofpacts_len;
+        version->ofpacts = xmemdup(fs.ofpacts, fs.ofpacts_len);
 
-                fte_insert(cls, &fs.rule, version, index);
-            }
-        } else {
-            VLOG_DBG("received reply with xid %08"PRIx32" "
-                     "!= expected %08"PRIx32, recv_xid, send_xid);
-        }
-        ofpbuf_delete(reply);
+        fte_insert(cls, &fs.rule, version, index);
     }
+    ofpbuf_uninit(&ofpacts);
 }
 
 static void
@@ -1574,7 +1966,9 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
     struct ofpbuf *ofm;
 
     fm.cr = fte->rule;
-    fm.cookie = version->cookie;
+    fm.cookie = htonll(0);
+    fm.cookie_mask = htonll(0);
+    fm.new_cookie = version->cookie;
     fm.table_id = 0xff;
     fm.command = command;
     fm.idle_timeout = version->idle_timeout;
@@ -1584,11 +1978,11 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
     fm.flags = version->flags;
     if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
         command == OFPFC_MODIFY_STRICT) {
-        fm.actions = version->actions;
-        fm.n_actions = version->n_actions;
+        fm.ofpacts = version->ofpacts;
+        fm.ofpacts_len = version->ofpacts_len;
     } else {
-        fm.actions = NULL;
-        fm.n_actions = 0;
+        fm.ofpacts = NULL;
+        fm.ofpacts_len = 0;
     }
 
     ofm = ofputil_encode_flow_mod(&fm, protocol);
@@ -1596,7 +1990,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
 }
 
 static void
-do_replace_flows(int argc OVS_UNUSED, char *argv[])
+ofctl_replace_flows(int argc OVS_UNUSED, char *argv[])
 {
     enum { FILE_IDX = 0, SWITCH_IDX = 1 };
     enum ofputil_protocol usable_protocols, protocol;
@@ -1666,38 +2060,44 @@ read_flows_from_source(const char *source, struct classifier *cls, int index)
 }
 
 static void
-do_diff_flows(int argc OVS_UNUSED, char *argv[])
+ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
 {
     bool differences = false;
     struct cls_cursor cursor;
     struct classifier cls;
+    struct ds a_s, b_s;
     struct fte *fte;
 
     classifier_init(&cls);
     read_flows_from_source(argv[1], &cls, 0);
     read_flows_from_source(argv[2], &cls, 1);
 
+    ds_init(&a_s);
+    ds_init(&b_s);
+
     cls_cursor_init(&cursor, &cls, NULL);
     CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
         struct fte_version *a = fte->versions[0];
         struct fte_version *b = fte->versions[1];
 
         if (!a || !b || !fte_version_equals(a, b)) {
-            char *rule_s = cls_rule_to_string(&fte->rule);
-            if (a) {
-                printf("-%s", rule_s);
-                fte_version_print(a);
-            }
-            if (b) {
-                printf("+%s", rule_s);
-                fte_version_print(b);
+            fte_version_format(fte, 0, &a_s);
+            fte_version_format(fte, 1, &b_s);
+            if (strcmp(ds_cstr(&a_s), ds_cstr(&b_s))) {
+                if (a_s.length) {
+                    printf("-%s", ds_cstr(&a_s));
+                }
+                if (b_s.length) {
+                    printf("+%s", ds_cstr(&b_s));
+                }
+                differences = true;
             }
-            free(rule_s);
-
-            differences = true;
         }
     }
 
+    ds_destroy(&a_s);
+    ds_destroy(&b_s);
+
     fte_free_all(&cls);
 
     if (differences) {
@@ -1708,7 +2108,7 @@ do_diff_flows(int argc OVS_UNUSED, char *argv[])
 /* Undocumented commands for unit testing. */
 
 static void
-do_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
+ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
 {
     enum ofputil_protocol usable_protocols;
     enum ofputil_protocol protocol = 0;
@@ -1741,88 +2141,88 @@ do_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
         ofp_print(stdout, msg->data, msg->size, verbosity);
         ofpbuf_delete(msg);
 
-        free(fm->actions);
+        free(fm->ofpacts);
     }
 }
 
 /* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints
  * it back to stdout.  */
 static void
-do_parse_flow(int argc OVS_UNUSED, char *argv[])
+ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
 {
     struct ofputil_flow_mod fm;
 
     parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
-    do_parse_flows__(&fm, 1);
+    ofctl_parse_flows__(&fm, 1);
 }
 
 /* "parse-flows FILENAME": reads the named file as a sequence of flows (like
  * add-flows) and prints each of the flows back to stdout.  */
 static void
-do_parse_flows(int argc OVS_UNUSED, char *argv[])
+ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
 {
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
 
     parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
-    do_parse_flows__(fms, n_fms);
+    ofctl_parse_flows__(fms, n_fms);
     free(fms);
 }
 
-/* "parse-nx-match": reads a series of nx_match specifications as strings from
- * stdin, does some internal fussing with them, and then prints them back as
- * strings on stdout. */
 static void
-do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_parse_nxm__(bool oxm)
 {
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_line(&in, stdin)) {
+    while (!ds_get_test_line(&in, stdin)) {
         struct ofpbuf nx_match;
         struct cls_rule rule;
         ovs_be64 cookie, cookie_mask;
         enum ofperr error;
         int match_len;
-        char *s;
-
-        /* Delete comments, skip blank lines. */
-        s = ds_cstr(&in);
-        if (*s == '#') {
-            puts(s);
-            continue;
-        }
-        if (strchr(s, '#')) {
-            *strchr(s, '#') = '\0';
-        }
-        if (s[strspn(s, " ")] == '\0') {
-            putchar('\n');
-            continue;
-        }
 
         /* Convert string to nx_match. */
         ofpbuf_init(&nx_match, 0);
-        match_len = nx_match_from_string(ds_cstr(&in), &nx_match);
+        if (oxm) {
+            match_len = oxm_match_from_string(ds_cstr(&in), &nx_match);
+        } else {
+            match_len = nx_match_from_string(ds_cstr(&in), &nx_match);
+        }
 
         /* Convert nx_match to cls_rule. */
         if (strict) {
-            error = nx_pull_match(&nx_match, match_len, 0, &rule,
-                                  &cookie, &cookie_mask);
+            if (oxm) {
+                error = oxm_pull_match(&nx_match, 0, &rule);
+            } else {
+                error = nx_pull_match(&nx_match, match_len, 0, &rule,
+                                      &cookie, &cookie_mask);
+            }
         } else {
-            error = nx_pull_match_loose(&nx_match, match_len, 0, &rule,
-                                        &cookie, &cookie_mask);
+            if (oxm) {
+                error = oxm_pull_match_loose(&nx_match, 0, &rule);
+            } else {
+                error = nx_pull_match_loose(&nx_match, match_len, 0, &rule,
+                                            &cookie, &cookie_mask);
+            }
         }
 
+
         if (!error) {
             char *out;
 
             /* Convert cls_rule back to nx_match. */
             ofpbuf_uninit(&nx_match);
             ofpbuf_init(&nx_match, 0);
-            match_len = nx_put_match(&nx_match, &rule, cookie, cookie_mask);
+            if (oxm) {
+                match_len = oxm_put_match(&nx_match, &rule);
+                out = oxm_match_to_string(nx_match.data, match_len);
+            } else {
+                match_len = nx_put_match(&nx_match, &rule,
+                                         cookie, cookie_mask);
+                out = nx_match_to_string(nx_match.data, match_len);
+            }
 
-            /* Convert nx_match to string. */
-            out = nx_match_to_string(nx_match.data, match_len);
             puts(out);
             free(out);
         } else {
@@ -1835,10 +2235,456 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     ds_destroy(&in);
 }
 
+/* "parse-nxm": reads a series of NXM nx_match specifications as strings from
+ * stdin, does some internal fussing with them, and then prints them back as
+ * strings on stdout. */
+static void
+ofctl_parse_nxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    return ofctl_parse_nxm__(false);
+}
+
+/* "parse-oxm": reads a series of OXM nx_match specifications as strings from
+ * stdin, does some internal fussing with them, and then prints them back as
+ * strings on stdout. */
+static void
+ofctl_parse_oxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    return ofctl_parse_nxm__(true);
+}
+
+static void
+print_differences(const char *prefix,
+                  const void *a_, size_t a_len,
+                  const void *b_, size_t b_len)
+{
+    const uint8_t *a = a_;
+    const uint8_t *b = b_;
+    size_t i;
+
+    for (i = 0; i < MIN(a_len, b_len); i++) {
+        if (a[i] != b[i]) {
+            printf("%s%2zu: %02"PRIx8" -> %02"PRIx8"\n",
+                   prefix, i, a[i], b[i]);
+        }
+    }
+    for (i = a_len; i < b_len; i++) {
+        printf("%s%2zu: (none) -> %02"PRIx8"\n", prefix, i, b[i]);
+    }
+    for (i = b_len; i < a_len; i++) {
+        printf("%s%2zu: %02"PRIx8" -> (none)\n", prefix, i, a[i]);
+    }
+}
+
+/* "parse-ofp10-actions": reads a series of OpenFlow 1.0 action specifications
+ * as hex bytes from stdin, converts them to ofpacts, prints them as strings
+ * on stdout, and then converts them back to hex bytes and prints any
+ * differences from the input. */
+static void
+ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct ds in;
+
+    ds_init(&in);
+    while (!ds_get_preprocessed_line(&in, stdin)) {
+        struct ofpbuf of10_out;
+        struct ofpbuf of10_in;
+        struct ofpbuf ofpacts;
+        enum ofperr error;
+        size_t size;
+        struct ds s;
+
+        /* Parse hex bytes. */
+        ofpbuf_init(&of10_in, 0);
+        if (ofpbuf_put_hex(&of10_in, ds_cstr(&in), NULL)[0] != '\0') {
+            ovs_fatal(0, "Trailing garbage in hex data");
+        }
+
+        /* Convert to ofpacts. */
+        ofpbuf_init(&ofpacts, 0);
+        size = of10_in.size;
+        error = ofpacts_pull_openflow10(&of10_in, of10_in.size, &ofpacts);
+        if (error) {
+            printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
+            ofpbuf_uninit(&ofpacts);
+            ofpbuf_uninit(&of10_in);
+            continue;
+        }
+        ofpbuf_push_uninit(&of10_in, size);
+
+        /* Print cls_rule. */
+        ds_init(&s);
+        ofpacts_format(ofpacts.data, ofpacts.size, &s);
+        puts(ds_cstr(&s));
+        ds_destroy(&s);
+
+        /* Convert back to ofp10 actions and print differences from input. */
+        ofpbuf_init(&of10_out, 0);
+        ofpacts_put_openflow10(ofpacts.data, ofpacts.size, &of10_out);
+
+        print_differences("", of10_in.data, of10_in.size,
+                          of10_out.data, of10_out.size);
+        putchar('\n');
+
+        ofpbuf_uninit(&ofpacts);
+        ofpbuf_uninit(&of10_in);
+        ofpbuf_uninit(&of10_out);
+    }
+    ds_destroy(&in);
+}
+
+/* "parse-ofp10-match": reads a series of ofp10_match specifications as hex
+ * bytes from stdin, converts them to cls_rules, prints them as strings on
+ * stdout, and then converts them back to hex bytes and prints any differences
+ * from the input.
+ *
+ * The input hex bytes may contain "x"s to represent "don't-cares", bytes whose
+ * values are ignored in the input and will be set to zero when OVS converts
+ * them back to hex bytes.  ovs-ofctl actually sets "x"s to random bits when
+ * it does the conversion to hex, to ensure that in fact they are ignored. */
+static void
+ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct ds expout;
+    struct ds in;
+
+    ds_init(&in);
+    ds_init(&expout);
+    while (!ds_get_preprocessed_line(&in, stdin)) {
+        struct ofpbuf match_in, match_expout;
+        struct ofp10_match match_out;
+        struct ofp10_match match_normal;
+        struct cls_rule rule;
+        char *p;
+
+        /* Parse hex bytes to use for expected output. */
+        ds_clear(&expout);
+        ds_put_cstr(&expout, ds_cstr(&in));
+        for (p = ds_cstr(&expout); *p; p++) {
+            if (*p == 'x') {
+                *p = '0';
+            }
+        }
+        ofpbuf_init(&match_expout, 0);
+        if (ofpbuf_put_hex(&match_expout, ds_cstr(&expout), NULL)[0] != '\0') {
+            ovs_fatal(0, "Trailing garbage in hex data");
+        }
+        if (match_expout.size != sizeof(struct ofp10_match)) {
+            ovs_fatal(0, "Input is %zu bytes, expected %zu",
+                      match_expout.size, sizeof(struct ofp10_match));
+        }
+
+        /* Parse hex bytes for input. */
+        for (p = ds_cstr(&in); *p; p++) {
+            if (*p == 'x') {
+                *p = "0123456789abcdef"[random_uint32() & 0xf];
+            }
+        }
+        ofpbuf_init(&match_in, 0);
+        if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') {
+            ovs_fatal(0, "Trailing garbage in hex data");
+        }
+        if (match_in.size != sizeof(struct ofp10_match)) {
+            ovs_fatal(0, "Input is %zu bytes, expected %zu",
+                      match_in.size, sizeof(struct ofp10_match));
+        }
+
+        /* Convert to cls_rule and print. */
+        ofputil_cls_rule_from_ofp10_match(match_in.data, OFP_DEFAULT_PRIORITY,
+                                          &rule);
+        cls_rule_print(&rule);
+
+        /* Convert back to ofp10_match and print differences from input. */
+        ofputil_cls_rule_to_ofp10_match(&rule, &match_out);
+        print_differences("", match_expout.data, match_expout.size,
+                          &match_out, sizeof match_out);
+
+        /* Normalize, then convert and compare again. */
+        ofputil_normalize_rule(&rule);
+        ofputil_cls_rule_to_ofp10_match(&rule, &match_normal);
+        print_differences("normal: ", &match_out, sizeof match_out,
+                          &match_normal, sizeof match_normal);
+        putchar('\n');
+
+        ofpbuf_uninit(&match_in);
+        ofpbuf_uninit(&match_expout);
+    }
+    ds_destroy(&in);
+    ds_destroy(&expout);
+}
+
+/* "parse-ofp11-match": reads a series of ofp11_match specifications as hex
+ * bytes from stdin, converts them to cls_rules, prints them as strings on
+ * stdout, and then converts them back to hex bytes and prints any differences
+ * from the input. */
+static void
+ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct ds in;
+
+    ds_init(&in);
+    while (!ds_get_preprocessed_line(&in, stdin)) {
+        struct ofpbuf match_in;
+        struct ofp11_match match_out;
+        struct cls_rule rule;
+        enum ofperr error;
+
+        /* Parse hex bytes. */
+        ofpbuf_init(&match_in, 0);
+        if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') {
+            ovs_fatal(0, "Trailing garbage in hex data");
+        }
+        if (match_in.size != sizeof(struct ofp11_match)) {
+            ovs_fatal(0, "Input is %zu bytes, expected %zu",
+                      match_in.size, sizeof(struct ofp11_match));
+        }
+
+        /* Convert to cls_rule. */
+        error = ofputil_cls_rule_from_ofp11_match(match_in.data,
+                                                  OFP_DEFAULT_PRIORITY, &rule);
+        if (error) {
+            printf("bad ofp11_match: %s\n\n", ofperr_get_name(error));
+            ofpbuf_uninit(&match_in);
+            continue;
+        }
+
+        /* Print cls_rule. */
+        cls_rule_print(&rule);
+
+        /* Convert back to ofp11_match and print differences from input. */
+        ofputil_cls_rule_to_ofp11_match(&rule, &match_out);
+
+        print_differences("", match_in.data, match_in.size,
+                          &match_out, sizeof match_out);
+        putchar('\n');
+
+        ofpbuf_uninit(&match_in);
+    }
+    ds_destroy(&in);
+}
+
+/* "parse-ofp11-actions": reads a series of OpenFlow 1.1 action specifications
+ * as hex bytes from stdin, converts them to ofpacts, prints them as strings
+ * on stdout, and then converts them back to hex bytes and prints any
+ * differences from the input. */
+static void
+ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct ds in;
+
+    ds_init(&in);
+    while (!ds_get_preprocessed_line(&in, stdin)) {
+        struct ofpbuf of11_out;
+        struct ofpbuf of11_in;
+        struct ofpbuf ofpacts;
+        enum ofperr error;
+        size_t size;
+        struct ds s;
+
+        /* Parse hex bytes. */
+        ofpbuf_init(&of11_in, 0);
+        if (ofpbuf_put_hex(&of11_in, ds_cstr(&in), NULL)[0] != '\0') {
+            ovs_fatal(0, "Trailing garbage in hex data");
+        }
+
+        /* Convert to ofpacts. */
+        ofpbuf_init(&ofpacts, 0);
+        size = of11_in.size;
+        error = ofpacts_pull_openflow11_actions(&of11_in, of11_in.size,
+                                                &ofpacts);
+        if (error) {
+            printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
+            ofpbuf_uninit(&ofpacts);
+            ofpbuf_uninit(&of11_in);
+            continue;
+        }
+        ofpbuf_push_uninit(&of11_in, size);
+
+        /* Print cls_rule. */
+        ds_init(&s);
+        ofpacts_format(ofpacts.data, ofpacts.size, &s);
+        puts(ds_cstr(&s));
+        ds_destroy(&s);
+
+        /* Convert back to ofp11 actions and print differences from input. */
+        ofpbuf_init(&of11_out, 0);
+        ofpacts_put_openflow11_actions(ofpacts.data, ofpacts.size, &of11_out);
+
+        print_differences("", of11_in.data, of11_in.size,
+                          of11_out.data, of11_out.size);
+        putchar('\n');
+
+        ofpbuf_uninit(&ofpacts);
+        ofpbuf_uninit(&of11_in);
+        ofpbuf_uninit(&of11_out);
+    }
+    ds_destroy(&in);
+}
+
+/* "parse-ofp11-instructions": reads a series of OpenFlow 1.1 instruction
+ * specifications as hex bytes from stdin, converts them to ofpacts, prints
+ * them as strings on stdout, and then converts them back to hex bytes and
+ * prints any differences from the input. */
+static void
+ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    struct ds in;
+
+    ds_init(&in);
+    while (!ds_get_preprocessed_line(&in, stdin)) {
+        struct ofpbuf of11_out;
+        struct ofpbuf of11_in;
+        struct ofpbuf ofpacts;
+        enum ofperr error;
+        size_t size;
+        struct ds s;
+
+        /* Parse hex bytes. */
+        ofpbuf_init(&of11_in, 0);
+        if (ofpbuf_put_hex(&of11_in, ds_cstr(&in), NULL)[0] != '\0') {
+            ovs_fatal(0, "Trailing garbage in hex data");
+        }
+
+        /* Convert to ofpacts. */
+        ofpbuf_init(&ofpacts, 0);
+        size = of11_in.size;
+        error = ofpacts_pull_openflow11_instructions(&of11_in, of11_in.size,
+                                                     &ofpacts);
+        if (error) {
+            printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));
+            ofpbuf_uninit(&ofpacts);
+            ofpbuf_uninit(&of11_in);
+            continue;
+        }
+        ofpbuf_push_uninit(&of11_in, size);
+
+        /* Print cls_rule. */
+        ds_init(&s);
+        ofpacts_format(ofpacts.data, ofpacts.size, &s);
+        puts(ds_cstr(&s));
+        ds_destroy(&s);
+
+        /* Convert back to ofp11 instructions and print differences from
+         * input. */
+        ofpbuf_init(&of11_out, 0);
+        ofpacts_put_openflow11_instructions(ofpacts.data, ofpacts.size,
+                                            &of11_out);
+
+        print_differences("", of11_in.data, of11_in.size,
+                          of11_out.data, of11_out.size);
+        putchar('\n');
+
+        ofpbuf_uninit(&ofpacts);
+        ofpbuf_uninit(&of11_in);
+        ofpbuf_uninit(&of11_out);
+    }
+    ds_destroy(&in);
+}
+
+/* "check-vlan VLAN_TCI VLAN_TCI_MASK": converts the specified vlan_tci and
+ * mask values to and from various formats and prints the results. */
+static void
+ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
+{
+    struct cls_rule rule;
+
+    char *string_s;
+    struct ofputil_flow_mod fm;
+
+    struct ofpbuf nxm;
+    struct cls_rule nxm_rule;
+    int nxm_match_len;
+    char *nxm_s;
+
+    struct ofp10_match of10_match;
+    struct cls_rule of10_rule;
+
+    struct ofp11_match of11_match;
+    struct cls_rule of11_rule;
+
+    enum ofperr error;
+
+    cls_rule_init_catchall(&rule, OFP_DEFAULT_PRIORITY);
+    rule.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
+    rule.wc.vlan_tci_mask = htons(strtoul(argv[2], NULL, 16));
+
+    /* Convert to and from string. */
+    string_s = cls_rule_to_string(&rule);
+    printf("%s -> ", string_s);
+    fflush(stdout);
+    parse_ofp_str(&fm, -1, string_s, false);
+    printf("%04"PRIx16"/%04"PRIx16"\n",
+           ntohs(fm.cr.flow.vlan_tci),
+           ntohs(fm.cr.wc.vlan_tci_mask));
+    free(string_s);
+
+    /* Convert to and from NXM. */
+    ofpbuf_init(&nxm, 0);
+    nxm_match_len = nx_put_match(&nxm, &rule, htonll(0), htonll(0));
+    nxm_s = nx_match_to_string(nxm.data, nxm_match_len);
+    error = nx_pull_match(&nxm, nxm_match_len, 0, &nxm_rule, NULL, NULL);
+    printf("NXM: %s -> ", nxm_s);
+    if (error) {
+        printf("%s\n", ofperr_to_string(error));
+    } else {
+        printf("%04"PRIx16"/%04"PRIx16"\n",
+               ntohs(nxm_rule.flow.vlan_tci),
+               ntohs(nxm_rule.wc.vlan_tci_mask));
+    }
+    free(nxm_s);
+    ofpbuf_uninit(&nxm);
+
+    /* Convert to and from OXM. */
+    ofpbuf_init(&nxm, 0);
+    nxm_match_len = oxm_put_match(&nxm, &rule);
+    nxm_s = oxm_match_to_string(nxm.data, nxm_match_len);
+    error = oxm_pull_match(&nxm, 0, &nxm_rule);
+    printf("OXM: %s -> ", nxm_s);
+    if (error) {
+        printf("%s\n", ofperr_to_string(error));
+    } else {
+        uint16_t vid = ntohs(nxm_rule.flow.vlan_tci) &
+            (VLAN_VID_MASK | VLAN_CFI);
+        uint16_t mask = ntohs(nxm_rule.wc.vlan_tci_mask) &
+            (VLAN_VID_MASK | VLAN_CFI);
+
+        printf("%04"PRIx16"/%04"PRIx16",", vid, mask);
+        if (vid && vlan_tci_to_pcp(nxm_rule.wc.vlan_tci_mask)) {
+            printf("%02"PRIx8"\n", vlan_tci_to_pcp(nxm_rule.flow.vlan_tci));
+        } else {
+            printf("--\n");
+        }
+    }
+    free(nxm_s);
+    ofpbuf_uninit(&nxm);
+
+    /* Convert to and from OpenFlow 1.0. */
+    ofputil_cls_rule_to_ofp10_match(&rule, &of10_match);
+    ofputil_cls_rule_from_ofp10_match(&of10_match, 0, &of10_rule);
+    printf("OF1.0: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
+           ntohs(of10_match.dl_vlan),
+           (of10_match.wildcards & htonl(OFPFW10_DL_VLAN)) != 0,
+           of10_match.dl_vlan_pcp,
+           (of10_match.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0,
+           ntohs(of10_rule.flow.vlan_tci),
+           ntohs(of10_rule.wc.vlan_tci_mask));
+
+    /* Convert to and from OpenFlow 1.1. */
+    ofputil_cls_rule_to_ofp11_match(&rule, &of11_match);
+    ofputil_cls_rule_from_ofp11_match(&of11_match, 0, &of11_rule);
+    printf("OF1.1: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
+           ntohs(of11_match.dl_vlan),
+           (of11_match.wildcards & htonl(OFPFW11_DL_VLAN)) != 0,
+           of11_match.dl_vlan_pcp,
+           (of11_match.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0,
+           ntohs(of11_rule.flow.vlan_tci),
+           ntohs(of11_rule.wc.vlan_tci_mask));
+}
+
 /* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow
  * version. */
 static void
-do_print_error(int argc OVS_UNUSED, char *argv[])
+ofctl_print_error(int argc OVS_UNUSED, char *argv[])
 {
     enum ofperr error;
     int version;
@@ -1849,17 +2695,14 @@ do_print_error(int argc OVS_UNUSED, char *argv[])
     }
 
     for (version = 0; version <= UINT8_MAX; version++) {
-        const struct ofperr_domain *domain;
-
-        domain = ofperr_domain_from_version(version);
-        if (!domain) {
+        const char *name = ofperr_domain_get_name(version);
+        if (!name) {
             continue;
         }
-
         printf("%s: %d,%d\n",
-               ofperr_domain_get_name(domain),
-               ofperr_get_type(error, domain),
-               ofperr_get_code(error, domain));
+               ofperr_domain_get_name(version),
+               ofperr_get_type(error, version),
+               ofperr_get_code(error, version));
     }
 }
 
@@ -1867,7 +2710,7 @@ do_print_error(int argc OVS_UNUSED, char *argv[])
  * binary data, interpreting them as an OpenFlow message, and prints the
  * OpenFlow message on stdout, at VERBOSITY (level 2 by default).  */
 static void
-do_ofp_print(int argc, char *argv[])
+ofctl_ofp_print(int argc, char *argv[])
 {
     struct ofpbuf packet;
 
@@ -1880,36 +2723,45 @@ do_ofp_print(int argc, char *argv[])
 }
 
 static const struct command all_commands[] = {
-    { "show", 1, 1, do_show },
-    { "monitor", 1, 3, do_monitor },
-    { "snoop", 1, 1, do_snoop },
-    { "dump-desc", 1, 1, do_dump_desc },
-    { "dump-tables", 1, 1, do_dump_tables },
-    { "dump-flows", 1, 2, do_dump_flows },
-    { "dump-aggregate", 1, 2, do_dump_aggregate },
-    { "queue-stats", 1, 3, do_queue_stats },
-    { "add-flow", 2, 2, do_add_flow },
-    { "add-flows", 2, 2, do_add_flows },
-    { "mod-flows", 2, 2, do_mod_flows },
-    { "del-flows", 1, 2, do_del_flows },
-    { "replace-flows", 2, 2, do_replace_flows },
-    { "diff-flows", 2, 2, do_diff_flows },
-    { "packet-out", 4, INT_MAX, do_packet_out },
-    { "dump-ports", 1, 2, do_dump_ports },
-    { "mod-port", 3, 3, do_mod_port },
-    { "get-frags", 1, 1, do_get_frags },
-    { "set-frags", 2, 2, do_set_frags },
-    { "probe", 1, 1, do_probe },
-    { "ping", 1, 2, do_ping },
-    { "benchmark", 3, 3, do_benchmark },
-    { "help", 0, INT_MAX, do_help },
+    { "show", 1, 1, ofctl_show },
+    { "monitor", 1, 3, ofctl_monitor },
+    { "snoop", 1, 1, ofctl_snoop },
+    { "dump-desc", 1, 1, ofctl_dump_desc },
+    { "dump-tables", 1, 1, ofctl_dump_tables },
+    { "dump-flows", 1, 2, ofctl_dump_flows },
+    { "dump-aggregate", 1, 2, ofctl_dump_aggregate },
+    { "queue-stats", 1, 3, ofctl_queue_stats },
+    { "add-flow", 2, 2, ofctl_add_flow },
+    { "add-flows", 2, 2, ofctl_add_flows },
+    { "mod-flows", 2, 2, ofctl_mod_flows },
+    { "del-flows", 1, 2, ofctl_del_flows },
+    { "replace-flows", 2, 2, ofctl_replace_flows },
+    { "diff-flows", 2, 2, ofctl_diff_flows },
+    { "packet-out", 4, INT_MAX, ofctl_packet_out },
+    { "dump-ports", 1, 2, ofctl_dump_ports },
+    { "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
+    { "mod-port", 3, 3, ofctl_mod_port },
+    { "get-frags", 1, 1, ofctl_get_frags },
+    { "set-frags", 2, 2, ofctl_set_frags },
+    { "probe", 1, 1, ofctl_probe },
+    { "ping", 1, 2, ofctl_ping },
+    { "benchmark", 3, 3, ofctl_benchmark },
+    { "help", 0, INT_MAX, ofctl_help },
 
     /* Undocumented commands for testing. */
-    { "parse-flow", 1, 1, do_parse_flow },
-    { "parse-flows", 1, 1, do_parse_flows },
-    { "parse-nx-match", 0, 0, do_parse_nx_match },
-    { "print-error", 1, 1, do_print_error },
-    { "ofp-print", 1, 2, do_ofp_print },
+    { "parse-flow", 1, 1, ofctl_parse_flow },
+    { "parse-flows", 1, 1, ofctl_parse_flows },
+    { "parse-nx-match", 0, 0, ofctl_parse_nxm },
+    { "parse-nxm", 0, 0, ofctl_parse_nxm },
+    { "parse-oxm", 0, 0, ofctl_parse_oxm },
+    { "parse-ofp10-actions", 0, 0, ofctl_parse_ofp10_actions },
+    { "parse-ofp10-match", 0, 0, ofctl_parse_ofp10_match },
+    { "parse-ofp11-match", 0, 0, ofctl_parse_ofp11_match },
+    { "parse-ofp11-actions", 0, 0, ofctl_parse_ofp11_actions },
+    { "parse-ofp11-instructions", 0, 0, ofctl_parse_ofp11_instructions },
+    { "check-vlan", 2, 2, ofctl_check_vlan },
+    { "print-error", 1, 1, ofctl_print_error },
+    { "ofp-print", 1, 2, ofctl_ofp_print },
 
     { NULL, 0, 0, NULL },
 };
index 3f4cb16..72417e5 100755 (executable)
@@ -1,6 +1,6 @@
 #! @PERL@
 
-# Copyright (c) 2009, 2010 Nicira Networks.
+# 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.
index 8ab4756..5306480 100755 (executable)
@@ -1,6 +1,6 @@
 #! @PYTHON@
 #
-# Copyright (c) 2010 Nicira Networks.
+# Copyright (c) 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.
diff --git a/utilities/ovs-pki-cgi.in b/utilities/ovs-pki-cgi.in
deleted file mode 100755 (executable)
index c8c53d0..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#! @PERL@
-
-# Copyright (c) 2008, 2009 Nicira Networks.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at:
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-use CGI;
-use Digest::SHA1;
-use Fcntl;
-
-$CGI::POST_MAX = 65536;    # Limit POSTs to 64 kB.
-
-use strict;
-use warnings;
-
-my $pkidir = '@PKIDIR@';
-my $q = new CGI;
-
-die unless $q->request_method() eq 'POST';
-
-my $type = $q->param('type');
-die unless defined $type;
-die unless $type eq 'switch' or $type eq 'controller';
-
-my $req = $q->param('req');
-die unless defined $req;
-die unless $req =~ /^-----BEGIN CERTIFICATE REQUEST-----$/m;
-die unless $req =~ /^-----END CERTIFICATE REQUEST-----$/m;
-
-my $digest = Digest::SHA1::sha1_hex($req);
-my $incoming = "$pkidir/${type}ca/incoming";
-my $dst = "$incoming/$digest-req.pem";
-
-sysopen(REQUEST, "$dst.tmp", O_RDWR | O_CREAT | O_EXCL, 0600)
-  or die "sysopen $dst.tmp: $!";
-print REQUEST $req;
-close(REQUEST) or die "close $dst.tmp: $!";
-
-rename("$dst.tmp", $dst) or die "rename $dst.tmp to $dst: $!";
-
-print $q->header('text/html', '204 No response');
-
-# Local Variables:
-# mode: perl
-# End:
index e40fdee..e2aa5b4 100644 (file)
@@ -9,39 +9,27 @@
 ovs\-pki \- OpenFlow public key infrastructure management utility
 
 .SH SYNOPSIS
-\fBovs\-pki\fR [\fIOPTIONS\fR] \fICOMMAND\fR [\fIARGS\fR]
+Each command takes the form:
 .sp
-Stand\-alone commands with their arguments:
+\fBovs\-pki\fR [\fIoptions\fR] \fIcommand\fR [\fIargs\fR]
+.sp
+The implemented commands and their arguments are:
 .br
 \fBovs\-pki\fR \fBinit\fR
 .br
-\fBovs\-pki\fR \fBreq\fR \fINAME\fR
-.br
-\fBovs\-pki\fR \fBsign\fR \fINAME\fR [\fITYPE\fR]
-.br
-\fBovs\-pki\fR \fBreq+sign\fR \fINAME\fR [\fITYPE\fR]
-.br
-\fBovs\-pki\fR \fBverify\fR \fINAME\fR [\fITYPE\fR]
-.br
-\fBovs\-pki\fR \fBfingerprint\fR \fIFILE\fR
-.br
-\fBovs\-pki\fR \fBself\-sign\fR \fINAME\fR
-.sp
-The following additional commands manage an online PKI:
-.br
-\fBovs\-pki\fR \fBls\fR [\fIPREFIX\fR] [\fITYPE\fR]
+\fBovs\-pki\fR \fBreq\fR \fIname\fR
 .br
-\fBovs\-pki\fR \fBflush\fR [\fITYPE\fR]
+\fBovs\-pki\fR \fBsign\fR \fIname\fR [\fItype\fR]
 .br
-\fBovs\-pki\fR \fBreject\fR \fIPREFIX\fR [\fITYPE\fR]
+\fBovs\-pki\fR \fBreq+sign\fR \fIname\fR [\fItype\fR]
 .br
-\fBovs\-pki\fR \fBapprove\fR \fIPREFIX\fR [\fITYPE\fR]
+\fBovs\-pki\fR \fBverify\fR \fIname\fR [\fItype\fR]
 .br
-\fBovs\-pki\fR \fBprompt\fR [\fITYPE\fR]
+\fBovs\-pki\fR \fBfingerprint\fR \fIfile\fR
 .br
-\fBovs\-pki\fR \fBexpire\fR [\fIAGE\fR]
+\fBovs\-pki\fR \fBself\-sign\fR \fIname\fR
 .sp
-Each \fITYPE\fR above is a certificate type, either \fBswitch\fR
+Each \fItype\fR above is a certificate type, either \fBswitch\fR
 (default) or \fBcontroller\fR.
 .sp
 The available options are:
@@ -106,14 +94,14 @@ The files \fBpki/controllerca/private/cakey.pem\fR and
 contents that should not be exposed.
 
 .TP
-\fBreq\fR \fINAME\fR
-Generates a new private key named \fINAME\fR\fB\-privkey.pem\fR and
-corresponding certificate request named \fINAME\fR\fB\-req.pem\fR.
+\fBreq\fR \fIname\fR
+Generates a new private key named \fIname\fR\fB\-privkey.pem\fR and
+corresponding certificate request named \fIname\fR\fB\-req.pem\fR.
 The private key can be intended for use by a switch or a controller.
 
 This command should ideally be run on the switch or controller that
 will use the private key to identify itself.  The file
-\fINAME\fR\fB\-req.pem\fR must be copied to the CA machine for signing
+\fIname\fR\fB\-req.pem\fR must be copied to the CA machine for signing
 with the \fBsign\fR command (below).  
 
 This command will output a fingerprint to stdout as its final step.
@@ -133,14 +121,14 @@ hierarchy (but not to other files in that tree).  By default,
 the \fB\-D\fR or \fB\-\^\-dsaparam\fR option (see below) may be used to
 specify an alternate location.
 
-\fINAME\fR\fB\-privkey.pem\fR has sensitive contents that should not be
-exposed.  \fINAME\fR\fB\-req.pem\fR may be safely made public.
+\fIname\fR\fB\-privkey.pem\fR has sensitive contents that should not be
+exposed.  \fIname\fR\fB\-req.pem\fR may be safely made public.
 
 .TP
-\fBsign\fR \fINAME\fR [\fITYPE\fR]
-Signs the certificate request named \fINAME\fR\fB\-req.pem\fR that was
+\fBsign\fR \fIname\fR [\fItype\fR]
+Signs the certificate request named \fIname\fR\fB\-req.pem\fR that was
 produced in the previous step, producing a certificate named
-\fINAME\fR\fB\-cert.pem\fR.  \fITYPE\fR, either \fBswitch\fR (default) or
+\fIname\fR\fB\-cert.pem\fR.  \fItype\fR, either \fBswitch\fR (default) or
 \fBcontroller\fR, indicates the use for which the key is being
 certified.
 
@@ -152,17 +140,17 @@ command.  This ensures that the request being signed is the same one
 produced by \fBreq\fR.  (The \fB\-b\fR or \fB\-\^\-batch\fR option
 suppresses the verification step.)
 
-The file \fINAME\fR\fB\-cert.pem\fR will need to be copied back to the
+The file \fIname\fR\fB\-cert.pem\fR will need to be copied back to the
 switch or controller for which it is intended.  Its contents may
 safely be made public.
 
 .TP
-\fBreq+sign\fR \fINAME\fR [\fITYPE\fR]
+\fBreq+sign\fR \fIname\fR [\fItype\fR]
 Combines the \fBreq\fR and \fBsign\fR commands into a single step,
 outputting all the files produced by each.  The
-\fINAME\fR\fB\-privkey.pem\fR and \fINAME\fR\fB\-cert.pem\fR files must
+\fIname\fR\fB\-privkey.pem\fR and \fIname\fR\fB\-cert.pem\fR files must
 be copied securely to the switch or controller.
-\fINAME\fR\fB\-privkey.pem\fR has sensitive contents and must not be
+\fIname\fR\fB\-privkey.pem\fR has sensitive contents and must not be
 exposed in transit.  Afterward, it should be deleted from the CA
 machine.
 
@@ -172,108 +160,29 @@ because there is additional potential for exposure of the private
 key.  However, it is also more convenient.
 
 .TP
-\fBverify\fR \fINAME\fR [\fITYPE\fR]
-Verifies that \fINAME\fR\fB\-cert.pem\fR is a valid certificate for the
-given \fITYPE\fR of use, either \fBswitch\fR (default) or
+\fBverify\fR \fIname\fR [\fItype\fR]
+Verifies that \fIname\fR\fB\-cert.pem\fR is a valid certificate for the
+given \fItype\fR of use, either \fBswitch\fR (default) or
 \fBcontroller\fR.  If the certificate is valid for this use, it prints
-the message ``\fINAME\fR\fB\-cert.pem\fR: OK''; otherwise, it prints an
+the message ``\fIname\fR\fB\-cert.pem\fR: OK''; otherwise, it prints an
 error message.
 
 .TP
-\fBfingerprint\fR \fIFILE\fR
-Prints the fingerprint for \fIFILE\fR.  If \fIFILE\fR is a
+\fBfingerprint\fR \fIfile\fR
+Prints the fingerprint for \fIfile\fR.  If \fIfile\fR is a
 certificate, then this is the SHA\-1 digest of the DER encoded version
 of the certificate; otherwise, it is the SHA\-1 digest of the entire
 file.
 
 .TP
-\fBself\-sign\fR \fINAME\fR
-Signs the certificate request named \fINAME\fB\-req.pem\fR using the
-private key \fINAME\fB\-privkey.pem\fR, producing a self-signed
-certificate named \fINAME\fB\-cert.pem\fR.  The input files should have
+\fBself\-sign\fR \fIname\fR
+Signs the certificate request named \fIname\fB\-req.pem\fR using the
+private key \fIname\fB\-privkey.pem\fR, producing a self-signed
+certificate named \fIname\fB\-cert.pem\fR.  The input files should have
 been produced with \fBovs\-pki req\fR.
 
 Some controllers accept such self-signed certificates.
 
-.SH "ONLINE COMMANDS"
-
-An OpenFlow PKI can be administered online, in conjunction with
-.BR ovs\-pki\-cgi (8)
-and a web server such as Apache:
-
-.IP \(bu
-The web server exports the contents of the PKI via HTTP.  All files in
-a PKI hierarchy files may be made public, except for the files
-\fBpki/controllerca/private/cakey.pem\fR and
-\fBpki/switchca/private/cakey.pem\fR, which must not be exposed.
-
-.IP \(bu
-\fBovs\-pki\-cgi\fR allows newly generated certificate requests for
-controllers and switches to be uploaded into the
-\fBpki/controllerca/incoming\fR and \fBpki/switchca/incoming\fR
-directories, respectively.  Uploaded certificate requests are stored
-in those directories under names of the form
-\fIFINGERPRINT\fB\-req.pem\fR, which \fIFINGERPRINT\fR is the SHA\-1
-hash of the file.
-
-.IP \(bu
-These \fBovs\-pki\fR commands allow incoming certificate requests to
-be approved or rejected, in a form are suitable for use by humans or
-other software.
-
-.PP
-The following \fBovs\-pki\fR commands support online administration:
-
-.TP
-\fBovs\-pki\fR \fBls\fR [\fIPREFIX\fR] [\fITYPE\fR]
-Lists all of the incoming certificate requests of the given \fITYPE\fR
-(either \fBswitch\fR, the default, or \fBcontroller\fR).  If
-\fIPREFIX\fR, which must be at least 4 characters long, is specified,
-it causes the list to be limited to files whose names begin with
-\fIPREFIX\fR.  This is useful, for example, to avoid typing in an
-entire fingerprint when checking that a specific certificate request
-has been received.
-
-.TP
-\fBovs\-pki\fR \fBflush\fR [\fITYPE\fR]
-Deletes all certificate requests of the given \fITYPE\fR.
-
-.TP
-\fBovs\-pki\fR \fBreject\fR \fIPREFIX\fR [\fITYPE\fR]
-Rejects the certificate request whose name begins with \fIPREFIX\fR,
-which must be at least 4 characters long, of the given type (either
-\fBswitch\fR, the default, or \fBcontroller\fR).  \fIPREFIX\fR must
-match exactly one certificate request; its purpose is to allow the
-user to type fewer characters, not to match multiple certificate
-requests.
-
-.TP
-\fBovs\-pki\fR \fBapprove\fR \fIPREFIX\fR [\fITYPE\fR]
-Approves the certificate request whose name begins with \fIPREFIX\fR,
-which must be at least 4 characters long, of the given \fITYPE\fR
-(either \fBswitch\fR, the default, or \fBcontroller\fR).  \fIPREFIX\fR
-must match exactly one certificate request; its purpose is to allow
-the user to type fewer characters, not to match multiple certificate
-requests.
-
-The command will output a fingerprint to stdout and request that you
-verify that it is correct.  (The \fB\-b\fR or \fB\-\^\-batch\fR option
-suppresses the verification step.)
-
-.TP
-\fBovs\-pki\fR \fBprompt\fR [\fITYPE\fR]
-Prompts the user for each incoming certificate request of the given
-\fITYPE\fR (either \fBswitch\fR, the default, or \fBcontroller\fR).
-Based on the certificate request's fingerprint, the user is given the
-option of approving, rejecting, or skipping the certificate request.
-
-.TP
-\fBovs\-pki\fR \fBexpire\fR [\fIAGE\fR]
-
-Rejects all the incoming certificate requests, of either type, that is
-older than \fIAGE\fR, which must in one of the forms \fIN\fBs\fR,
-\fIN\fBmin\fR, \fIN\fBh\fR, \fIN\fBday\fR.  The default is \fB1day\fR.
-
 .SH OPTIONS
 .IP "\fB\-k\fR \fItype\fR"
 .IQ "\fB\-\^\-key=\fItype\fR"
@@ -306,7 +215,7 @@ The default is \fBdsaparam.pem\fR under the PKI hierarchy.
 .IP "\fB\-b\fR"
 .IQ "\fB\-\^\-batch\fR"
 Suppresses the interactive verification of fingerprints that the
-\fBsign\fR and \fBapprove\fR commands by default require.
+\fBsign\fR command by default requires.
 
 .IP "\fB\-d\fR \fIdir\fR"
 .IQ "\fB\-\^\-dir=\fR\fIdir\fR"
@@ -330,5 +239,4 @@ Prints a help usage message and exits.
 
 .SH "SEE ALSO"
 
-.BR ovs\-controller (8),
-.BR ovs\-pki\-cgi (8)
+.BR ovs\-controller (8).
index e79ecc6..2a67d53 100755 (executable)
@@ -1,6 +1,6 @@
 #! /bin/sh
 
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -24,6 +24,38 @@ batch=no
 log='@LOGDIR@/ovs-pki.log'
 keytype=rsa
 bits=2048
+
+# OS-specific compatibility routines
+case $(uname -s) in
+FreeBSD)
+    file_mod_epoch()
+    {
+        stat -r "$1" | awk '{print $10}'
+    }
+
+    file_mod_date()
+    {
+        stat -f '%Sm' "$1"
+    }
+
+    sha1sum()
+    {
+        sha1 "$@"
+    }
+    ;;
+*)
+    file_mod_epoch()
+    {
+        date -r "$1" +%s
+    }
+
+    file_mod_date()
+    {
+        date -r "$1"
+    }
+    ;;
+esac
+
 for option; do
     # This option-parsing mechanism borrowed from a Autoconf-generated
     # configure script under the following license:
@@ -63,20 +95,6 @@ The valid stand-alone commands and their arguments are:
   fingerprint FILE     Prints the fingerprint for FILE
   self-sign NAME       Sign NAME-req.pem with NAME-privkey.pem,
                        producing self-signed certificate NAME-cert.pem
-
-The following additional commands manage an online PKI:
-  ls [PREFIX] [TYPE]   Lists incoming requests of the given TYPE, optionally 
-                       limited to those whose fingerprint begins with PREFIX
-  flush [TYPE]         Rejects all incoming requests of the given TYPE
-  reject PREFIX [TYPE] Rejects the incoming request(s) whose fingerprint begins
-                       with PREFIX and has the given TYPE
-  approve PREFIX [TYPE] Approves the incoming request whose fingerprint begins
-                       with PREFIX and has the given TYPE
-  expire [AGE]         Rejects all incoming requests older than AGE, in
-                       one of the forms Ns, Nmin, Nh, Nday (default: 1day)
-  prompt [TYPE]        Interactively prompts to accept or reject each incoming
-                       request of the given TYPE
-
 Each TYPE above is a certificate type: 'switch' (default) or 'controller'.
 
 Options for 'init', 'req', and 'req+sign' only:
@@ -85,7 +103,7 @@ Options for 'init', 'req', and 'req+sign' only:
                          this has an effect only on 'init'.
   -D, --dsaparam=FILE  File with DSA parameters (DSA only)
                          (default: dsaparam.pem within PKI directory)
-Options for use with the 'sign' and 'approve' commands:
+Options for use with the 'sign' command:
   -b, --batch          Skip fingerprint verification
 Options that apply to any command:
   -d, --dir=DIR        Directory where the PKI is located
@@ -219,7 +237,6 @@ if test "$command" = "init"; then
 
         mkdir -p certs crl newcerts
         mkdir -p -m 0700 private
-        mkdir -p -m 0733 incoming
         touch index.txt
         test -e crlnumber || echo 01 > crlnumber
         test -e serial || echo 01 > serial
@@ -302,13 +319,6 @@ one_arg() {
     fi
 }
 
-zero_or_one_args() {
-    if test -n "$arg2"; then
-        echo "$0: $command must have zero or one arguments; use --help for help" >&2
-        exit 1
-    fi
-}
-
 one_or_two_args() {
     if test -z "$arg1"; then
         echo "$0: $command must have one or two arguments; use --help for help" >&2
@@ -323,38 +333,6 @@ must_not_exist() {
     fi
 }
 
-resolve_prefix() {
-    test -n "$type" || exit 123 # Forgot to call check_type?
-
-    case $1 in
-        ????*)
-            ;;
-        *)
-            echo "Prefix $arg1 is too short (less than 4 hex digits)" >&2
-            exit 0
-            ;;
-    esac
-    
-    fingerprint=$(cd "$pkidir/${type}ca/incoming" && echo "$1"*-req.pem | sed 's/-req\.pem$//')
-    case $fingerprint in
-        "${1}*")
-            echo "No certificate requests matching $1" >&2
-            exit 1
-            ;;
-        *" "*)
-            echo "$1 matches more than one certificate request:" >&2
-            echo $fingerprint | sed 's/ /\
-/g' >&2
-            exit 1
-            ;;
-        *)
-            # Nothing to do.
-            ;;
-    esac
-    req="$pkidir/${type}ca/incoming/$fingerprint-req.pem"
-    cert="$pkidir/${type}ca/certs/$fingerprint-cert.pem"
-}
-
 make_tmpdir() {
     TMP=/tmp/ovs-pki.tmp$$
     rm -rf $TMP
@@ -365,7 +343,7 @@ make_tmpdir() {
 fingerprint() {
     file=$1
     name=${1-$2}
-    date=$(date -r $file)
+    date=$(file_mod_date "$file")
     if grep -e '-BEGIN CERTIFICATE-' "$file" > /dev/null; then
         fingerprint=$(openssl x509 -noout -in "$file" -fingerprint |
                       sed 's/SHA1 Fingerprint=//' | tr -d ':')
@@ -539,82 +517,6 @@ elif test "$command" = self-sign; then
     # Reset the permissions on the certificate to the user's default.
     cat "$arg1-cert.pem.tmp" > "$arg1-cert.pem"
     rm -f "$arg1-cert.pem.tmp"
-elif test "$command" = ls; then
-    check_type "$arg2"
-
-    cd "$pkidir/${type}ca/incoming"
-    for file in $(glob "$arg1*-req.pem"); do
-        fingerprint $file
-    done
-elif test "$command" = flush; then
-    check_type "$arg1"
-
-    rm -f "$pkidir/${type}ca/incoming/"*
-elif test "$command" = reject; then
-    one_or_two_args
-    check_type "$arg2"
-    resolve_prefix "$arg1"
-
-    rm -f "$req"
-elif test "$command" = approve; then
-    one_or_two_args
-    check_type "$arg2"
-    resolve_prefix "$arg1"
-
-    make_tmpdir
-    cp "$req" "$TMP/$req"
-    verify_fingerprint "$TMP/$req"
-    sign_request "$TMP/$req"
-    rm -f "$req" "$TMP/$req"
-elif test "$command" = prompt; then
-    zero_or_one_args
-    check_type "$arg1"
-
-    make_tmpdir
-    cd "$pkidir/${type}ca/incoming"
-    for req in $(glob "*-req.pem"); do
-        cp "$req" "$TMP/$req"
-
-        cert=$(echo "$pkidir/${type}ca/certs/$req" |
-               sed 's/-req.pem/-cert.pem/')
-        if test -f $cert; then
-            echo "Request $req already approved--dropping duplicate request"
-            rm -f "$req" "$TMP/$req"
-            continue
-        fi
-
-        echo
-        echo
-        fingerprint "$TMP/$req" "$req"
-        printf "Disposition for this request (skip/approve/reject)? "
-        read answer
-        case $answer in
-            approve)
-                echo "Approving $req"
-                sign_request "$TMP/$req" "$cert"
-                rm -f "$req" "$TMP/$req"
-                ;;
-            r*)
-                echo "Rejecting $req"
-                rm -f "$req" "$TMP/$req"
-                ;;
-            *)
-                echo "Skipping $req"
-                ;;
-        esac
-    done
-elif test "$command" = expire; then
-    zero_or_one_args
-    cutoff=$(($(date +%s) - $(parse_age ${arg1-1day})))
-    for type in switch controller; do
-        cd "$pkidir/${type}ca/incoming" || exit 1
-        for file in $(glob "*"); do
-            time=$(date -r "$file" +%s)
-            if test "$time" -lt "$cutoff"; then
-                rm -f "$file"
-            fi
-        done
-    done
 else
     echo "$0: $command command unknown; use --help for help" >&2
     exit 1
index de31290..297c2fa 100755 (executable)
@@ -1,6 +1,6 @@
 #! /bin/sh
 
-# Copyright (c) 2011 Nicira Networks, Inc.
+# Copyright (c) 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.
index 4f23905..0268c50 100755 (executable)
@@ -1,6 +1,6 @@
 #! @PYTHON@
 #
-# Copyright (c) 2010 Nicira Networks.
+# Copyright (c) 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.
index b53302f..fb1f9ad 100644 (file)
@@ -32,80 +32,18 @@ import twisted
 
 import ovstest.args as args
 import ovstest.rpcserver as rpcserver
+import ovstest.tests as tests
+import ovstest.util as util
 
 DEFAULT_TEST_BRIDGE = "ovstestbr0"
 DEFAULT_TEST_PORT = "ovstestport0"
 DEFAULT_TEST_TUN = "ovstestport1"
 
 
-def rpc_client(ip, port):
-    return xmlrpclib.Server("http://%s:%u/" % (ip, port), allow_none=True)
-
-
-def sigint_intercept():
-    """
-    Intercept SIGINT from child (the local ovs-test server process).
-    """
-    signal.signal(signal.SIGINT, signal.SIG_IGN)
-
-
-def start_local_server(port):
-    """
-    This function spawns an ovs-test server that listens on specified port
-    and blocks till the spawned ovs-test server is ready to accept XML RPC
-    connections.
-    """
-    p = subprocess.Popen(["ovs-test", "-s", str(port)],
-                         stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                         preexec_fn = sigint_intercept)
-    fcntl.fcntl( p.stdout.fileno(),fcntl.F_SETFL,
-        fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
-
-    while p.poll() is None:
-        fd = select.select([p.stdout.fileno()], [], [])[0]
-        if fd:
-            out = p.stdout.readline()
-            if out.startswith("Starting RPC server"):
-                break
-    if p.poll() is not None:
-        raise RuntimeError("Couldn't start local instance of ovs-test server")
-    return p
-
-
-def get_datagram_sizes(mtu1, mtu2):
-    """
-    This function calculates all the "interesting" datagram sizes so that
-    we test both - receive and send side with different packets sizes.
-    """
-    s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1])
-    s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2])
-    return sorted(s1.union(s2))
-
-
-def ip_from_cidr(string):
-    """
-    This function removes the netmask (if present) from the given string and
-    returns the IP address.
-    """
-    token = string.split("/")
-    return token[0]
-
-
-def bandwidth_to_string(bwidth):
-    """Convert bandwidth from long to string and add units."""
-    bwidth = bwidth * 8  # Convert back to bits/second
-    if bwidth >= 10000000:
-        return str(int(bwidth / 1000000)) + "Mbps"
-    elif bwidth > 10000:
-        return str(int(bwidth / 1000)) + "Kbps"
-    else:
-        return str(int(bwidth)) + "bps"
-
-
 def collect_information(node):
     """Print information about hosts that will do testing"""
     print "Node %s:%u " % (node[0], node[1])
-    server = rpc_client(node[0], node[1])
+    server = util.rpc_client(node[0], node[1])
     interface_name = server.get_interface(node[0])
     phys_iface = None
     uname = server.uname()
@@ -140,210 +78,6 @@ def collect_information(node):
     return mtu
 
 
-def do_udp_tests(receiver, sender, tbwidth, duration, port_sizes):
-    """Schedule UDP tests between receiver and sender"""
-    server1 = rpc_client(receiver[0], receiver[1])
-    server2 = rpc_client(sender[0], sender[1])
-
-    udpformat = '{0:>15} {1:>15} {2:>15} {3:>15} {4:>15}'
-
-    print ("UDP test from %s:%u to %s:%u with target bandwidth %s" %
-                            (sender[0], sender[1], receiver[0], receiver[1],
-                             bandwidth_to_string(tbwidth)))
-    print udpformat.format("Datagram Size", "Snt Datagrams", "Rcv Datagrams",
-                            "Datagram Loss", "Bandwidth")
-
-    for size in port_sizes:
-        listen_handle = -1
-        send_handle = -1
-        try:
-            packetcnt = (tbwidth * duration) / size
-
-            listen_handle = server1.create_udp_listener(receiver[3])
-            if listen_handle == -1:
-                print ("Server could not open UDP listening socket on port"
-                        " %u. Try to restart the server.\n" % receiver[3])
-                return
-            send_handle = server2.create_udp_sender(
-                                            (ip_from_cidr(receiver[2]),
-                                             receiver[3]), packetcnt, size,
-                                             duration)
-
-            # Using sleep here because there is no other synchronization source
-            # that would notify us when all sent packets were received
-            time.sleep(duration + 1)
-
-            rcv_packets = server1.get_udp_listener_results(listen_handle)
-            snt_packets = server2.get_udp_sender_results(send_handle)
-
-            loss = math.ceil(((snt_packets - rcv_packets) * 10000.0) /
-                                                        snt_packets) / 100
-            bwidth = (rcv_packets * size) / duration
-
-            print udpformat.format(size, snt_packets, rcv_packets,
-                                '%.2f%%' % loss, bandwidth_to_string(bwidth))
-        finally:
-            if listen_handle != -1:
-                server1.close_udp_listener(listen_handle)
-            if send_handle != -1:
-                server2.close_udp_sender(send_handle)
-    print "\n"
-
-
-def do_tcp_tests(receiver, sender, duration):
-    """Schedule TCP tests between receiver and sender"""
-    server1 = rpc_client(receiver[0], receiver[1])
-    server2 = rpc_client(sender[0], sender[1])
-
-    tcpformat = '{0:>15} {1:>15} {2:>15}'
-    print "TCP test from %s:%u to %s:%u (full speed)" % (sender[0], sender[1],
-                                                    receiver[0], receiver[1])
-    print tcpformat.format("Snt Bytes", "Rcv Bytes", "Bandwidth")
-
-    listen_handle = -1
-    send_handle = -1
-    try:
-        listen_handle = server1.create_tcp_listener(receiver[3])
-        if listen_handle == -1:
-            print ("Server was unable to open TCP listening socket on port"
-                    " %u. Try to restart the server.\n" % receiver[3])
-            return
-        send_handle = server2.create_tcp_sender(ip_from_cidr(receiver[2]),
-                                                receiver[3], duration)
-
-        time.sleep(duration + 1)
-
-        rcv_bytes = long(server1.get_tcp_listener_results(listen_handle))
-        snt_bytes = long(server2.get_tcp_sender_results(send_handle))
-
-        bwidth = rcv_bytes / duration
-
-        print tcpformat.format(snt_bytes, rcv_bytes,
-                               bandwidth_to_string(bwidth))
-    finally:
-        if listen_handle != -1:
-            server1.close_tcp_listener(listen_handle)
-        if send_handle != -1:
-            server2.close_tcp_sender(send_handle)
-    print "\n"
-
-
-def do_l3_tests(node1, node2, bandwidth, duration, ps, type):
-    """
-    Do L3 tunneling tests.
-    """
-    server1 = rpc_client(node1[0], node1[1])
-    server2 = rpc_client(node2[0], node2[1])
-    servers_with_bridges = []
-    try:
-        server1.create_bridge(DEFAULT_TEST_BRIDGE)
-        servers_with_bridges.append(server1)
-        server2.create_bridge(DEFAULT_TEST_BRIDGE)
-        servers_with_bridges.append(server2)
-
-        server1.interface_up(DEFAULT_TEST_BRIDGE)
-        server2.interface_up(DEFAULT_TEST_BRIDGE)
-
-        server1.interface_assign_ip(DEFAULT_TEST_BRIDGE, node1[2], None)
-        server2.interface_assign_ip(DEFAULT_TEST_BRIDGE, node2[2], None)
-
-        server1.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
-        server2.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
-
-        server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
-                              None, type)
-        server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
-                              None, type)
-        server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
-                              "remote_ip", node2[0])
-        server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
-                              "remote_ip", node1[0])
-
-        do_udp_tests(node1, node2, bandwidth, duration, ps)
-        do_udp_tests(node2, node1, bandwidth, duration, ps)
-        do_tcp_tests(node1, node2, duration)
-        do_tcp_tests(node2, node1, duration)
-
-    finally:
-        for server in servers_with_bridges:
-            server.del_bridge(DEFAULT_TEST_BRIDGE)
-
-
-
-def do_vlan_tests(node1, node2, bandwidth, duration, ps, tag):
-    """
-    Do VLAN tests between node1 and node2.
-    """
-    server1 = rpc_client(node1[0], node1[1])
-    server2 = rpc_client(node2[0], node2[1])
-
-    br_name1 = None
-    br_name2 = None
-
-    servers_with_test_ports = []
-
-    try:
-        interface_node1 = server1.get_interface(node1[0])
-        interface_node2 = server2.get_interface(node2[0])
-
-        if server1.is_ovs_bridge(interface_node1):
-            br_name1 = interface_node1
-        else:
-            br_name1 = DEFAULT_TEST_BRIDGE
-            server1.create_test_bridge(br_name1, interface_node1)
-
-        if server2.is_ovs_bridge(interface_node2):
-            br_name2 = interface_node2
-        else:
-            br_name2 = DEFAULT_TEST_BRIDGE
-            server2.create_test_bridge(br_name2, interface_node2)
-
-        server1.add_port_to_bridge(br_name1, DEFAULT_TEST_PORT)
-        servers_with_test_ports.append(server1)
-        server2.add_port_to_bridge(br_name2, DEFAULT_TEST_PORT)
-        servers_with_test_ports.append(server2)
-
-        server1.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
-        server2.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
-
-        server1.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
-                              "internal")
-        server2.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
-                              "internal")
-
-        server1.interface_assign_ip(DEFAULT_TEST_PORT, node1[2], None)
-        server2.interface_assign_ip(DEFAULT_TEST_PORT, node2[2], None)
-
-        server1.interface_up(DEFAULT_TEST_PORT)
-        server2.interface_up(DEFAULT_TEST_PORT)
-
-        do_udp_tests(node1, node2, bandwidth, duration, ps)
-        do_udp_tests(node2, node1, bandwidth, duration, ps)
-        do_tcp_tests(node1, node2, duration)
-        do_tcp_tests(node2, node1, duration)
-
-    finally:
-        for server in servers_with_test_ports:
-            server.del_port_from_bridge(DEFAULT_TEST_PORT)
-        if br_name1 == DEFAULT_TEST_BRIDGE:
-            server1.del_test_bridge(br_name1, interface_node1)
-        if br_name2 == DEFAULT_TEST_BRIDGE:
-            server2.del_test_bridge(br_name2, interface_node2)
-
-
-def do_direct_tests(node1, node2, bandwidth, duration, ps):
-    """
-    Do tests between outer IPs without involving Open vSwitch
-    """
-    n1 = (node1[0], node1[1], node1[0], node1[3])
-    n2 = (node2[0], node2[1], node2[0], node2[3])
-
-    do_udp_tests(n1, n2, bandwidth, duration, ps)
-    do_udp_tests(n2, n1, bandwidth, duration, ps)
-    do_tcp_tests(n1, n2, duration)
-    do_tcp_tests(n2, n1, duration)
-
-
 if __name__ == '__main__':
     local_server = None
     try:
@@ -360,10 +94,10 @@ if __name__ == '__main__':
             # ovs-test server by looking at the first OuterIP. if it is a
             # 127.0.0.1 then spawn local ovs-test server.
             if node1[0] == "127.0.0.1":
-                local_server = start_local_server(node1[1])
+                local_server = util.start_local_server(node1[1])
                 # We must determine the IP address that local ovs-test server
                 # will use:
-                me = rpc_client(node1[0], node1[1])
+                me = util.rpc_client(node1[0], node1[1])
                 my_ip = me.get_my_address_from(node2[0], node2[1])
                 node1 = (my_ip, node1[1], node1[2], node1[3])
 
@@ -372,7 +106,7 @@ if __name__ == '__main__':
 
             bandwidth = ovs_args.targetBandwidth
             interval = ovs_args.testInterval
-            ps = get_datagram_sizes(mtu_node1, mtu_node2)
+            ps = util.get_datagram_sizes(mtu_node1, mtu_node2)
 
             direct = ovs_args.direct
             vlan_tag = ovs_args.vlanTag
@@ -380,15 +114,17 @@ if __name__ == '__main__':
 
             if direct is not None:
                 print "Performing direct tests"
-                do_direct_tests(node2, node1, bandwidth, interval, ps)
+                tests.do_direct_tests(node2, node1, bandwidth, interval, ps)
 
             if vlan_tag is not None:
                 print "Performing VLAN tests"
-                do_vlan_tests(node2, node1, bandwidth, interval, ps, vlan_tag)
+                tests.do_vlan_tests(node2, node1, bandwidth, interval, ps,
+                                    vlan_tag)
 
             for tmode in tunnel_modes:
                 print "Performing", tmode, "tests"
-                do_l3_tests(node2, node1, bandwidth, interval, ps, tmode)
+                tests.do_l3_tests(node2, node1, bandwidth, interval, ps,
+                                  tmode)
 
     except KeyboardInterrupt:
         pass
index f459dec..8688f61 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 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.
index f937845..e229498 100755 (executable)
@@ -1,6 +1,6 @@
 #! @PYTHON@
 #
-# Copyright (c) 2010 Nicira Networks.
+# Copyright (c) 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.
index 57f76d5..1b80d05 100644 (file)
@@ -25,19 +25,17 @@ ovs\-vsctl \- utility for querying and configuring \fBovs\-vswitchd\fR
 .
 .SH DESCRIPTION
 The \fBovs\-vsctl\fR program configures \fBovs\-vswitchd\fR(8) by
-providing a high\-level interface to its configuration
-database.  This program is mainly intended for use when
-\fBovs\-vswitchd\fR is running.  If it is used when
-\fBovs\-vswitchd\fR is not running, then \fB\-\-no\-wait\fR should be
-specified and configuration changes will only take effect when
-\fBovs\-vswitchd\fR is started.
-.PP
-By default, each time \fBovs\-vsctl\fR runs, it connects to an
-\fBovsdb\-server\fR process that maintains an Open vSwitch
-configuration database.  Using this connection, it queries and
-possibly applies changes to the database, depending on the supplied
-commands.  Then, if it applied any changes, it waits until
-\fBovs\-vswitchd\fR has finished reconfiguring itself before it exits.
+providing a high\-level interface to its configuration database.
+See \fBovs\-vswitchd.conf.db\fR(5) for comprehensive documentation of
+the database schema.
+.PP
+\fBovs\-vsctl\fR connects to an \fBovsdb\-server\fR process that
+maintains an Open vSwitch configuration database.  Using this
+connection, it queries and possibly applies changes to the database,
+depending on the supplied commands.  Then, if it applied any changes,
+by default it waits until \fBovs\-vswitchd\fR has finished
+reconfiguring itself before it exits.  (If you use \fBovs\-vsctl\fR
+when \fBovs\-vswitchd\fR is not running, use \fB\-\-no\-wait\fR.)
 .PP
 \fBovs\-vsctl\fR can perform any number of commands in a single run,
 implemented as a single atomic transaction against the database.
@@ -716,6 +714,8 @@ this.
 .IP "\fR[\fB\-\-if\-exists\fR] \fBdestroy \fItable record\fR..."
 Deletes each specified \fIrecord\fR from \fItable\fR.  Unless
 \fB\-\-if\-exists\fR is specified, each \fIrecord\fRs must exist.
+.IP "\fB\-\-all destroy \fItable\fR"
+Deletes all records from the \fItable\fR.
 .IP
 The \fBdestroy\fR command is only useful for records in the \fBQoS\fR
 or \fBQueue\fR tables.  Records in other tables are automatically
@@ -815,7 +815,13 @@ ignored):
 Remove the mirror created above from \fBbr0\fR, which also destroys
 the Mirror record (since it is now unreferenced):
 .IP
-.B "remove Bridge br0 mirrors mymirror"
+.B "ovs\-vsctl \-\- \-\-id=@rec get Mirror mymirror \(rs"
+.IP
+.B "\-\- remove Bridge br0 mirrors @rec"
+.PP
+The following simpler command also works:
+.IP
+.B "ovs\-vsctl clear Bridge br0 mirrors"
 .SS "Quality of Service (QoS)"
 .PP
 Create a \fBlinux\-htb\fR QoS record that points to a few queues and
@@ -844,7 +850,8 @@ unreferenced QoS records are not automatically destroyed):
 (This command will leave two unreferenced Queue records in the
 database.  To delete them, use "\fBovs\-vsctl list Queue\fR" to find
 their UUIDs, then "\fBovs\-vsctl destroy Queue \fIuuid1\fR
-\fIuuid2\fR" to destroy each of them.)
+\fIuuid2\fR" to destroy each of them or use
+"\fBovs\-vsctl -- --all destroy Queue\fR" to delete all records.)
 .SS "Connectivity Monitoring"
 .PP
 Monitor connectivity to a remote maintenance point on eth0.
@@ -915,4 +922,5 @@ bridge that does not exist.
 .SH "SEE ALSO"
 .
 .BR ovsdb\-server (1),
-.BR ovs\-vswitchd (8).
+.BR ovs\-vswitchd (8),
+.BR ovs\-vswitchd.conf.db (5).
index 4c5362c..0a7c2c6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@
 #include "process.h"
 #include "stream.h"
 #include "stream-ssl.h"
+#include "smap.h"
 #include "sset.h"
 #include "svec.h"
 #include "lib/vswitch-idl.h"
@@ -1354,9 +1355,7 @@ cmd_emer_reset(struct vsctl_context *ctx)
     ovsrec_open_vswitch_set_ssl(ctx->ovs, NULL);
 
     OVSREC_BRIDGE_FOR_EACH (br, idl) {
-        int i;
-        char *hw_key = "hwaddr";
-        char *hw_val = NULL;
+        const char *hwaddr;
 
         ovsrec_bridge_set_controller(br, NULL, 0);
         ovsrec_bridge_set_fail_mode(br, NULL);
@@ -1366,23 +1365,19 @@ cmd_emer_reset(struct vsctl_context *ctx)
         ovsrec_bridge_set_flood_vlans(br, NULL, 0);
 
         /* We only want to save the "hwaddr" key from other_config. */
-        for (i=0; i < br->n_other_config; i++) {
-            if (!strcmp(br->key_other_config[i], hw_key)) {
-                hw_val = br->value_other_config[i];
-                break;
-            }
-        }
-        if (hw_val) {
-            char *val = xstrdup(hw_val);
-            ovsrec_bridge_set_other_config(br, &hw_key, &val, 1);
-            free(val);
+        hwaddr = smap_get(&br->other_config, "hwaddr");
+        if (hwaddr) {
+            struct smap smap = SMAP_INITIALIZER(&smap);
+            smap_add(&smap, "hwaddr", hwaddr);
+            ovsrec_bridge_set_other_config(br, &smap);
+            smap_destroy(&smap);
         } else {
-            ovsrec_bridge_set_other_config(br, NULL, NULL, 0);
+            ovsrec_bridge_set_other_config(br, NULL);
         }
     }
 
     OVSREC_PORT_FOR_EACH (port, idl) {
-        ovsrec_port_set_other_config(port, NULL, NULL, 0);
+        ovsrec_port_set_other_config(port, NULL);
     }
 
     OVSREC_INTERFACE_FOR_EACH (iface, idl) {
@@ -1608,43 +1603,17 @@ cmd_br_exists(struct vsctl_context *ctx)
     }
 }
 
-/* Returns true if 'b_prefix' (of length 'b_prefix_len') concatenated with 'b'
- * equals 'a', false otherwise. */
-static bool
-key_matches(const char *a,
-            const char *b_prefix, size_t b_prefix_len, const char *b)
-{
-    return !strncmp(a, b_prefix, b_prefix_len) && !strcmp(a + b_prefix_len, b);
-}
-
 static void
-set_external_id(char **old_keys, char **old_values, size_t old_n,
-                char *key, char *value,
-                char ***new_keysp, char ***new_valuesp, size_t *new_np)
+set_external_id(struct smap *old, struct smap *new,
+                char *key, char *value)
 {
-    char **new_keys;
-    char **new_values;
-    size_t new_n;
-    size_t i;
+    smap_clone(new, old);
 
-    new_keys = xmalloc(sizeof *new_keys * (old_n + 1));
-    new_values = xmalloc(sizeof *new_values * (old_n + 1));
-    new_n = 0;
-    for (i = 0; i < old_n; i++) {
-        if (strcmp(key, old_keys[i])) {
-            new_keys[new_n] = old_keys[i];
-            new_values[new_n] = old_values[i];
-            new_n++;
-        }
-    }
     if (value) {
-        new_keys[new_n] = key;
-        new_values[new_n] = value;
-        new_n++;
+        smap_replace(new, key, value);
+    } else {
+        smap_remove(new, key);
     }
-    *new_keysp = new_keys;
-    *new_valuesp = new_values;
-    *new_np = new_n;
 }
 
 static void
@@ -1659,56 +1628,54 @@ static void
 cmd_br_set_external_id(struct vsctl_context *ctx)
 {
     struct vsctl_bridge *bridge;
-    char **keys, **values;
-    size_t n;
+    struct smap new;
 
     vsctl_context_populate_cache(ctx);
     bridge = find_bridge(ctx, ctx->argv[1], true);
     if (bridge->br_cfg) {
-        set_external_id(bridge->br_cfg->key_external_ids,
-                        bridge->br_cfg->value_external_ids,
-                        bridge->br_cfg->n_external_ids,
-                        ctx->argv[2], ctx->argc >= 4 ? ctx->argv[3] : NULL,
-                        &keys, &values, &n);
+
+        set_external_id(&bridge->br_cfg->external_ids, &new, ctx->argv[2],
+                        ctx->argc >= 4 ? ctx->argv[3] : NULL);
         ovsrec_bridge_verify_external_ids(bridge->br_cfg);
-        ovsrec_bridge_set_external_ids(bridge->br_cfg, keys, values, n);
+        ovsrec_bridge_set_external_ids(bridge->br_cfg, &new);
     } else {
         char *key = xasprintf("fake-bridge-%s", ctx->argv[2]);
         struct vsctl_port *port = shash_find_data(&ctx->ports, ctx->argv[1]);
-        set_external_id(port->port_cfg->key_external_ids,
-                        port->port_cfg->value_external_ids,
-                        port->port_cfg->n_external_ids,
-                        key, ctx->argc >= 4 ? ctx->argv[3] : NULL,
-                        &keys, &values, &n);
+        set_external_id(&port->port_cfg->external_ids, &new,
+                        key, ctx->argc >= 4 ? ctx->argv[3] : NULL);
         ovsrec_port_verify_external_ids(port->port_cfg);
-        ovsrec_port_set_external_ids(port->port_cfg, keys, values, n);
+        ovsrec_port_set_external_ids(port->port_cfg, &new);
         free(key);
     }
-    free(keys);
-    free(values);
+    smap_destroy(&new);
 }
 
 static void
-get_external_id(char **keys, char **values, size_t n,
-                const char *prefix, const char *key,
+get_external_id(struct smap *smap, const char *prefix, const char *key,
                 struct ds *output)
 {
-    size_t prefix_len = strlen(prefix);
-    struct svec svec;
-    size_t i;
+    if (key) {
+        char *prefix_key = xasprintf("%s%s", prefix, key);
+        const char *value = smap_get(smap, prefix_key);
 
-    svec_init(&svec);
-    for (i = 0; i < n; i++) {
-        if (!key && !strncmp(keys[i], prefix, prefix_len)) {
-            svec_add_nocopy(&svec, xasprintf("%s=%s",
-                                             keys[i] + prefix_len, values[i]));
-        } else if (key && key_matches(keys[i], prefix, prefix_len, key)) {
-            svec_add(&svec, values[i]);
-            break;
+        if (value) {
+            ds_put_format(output, "%s\n", value);
         }
+        free(prefix_key);
+    } else {
+        const struct smap_node **sorted = smap_sort(smap);
+        size_t prefix_len = strlen(prefix);
+        size_t i;
+
+        for (i = 0; i < smap_count(smap); i++) {
+            const struct smap_node *node = sorted[i];
+            if (!strncmp(node->key, prefix, prefix_len)) {
+                ds_put_format(output, "%s=%s\n", node->key + prefix_len,
+                              node->value);
+            }
+        }
+        free(sorted);
     }
-    output_sorted(&svec, output);
-    svec_destroy(&svec);
 }
 
 static void
@@ -1727,18 +1694,13 @@ cmd_br_get_external_id(struct vsctl_context *ctx)
     bridge = find_bridge(ctx, ctx->argv[1], true);
     if (bridge->br_cfg) {
         ovsrec_bridge_verify_external_ids(bridge->br_cfg);
-        get_external_id(bridge->br_cfg->key_external_ids,
-                        bridge->br_cfg->value_external_ids,
-                        bridge->br_cfg->n_external_ids,
-                        "", ctx->argc >= 3 ? ctx->argv[2] : NULL,
-                        &ctx->output);
+        get_external_id(&bridge->br_cfg->external_ids, "",
+                        ctx->argc >= 3 ? ctx->argv[2] : NULL, &ctx->output);
     } else {
         struct vsctl_port *port = shash_find_data(&ctx->ports, ctx->argv[1]);
         ovsrec_port_verify_external_ids(port->port_cfg);
-        get_external_id(port->port_cfg->key_external_ids,
-                        port->port_cfg->value_external_ids,
-                        port->port_cfg->n_external_ids,
-                        "fake-bridge-", ctx->argc >= 3 ? ctx->argv[2] : NULL, &ctx->output);
+        get_external_id(&port->port_cfg->external_ids, "fake-bridge-",
+                        ctx->argc >= 3 ? ctx->argv[2] : NULL, &ctx->output);
     }
 }
 
@@ -3471,20 +3433,41 @@ static void
 cmd_destroy(struct vsctl_context *ctx)
 {
     bool must_exist = !shash_find(&ctx->options, "--if-exists");
+    bool delete_all = shash_find(&ctx->options, "--all");
     const char *table_name = ctx->argv[1];
     const struct vsctl_table_class *table;
     int i;
 
     table = get_table(table_name);
-    for (i = 2; i < ctx->argc; i++) {
+
+    if (delete_all && ctx->argc > 2) {
+        vsctl_fatal("--all and records argument should not be specified together");
+    }
+
+    if (delete_all && !must_exist) {
+        vsctl_fatal("--all and --if-exists should not be specified together");
+    }
+
+    if (delete_all) {
         const struct ovsdb_idl_row *row;
+        const struct ovsdb_idl_row *next_row;
 
-        row = (must_exist ? must_get_row : get_row)(ctx, table, ctx->argv[i]);
-        if (row) {
-            ovsdb_idl_txn_delete(row);
+        for (row = ovsdb_idl_first_row(ctx->idl, table->class);
+             row;) {
+             next_row = ovsdb_idl_next_row(row);
+             ovsdb_idl_txn_delete(row);
+             row = next_row;
         }
-    }
+    } else {
+        for (i = 2; i < ctx->argc; i++) {
+            const struct ovsdb_idl_row *row;
 
+            row = (must_exist ? must_get_row : get_row)(ctx, table, ctx->argv[i]);
+            if (row) {
+                ovsdb_idl_txn_delete(row);
+            }
+        }
+    }
     vsctl_context_invalidate_cache(ctx);
 }
 
@@ -3809,8 +3792,6 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
 
         if (ctx.try_again) {
             vsctl_context_done(&ctx, NULL);
-
-            status = TXN_TRY_AGAIN;
             goto try_again;
         }
     }
@@ -3917,7 +3898,7 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
         table_destroy(c->table);
         free(c->table);
 
-        smap_destroy(&c->options);
+        shash_destroy_free_data(&c->options);
     }
     free(commands);
 
@@ -4017,8 +3998,8 @@ static const struct vsctl_command_syntax all_commands[] = {
     {"remove", 4, INT_MAX, pre_cmd_remove, cmd_remove, NULL, "", RW},
     {"clear", 3, INT_MAX, pre_cmd_clear, cmd_clear, NULL, "", RW},
     {"create", 2, INT_MAX, pre_create, cmd_create, post_create, "--id=", RW},
-    {"destroy", 1, INT_MAX, pre_cmd_destroy, cmd_destroy, NULL, "--if-exists",
-     RW},
+    {"destroy", 1, INT_MAX, pre_cmd_destroy, cmd_destroy, NULL,
+     "--if-exists,--all", RW},
     {"wait-until", 2, INT_MAX, pre_cmd_wait_until, cmd_wait_until, NULL, "",
      RO},
 
index 645752d..2b48c9b 100644 (file)
@@ -27,12 +27,13 @@ one slave, the port becomes an ordinary port, not a bonded port, and
 none of the special features of bonded ports described in this section
 apply.
 
-There are many forms of bonding, but ovs-vswitchd currently implements
-only a single kind, called "source load balancing" or SLB bonding.
-SLB bonding divides traffic among the slaves based on the Ethernet
-source address.  This is useful only if the traffic over the bond has
-multiple Ethernet source addresses, for example if network traffic
-from multiple VMs are multiplexed over the bond.
+There are many forms of bonding of which ovs-vswitchd implements only
+a few.  The most complex bond ovs-vswitchd implements is called
+"source load balancing" or SLB bonding.  SLB bonding divides traffic
+among the slaves based on the Ethernet source address.  This is useful
+only if the traffic over the bond has multiple Ethernet source
+addresses, for example if network traffic from multiple VMs are
+multiplexed over the bond.
 
 Enabling and Disabling Slaves
 -----------------------------
@@ -57,14 +58,15 @@ enabled all output packets are blackholed anyway.
 
 When a slave becomes disabled, the vswitch immediately chooses a new
 output port for traffic that was destined for that slave (see
-bond_enable_slave()).  It also sends a "gratuitous learning packet" on
-the bond port (on the newly chosen slave) for each MAC address that
-the vswitch has learned on a port other than the bond (see
-bond_send_learning_packets()), to teach the physical switch that the
-new slave should be used in place of the one that is now disabled.
-(This behavior probably makes sense only for a vswitch that has only
-one port (the bond) connected to a physical switch; vswitchd should
-probably provide a way to disable or configure it in other scenarios.)
+bond_enable_slave()).  It also sends a "gratuitous learning packet",
+specifically a RARP, on the bond port (on the newly chosen slave) for
+each MAC address that the vswitch has learned on a port other than the
+bond (see bond_send_learning_packets()), to teach the physical switch
+that the new slave should be used in place of the one that is now
+disabled.  (This behavior probably makes sense only for a vswitch that
+has only one port (the bond) connected to a physical switch; vswitchd
+should probably provide a way to disable or configure it in other
+scenarios.)
 
 Bond Packet Input
 -----------------
@@ -141,6 +143,15 @@ LACP bonding requires the remote switch to implement LACP, but it is
 otherwise very simple in that, after LACP negotiation is complete,
 there is no need for special handling of received packets.
 
+Active Backup Bonding
+---------------------
+
+Active Backup bonds send all traffic out one "active" slave until that
+slave becomes unavailable.  Since they are significantly less
+complicated than SLB bonds, they are preferred when LACP is not an
+option.  Additionally, they are the only bond mode which supports
+attaching each slave to a different upstream switch.
+
 SLB Bonding
 -----------
 
index 61bf21c..53bb7b9 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@
 #include "poll-loop.h"
 #include "sha1.h"
 #include "shash.h"
+#include "smap.h"
 #include "socket-util.h"
 #include "stream.h"
 #include "stream-ssl.h"
@@ -147,16 +148,10 @@ static struct ovsdb_idl *idl;
 /* Most recently processed IDL sequence number. */
 static unsigned int idl_seqno;
 
-/* Each time this timer expires, the bridge fetches systems and interface
+/* Each time this timer expires, the bridge fetches interface and mirror
  * statistics and pushes them into the database. */
-#define STATS_INTERVAL (5 * 1000) /* In milliseconds. */
-static long long int stats_timer = LLONG_MIN;
-
-/* Stores the time after which rate limited statistics may be written to the
- * database.  Only updated when changes to the database require rate limiting.
- */
-#define DB_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
-static long long int db_limiter = LLONG_MIN;
+#define IFACE_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
+static long long int iface_stats_timer = LLONG_MIN;
 
 /* In some datapaths, creating and destroying OpenFlow ports can be extremely
  * expensive.  This can cause bridge_reconfigure() to take a long time during
@@ -219,6 +214,9 @@ static void port_configure_bond(struct port *, struct bond_settings *,
                                 uint32_t *bond_stable_ids);
 static bool port_is_synthetic(const struct port *);
 
+static void reconfigure_system_stats(const struct ovsrec_open_vswitch *);
+static void run_system_stats(void);
+
 static void bridge_configure_mirrors(struct bridge *);
 static struct mirror *mirror_create(struct bridge *,
                                     const struct ovsrec_mirror *);
@@ -245,10 +243,6 @@ 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 void shash_from_ovs_idl_map(char **keys, char **values, size_t n,
-                                   struct shash *);
-static void shash_to_ovs_idl_map(struct shash *,
-                                 char ***keys, char ***values, size_t *n);
 
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
  *
@@ -459,6 +453,8 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
             iface_clear_db_record(if_cfg->cfg);
         }
     }
+
+    reconfigure_system_stats(ovs_cfg);
 }
 
 static bool
@@ -477,6 +473,11 @@ bridge_reconfigure_ofp(void)
         struct ofpp_garbage *garbage, *next;
 
         LIST_FOR_EACH_SAFE (garbage, next, list_node, &br->ofpp_garbage) {
+            /* It's a bit dangerous to call bridge_run_fast() here as ofproto's
+             * internal datastructures may not be consistent.  Eventually, when
+             * port additions and deletions are cheaper, these calls should be
+             * removed. */
+            bridge_run_fast();
             ofproto_port_del(br->ofproto, garbage->ofp_port);
             list_remove(&garbage->list_node);
             free(garbage);
@@ -485,6 +486,7 @@ bridge_reconfigure_ofp(void)
             if (time_msec() >= deadline) {
                 return false;
             }
+            bridge_run_fast();
         }
     }
 
@@ -553,6 +555,8 @@ bridge_reconfigure_continue(const struct ovsrec_open_vswitch *ovs_cfg)
          * forked us to exit successfully. */
         daemonize_complete();
         reconfiguring = false;
+
+        VLOG_INFO("%s (Open vSwitch) %s", program_name, VERSION);
     }
 
     return done;
@@ -687,8 +691,8 @@ port_configure(struct port *port)
             s.vlan_mode = PORT_VLAN_TRUNK;
         }
     }
-    s.use_priority_tags = !strcmp("true", ovsrec_port_get_other_config_value(
-                                      cfg, "priority-tags", ""));
+    s.use_priority_tags = smap_get_bool(&cfg->other_config, "priority-tags",
+                                        false);
 
     /* Get LACP settings. */
     s.lacp = port_configure_lacp(port, &lacp_settings);
@@ -751,7 +755,10 @@ bridge_configure_datapath_id(struct bridge *br)
     memcpy(br->ea, ea, ETH_ADDR_LEN);
 
     dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
-    ofproto_set_datapath_id(br->ofproto, dpid);
+    if (dpid != ofproto_get_datapath_id(br->ofproto)) {
+        VLOG_INFO("bridge %s: using datapath ID %016"PRIx64, br->name, dpid);
+        ofproto_set_datapath_id(br->ofproto, dpid);
+    }
 
     dpid_string = xasprintf("%016"PRIx64, dpid);
     ovsrec_bridge_set_datapath_id(br->cfg, dpid_string);
@@ -878,9 +885,7 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port,
     const char *config_str;
     struct iface *iface;
 
-    config_str = ovsrec_port_get_other_config_value(port->cfg, "stp-enable",
-                                                    NULL);
-    if (config_str && !strcmp(config_str, "false")) {
+    if (!smap_get_bool(&port->cfg->other_config, "stp-enable", true)) {
         port_s->enable = false;
         return;
     } else {
@@ -912,8 +917,7 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port,
         return;
     }
 
-    config_str = ovsrec_port_get_other_config_value(port->cfg, "stp-port-num",
-                                                    NULL);
+    config_str = smap_get(&port->cfg->other_config, "stp-port-num");
     if (config_str) {
         unsigned long int port_num = strtoul(config_str, NULL, 0);
         int port_idx = port_num - 1;
@@ -933,7 +937,7 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port,
         bitmap_set1(port_num_bitmap, port_idx);
         port_s->port_num = port_idx;
     } else {
-        if (*port_num_counter > STP_MAX_PORTS) {
+        if (*port_num_counter >= STP_MAX_PORTS) {
             VLOG_ERR("port %s: too many STP ports, disabling", port->name);
             port_s->enable = false;
             return;
@@ -942,8 +946,7 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port,
         port_s->port_num = (*port_num_counter)++;
     }
 
-    config_str = ovsrec_port_get_other_config_value(port->cfg, "stp-path-cost",
-                                                    NULL);
+    config_str = smap_get(&port->cfg->other_config, "stp-path-cost");
     if (config_str) {
         port_s->path_cost = strtoul(config_str, NULL, 10);
     } else {
@@ -960,9 +963,7 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port,
         }
     }
 
-    config_str = ovsrec_port_get_other_config_value(port->cfg,
-                                                    "stp-port-priority",
-                                                    NULL);
+    config_str = smap_get(&port->cfg->other_config, "stp-port-priority");
     if (config_str) {
         port_s->priority = strtoul(config_str, NULL, 0);
     } else {
@@ -983,9 +984,7 @@ bridge_configure_stp(struct bridge *br)
         int port_num_counter;
         unsigned long *port_num_bitmap;
 
-        config_str = ovsrec_bridge_get_other_config_value(br->cfg,
-                                                          "stp-system-id",
-                                                          NULL);
+        config_str = smap_get(&br->cfg->other_config, "stp-system-id");
         if (config_str) {
             uint8_t ea[ETH_ADDR_LEN];
 
@@ -1000,36 +999,28 @@ bridge_configure_stp(struct bridge *br)
             br_s.system_id = eth_addr_to_uint64(br->ea);
         }
 
-        config_str = ovsrec_bridge_get_other_config_value(br->cfg,
-                                                          "stp-priority",
-                                                          NULL);
+        config_str = smap_get(&br->cfg->other_config, "stp-priority");
         if (config_str) {
             br_s.priority = strtoul(config_str, NULL, 0);
         } else {
             br_s.priority = STP_DEFAULT_BRIDGE_PRIORITY;
         }
 
-        config_str = ovsrec_bridge_get_other_config_value(br->cfg,
-                                                          "stp-hello-time",
-                                                          NULL);
+        config_str = smap_get(&br->cfg->other_config, "stp-hello-time");
         if (config_str) {
             br_s.hello_time = strtoul(config_str, NULL, 10) * 1000;
         } else {
             br_s.hello_time = STP_DEFAULT_HELLO_TIME;
         }
 
-        config_str = ovsrec_bridge_get_other_config_value(br->cfg,
-                                                          "stp-max-age",
-                                                          NULL);
+        config_str = smap_get(&br->cfg->other_config, "stp-max-age");
         if (config_str) {
             br_s.max_age = strtoul(config_str, NULL, 10) * 1000;
         } else {
             br_s.max_age = STP_DEFAULT_MAX_AGE;
         }
 
-        config_str = ovsrec_bridge_get_other_config_value(br->cfg,
-                                                          "stp-forward-delay",
-                                                          NULL);
+        config_str = smap_get(&br->cfg->other_config, "stp-forward-delay");
         if (config_str) {
             br_s.fwd_delay = strtoul(config_str, NULL, 10) * 1000;
         } else {
@@ -1150,16 +1141,9 @@ static int
 iface_set_netdev_config(const struct ovsrec_interface *iface_cfg,
                         struct netdev *netdev)
 {
-    struct shash args;
     int error;
 
-    shash_init(&args);
-    shash_from_ovs_idl_map(iface_cfg->key_options,
-                           iface_cfg->value_options,
-                           iface_cfg->n_options, &args);
-    error = netdev_set_config(netdev, &args);
-    shash_destroy(&args);
-
+    error = netdev_set_config(netdev, &iface_cfg->options);
     if (error) {
         VLOG_WARN("could not configure network device %s (%s)",
                   iface_cfg->name, strerror(error));
@@ -1355,9 +1339,15 @@ iface_create(struct bridge *br, struct if_cfg *if_cfg, int ofp_port)
     hmap_remove(&br->if_cfg_todo, &if_cfg->hmap_node);
     free(if_cfg);
 
-    /* Do the bits that can fail up front. */
+    /* Do the bits that can fail up front.
+     *
+     * It's a bit dangerous to call bridge_run_fast() here as ofproto's
+     * internal datastructures may not be consistent.  Eventually, when port
+     * additions and deletions are cheaper, these calls should be removed. */
+    bridge_run_fast();
     assert(!iface_lookup(br, iface_cfg->name));
     error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev);
+    bridge_run_fast();
     if (error) {
         iface_clear_db_record(iface_cfg);
         return false;
@@ -1420,10 +1410,8 @@ bridge_configure_flow_eviction_threshold(struct bridge *br)
     const char *threshold_str;
     unsigned threshold;
 
-    threshold_str =
-        ovsrec_bridge_get_other_config_value(br->cfg,
-                                             "flow-eviction-threshold",
-                                             NULL);
+    threshold_str = smap_get(&br->cfg->other_config,
+                             "flow-eviction-threshold");
     if (threshold_str) {
         threshold = strtoul(threshold_str, NULL, 10);
     } else {
@@ -1436,16 +1424,10 @@ bridge_configure_flow_eviction_threshold(struct bridge *br)
 static void
 bridge_configure_forward_bpdu(struct bridge *br)
 {
-    const char *forward_bpdu_str;
-    bool forward_bpdu = false;
-
-    forward_bpdu_str = ovsrec_bridge_get_other_config_value(br->cfg,
-                                                            "forward-bpdu",
-                                                            NULL);
-    if (forward_bpdu_str && !strcmp(forward_bpdu_str, "true")) {
-        forward_bpdu = true;
-    }
-    ofproto_set_forward_bpdu(br->ofproto, forward_bpdu);
+    ofproto_set_forward_bpdu(br->ofproto,
+                             smap_get_bool(&br->cfg->other_config,
+                                           "forward-bpdu",
+                                           false));
 }
 
 /* Set MAC aging time for 'br'. */
@@ -1455,9 +1437,7 @@ bridge_configure_mac_idle_time(struct bridge *br)
     const char *idle_time_str;
     int idle_time;
 
-    idle_time_str = ovsrec_bridge_get_other_config_value(br->cfg,
-                                                         "mac-aging-time",
-                                                         NULL);
+    idle_time_str = smap_get(&br->cfg->other_config, "mac-aging-time");
     idle_time = (idle_time_str && atoi(idle_time_str)
                  ? atoi(idle_time_str)
                  : MAC_ENTRY_DEFAULT_IDLE_TIME);
@@ -1478,7 +1458,7 @@ bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
     *hw_addr_iface = NULL;
 
     /* Did the user request a particular MAC? */
-    hwaddr = ovsrec_bridge_get_other_config_value(br->cfg, "hwaddr", NULL);
+    hwaddr = smap_get(&br->cfg->other_config, "hwaddr");
     if (hwaddr && eth_addr_from_string(hwaddr, ea)) {
         if (eth_addr_is_multicast(ea)) {
             VLOG_ERR("bridge %s: cannot set MAC address to multicast "
@@ -1603,8 +1583,7 @@ bridge_pick_datapath_id(struct bridge *br,
     const char *datapath_id;
     uint64_t dpid;
 
-    datapath_id = ovsrec_bridge_get_other_config_value(br->cfg, "datapath-id",
-                                                       NULL);
+    datapath_id = smap_get(&br->cfg->other_config, "datapath-id");
     if (datapath_id && dpid_from_string(datapath_id, &dpid)) {
         return dpid;
     }
@@ -1650,10 +1629,9 @@ dpid_from_hash(const void *data, size_t n)
 static void
 iface_refresh_status(struct iface *iface)
 {
-    struct shash sh;
+    struct smap smap;
 
     enum netdev_features current;
-    enum netdev_flags flags;
     int64_t bps;
     int mtu;
     int64_t mtu_64;
@@ -1663,31 +1641,15 @@ iface_refresh_status(struct iface *iface)
         return;
     }
 
-    shash_init(&sh);
-
-    if (!netdev_get_drv_info(iface->netdev, &sh)) {
-        size_t n;
-        char **keys, **values;
+    smap_init(&smap);
 
-        shash_to_ovs_idl_map(&sh, &keys, &values, &n);
-        ovsrec_interface_set_status(iface->cfg, keys, values, n);
-
-        free(keys);
-        free(values);
+    if (!netdev_get_drv_info(iface->netdev, &smap)) {
+        ovsrec_interface_set_status(iface->cfg, &smap);
     } else {
-        ovsrec_interface_set_status(iface->cfg, NULL, NULL, 0);
+        ovsrec_interface_set_status(iface->cfg, NULL);
     }
 
-    shash_destroy_free_data(&sh);
-
-    error = netdev_get_flags(iface->netdev, &flags);
-    if (!error) {
-        ovsrec_interface_set_admin_state(iface->cfg,
-                                         flags & NETDEV_UP ? "up" : "down");
-    }
-    else {
-        ovsrec_interface_set_admin_state(iface->cfg, NULL);
-    }
+    smap_destroy(&smap);
 
     error = netdev_get_features(iface->netdev, &current, NULL, NULL, NULL);
     if (!error) {
@@ -1713,20 +1675,17 @@ iface_refresh_status(struct iface *iface)
     }
 }
 
-/* Writes 'iface''s CFM statistics to the database. */
+/* Writes 'iface''s CFM statistics to the database. 'iface' must not be
+ * synthetic. */
 static void
 iface_refresh_cfm_stats(struct iface *iface)
 {
     const struct ovsrec_interface *cfg = iface->cfg;
-    int fault, error;
+    int fault, opup, error;
     const uint64_t *rmps;
     size_t n_rmps;
     int health;
 
-    if (iface_is_synthetic(iface)) {
-        return;
-    }
-
     fault = ofproto_port_get_cfm_fault(iface->port->bridge->ofproto,
                                        iface->ofp_port);
     if (fault >= 0) {
@@ -1749,6 +1708,14 @@ iface_refresh_cfm_stats(struct iface *iface)
         ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0);
     }
 
+    opup = ofproto_port_get_cfm_opup(iface->port->bridge->ofproto,
+                                     iface->ofp_port);
+    if (opup >= 0) {
+        ovsrec_interface_set_cfm_remote_opstate(cfg, opup ? "up" : "down");
+    } else {
+        ovsrec_interface_set_cfm_remote_opstate(cfg, NULL);
+    }
+
     error = ofproto_port_get_cfm_remote_mpids(iface->port->bridge->ofproto,
                                               iface->ofp_port, &rmps, &n_rmps);
     if (error >= 0) {
@@ -1816,32 +1783,27 @@ iface_refresh_stats(struct iface *iface)
 static void
 br_refresh_stp_status(struct bridge *br)
 {
+    struct smap smap = SMAP_INITIALIZER(&smap);
     struct ofproto *ofproto = br->ofproto;
     struct ofproto_stp_status status;
-    char *keys[3], *values[3];
-    size_t i;
 
     if (ofproto_get_stp_status(ofproto, &status)) {
         return;
     }
 
     if (!status.enabled) {
-        ovsrec_bridge_set_status(br->cfg, NULL, NULL, 0);
+        ovsrec_bridge_set_status(br->cfg, NULL);
         return;
     }
 
-    keys[0] = "stp_bridge_id",
-    values[0] = xasprintf(STP_ID_FMT, STP_ID_ARGS(status.bridge_id));
-    keys[1] = "stp_designated_root",
-    values[1] = xasprintf(STP_ID_FMT, STP_ID_ARGS(status.designated_root));
-    keys[2] = "stp_root_path_cost",
-    values[2] = xasprintf("%d", status.root_path_cost);
+    smap_add_format(&smap, "stp_bridge_id", STP_ID_FMT,
+                    STP_ID_ARGS(status.bridge_id));
+    smap_add_format(&smap, "stp_designated_root", STP_ID_FMT,
+                    STP_ID_ARGS(status.designated_root));
+    smap_add_format(&smap, "stp_root_path_cost", "%d", status.root_path_cost);
 
-    ovsrec_bridge_set_status(br->cfg, keys, values, ARRAY_SIZE(values));
-
-    for (i = 0; i < ARRAY_SIZE(values); i++) {
-        free(values[i]);
-    }
+    ovsrec_bridge_set_status(br->cfg, &smap);
+    smap_destroy(&smap);
 }
 
 static void
@@ -1850,10 +1812,9 @@ port_refresh_stp_status(struct port *port)
     struct ofproto *ofproto = port->bridge->ofproto;
     struct iface *iface;
     struct ofproto_port_stp_status status;
-    char *keys[4];
-    char *str_values[4];
+    char *keys[3];
     int64_t int_values[3];
-    size_t i;
+    struct smap smap;
 
     if (port_is_synthetic(port)) {
         return;
@@ -1861,7 +1822,7 @@ port_refresh_stp_status(struct port *port)
 
     /* STP doesn't currently support bonds. */
     if (!list_is_singleton(&port->ifaces)) {
-        ovsrec_port_set_status(port->cfg, NULL, NULL, 0);
+        ovsrec_port_set_status(port->cfg, NULL);
         return;
     }
 
@@ -1872,27 +1833,19 @@ port_refresh_stp_status(struct port *port)
     }
 
     if (!status.enabled) {
-        ovsrec_port_set_status(port->cfg, NULL, NULL, 0);
+        ovsrec_port_set_status(port->cfg, NULL);
         ovsrec_port_set_statistics(port->cfg, NULL, NULL, 0);
         return;
     }
 
     /* Set Status column. */
-    keys[0] = "stp_port_id";
-    str_values[0] = xasprintf(STP_PORT_ID_FMT, status.port_id);
-    keys[1] = "stp_state";
-    str_values[1] = xstrdup(stp_state_name(status.state));
-    keys[2] = "stp_sec_in_state";
-    str_values[2] = xasprintf("%u", status.sec_in_state);
-    keys[3] = "stp_role";
-    str_values[3] = xstrdup(stp_role_name(status.role));
-
-    ovsrec_port_set_status(port->cfg, keys, str_values,
-                           ARRAY_SIZE(str_values));
-
-    for (i = 0; i < ARRAY_SIZE(str_values); i++) {
-        free(str_values[i]);
-    }
+    smap_init(&smap);
+    smap_add_format(&smap, "stp_port_id", STP_PORT_ID_FMT, status.port_id);
+    smap_add(&smap, "stp_state", stp_state_name(status.state));
+    smap_add_format(&smap, "stp_sec_in_state", "%u", status.sec_in_state);
+    smap_add(&smap, "stp_role", stp_role_name(status.role));
+    ovsrec_port_set_status(port->cfg, &smap);
+    smap_destroy(&smap);
 
     /* Set Statistics column. */
     keys[0] = "stp_tx_count";
@@ -1909,34 +1862,40 @@ port_refresh_stp_status(struct port *port)
 static bool
 enable_system_stats(const struct ovsrec_open_vswitch *cfg)
 {
-    const char *enable;
+    return smap_get_bool(&cfg->other_config, "enable-statistics", false);
+}
 
-    /* Use other-config:enable-system-stats by preference. */
-    enable = ovsrec_open_vswitch_get_other_config_value(cfg,
-                                                        "enable-statistics",
-                                                        NULL);
-    if (enable) {
-        return !strcmp(enable, "true");
-    }
+static void
+reconfigure_system_stats(const struct ovsrec_open_vswitch *cfg)
+{
+    bool enable = enable_system_stats(cfg);
 
-    /* Disable by default. */
-    return false;
+    system_stats_enable(enable);
+    if (!enable) {
+        ovsrec_open_vswitch_set_statistics(cfg, NULL);
+    }
 }
 
 static void
-refresh_system_stats(const struct ovsrec_open_vswitch *cfg)
+run_system_stats(void)
 {
-    struct ovsdb_datum datum;
-    struct shash stats;
+    const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(idl);
+    struct smap *stats;
 
-    shash_init(&stats);
-    if (enable_system_stats(cfg)) {
-        get_system_stats(&stats);
-    }
+    stats = system_stats_run();
+    if (stats && cfg) {
+        struct ovsdb_idl_txn *txn;
+        struct ovsdb_datum datum;
 
-    ovsdb_datum_from_shash(&datum, &stats);
-    ovsdb_idl_txn_write(&cfg->header_, &ovsrec_open_vswitch_col_statistics,
-                        &datum);
+        txn = ovsdb_idl_txn_create(idl);
+        ovsdb_datum_from_smap(&datum, stats);
+        ovsdb_idl_txn_write(&cfg->header_, &ovsrec_open_vswitch_col_statistics,
+                            &datum);
+        ovsdb_idl_txn_commit(txn);
+        ovsdb_idl_txn_destroy(txn);
+
+        free(stats);
+    }
 }
 
 static inline const char *
@@ -1974,15 +1933,23 @@ refresh_controller_status(void)
             shash_find_data(&info, cfg->target);
 
         if (cinfo) {
+            struct smap smap = SMAP_INITIALIZER(&smap);
+            const char **values = cinfo->pairs.values;
+            const char **keys = cinfo->pairs.keys;
+            size_t i;
+
+            for (i = 0; i < cinfo->pairs.n; i++) {
+                smap_add(&smap, keys[i], values[i]);
+            }
+
             ovsrec_controller_set_is_connected(cfg, cinfo->is_connected);
             ovsrec_controller_set_role(cfg, nx_role_to_str(cinfo->role));
-            ovsrec_controller_set_status(cfg, (char **) cinfo->pairs.keys,
-                                         (char **) cinfo->pairs.values,
-                                         cinfo->pairs.n);
+            ovsrec_controller_set_status(cfg, &smap);
+            smap_destroy(&smap);
         } else {
             ovsrec_controller_set_is_connected(cfg, false);
             ovsrec_controller_set_role(cfg, NULL);
-            ovsrec_controller_set_status(cfg, NULL, NULL, 0);
+            ovsrec_controller_set_status(cfg, NULL);
         }
     }
 
@@ -1990,7 +1957,7 @@ refresh_controller_status(void)
 }
 
 static void
-refresh_cfm_stats(void)
+refresh_instant_stats(void)
 {
     static struct ovsdb_idl_txn *txn = NULL;
 
@@ -2001,8 +1968,47 @@ refresh_cfm_stats(void)
 
         HMAP_FOR_EACH (br, node, &all_bridges) {
             struct iface *iface;
+            struct port *port;
+
+            br_refresh_stp_status(br);
+
+            HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+                port_refresh_stp_status(port);
+            }
 
             HMAP_FOR_EACH (iface, name_node, &br->iface_by_name) {
+                enum netdev_flags flags;
+                const char *link_state;
+                int64_t link_resets;
+                int current, error;
+
+                if (iface_is_synthetic(iface)) {
+                    continue;
+                }
+
+                current = ofproto_port_is_lacp_current(br->ofproto,
+                                                       iface->ofp_port);
+                if (current >= 0) {
+                    bool bl = current;
+                    ovsrec_interface_set_lacp_current(iface->cfg, &bl, 1);
+                } else {
+                    ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0);
+                }
+
+                error = netdev_get_flags(iface->netdev, &flags);
+                if (!error) {
+                    const char *state = flags & NETDEV_UP ? "up" : "down";
+                    ovsrec_interface_set_admin_state(iface->cfg, state);
+                } else {
+                    ovsrec_interface_set_admin_state(iface->cfg, NULL);
+                }
+
+                link_state = netdev_get_carrier(iface->netdev) ? "up" : "down";
+                ovsrec_interface_set_link_state(iface->cfg, link_state);
+
+                link_resets = netdev_get_carrier_resets(iface->netdev);
+                ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1);
+
                 iface_refresh_cfm_stats(iface);
             }
         }
@@ -2040,6 +2046,8 @@ bridge_run(void)
     bool vlan_splinters_changed;
     struct bridge *br;
 
+    ovsrec_open_vswitch_init((struct ovsrec_open_vswitch *) &null_cfg);
+
     /* (Re)configure if necessary. */
     if (!reconfiguring) {
         ovsdb_idl_run(idl);
@@ -2124,8 +2132,8 @@ bridge_run(void)
         reconf_txn = NULL;
     }
 
-    /* Refresh system and interface stats if necessary. */
-    if (time_msec() >= stats_timer) {
+    /* Refresh interface and mirror stats if necessary. */
+    if (time_msec() >= iface_stats_timer) {
         if (cfg) {
             struct ovsdb_idl_txn *txn;
 
@@ -2148,62 +2156,16 @@ bridge_run(void)
                 }
 
             }
-            refresh_system_stats(cfg);
             refresh_controller_status();
             ovsdb_idl_txn_commit(txn);
             ovsdb_idl_txn_destroy(txn); /* XXX */
         }
 
-        stats_timer = time_msec() + STATS_INTERVAL;
+        iface_stats_timer = time_msec() + IFACE_STATS_INTERVAL;
     }
 
-    if (time_msec() >= db_limiter) {
-        struct ovsdb_idl_txn *txn;
-
-        txn = ovsdb_idl_txn_create(idl);
-        HMAP_FOR_EACH (br, node, &all_bridges) {
-            struct iface *iface;
-            struct port *port;
-
-            br_refresh_stp_status(br);
-
-            HMAP_FOR_EACH (port, hmap_node, &br->ports) {
-                port_refresh_stp_status(port);
-            }
-
-            HMAP_FOR_EACH (iface, name_node, &br->iface_by_name) {
-                const char *link_state;
-                int64_t link_resets;
-                int current;
-
-                if (iface_is_synthetic(iface)) {
-                    continue;
-                }
-
-                current = ofproto_port_is_lacp_current(br->ofproto,
-                                                       iface->ofp_port);
-                if (current >= 0) {
-                    bool bl = current;
-                    ovsrec_interface_set_lacp_current(iface->cfg, &bl, 1);
-                } else {
-                    ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0);
-                }
-
-                link_state = netdev_get_carrier(iface->netdev) ? "up" : "down";
-                ovsrec_interface_set_link_state(iface->cfg, link_state);
-
-                link_resets = netdev_get_carrier_resets(iface->netdev);
-                ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1);
-            }
-        }
-
-        if (ovsdb_idl_txn_commit(txn) != TXN_UNCHANGED) {
-            db_limiter = time_msec() + DB_LIMIT_INTERVAL;
-        }
-        ovsdb_idl_txn_destroy(txn);
-    }
-
-    refresh_cfm_stats();
+    run_system_stats();
+    refresh_instant_stats();
 }
 
 void
@@ -2221,11 +2183,21 @@ bridge_wait(void)
         HMAP_FOR_EACH (br, node, &all_bridges) {
             ofproto_wait(br->ofproto);
         }
-        poll_timer_wait_until(stats_timer);
+        poll_timer_wait_until(iface_stats_timer);
+    }
 
-        if (db_limiter > time_msec()) {
-            poll_timer_wait_until(db_limiter);
-        }
+    system_stats_wait();
+}
+
+/* Adds some memory usage statistics for bridges into 'usage', for use with
+ * memory_report(). */
+void
+bridge_get_memory_usage(struct simap *usage)
+{
+    struct bridge *br;
+
+    HMAP_FOR_EACH (br, node, &all_bridges) {
+        ofproto_get_memory_usage(br->ofproto, usage);
     }
 }
 \f
@@ -2238,14 +2210,14 @@ struct qos_unixctl_show_cbdata {
 
 static void
 qos_unixctl_show_cb(unsigned int queue_id,
-                    const struct shash *details,
+                    const struct smap *details,
                     void *aux)
 {
     struct qos_unixctl_show_cbdata *data = aux;
     struct ds *ds = data->ds;
     struct iface *iface = data->iface;
     struct netdev_queue_stats stats;
-    struct shash_node *node;
+    struct smap_node *node;
     int error;
 
     ds_put_cstr(ds, "\n");
@@ -2255,8 +2227,8 @@ qos_unixctl_show_cb(unsigned int queue_id,
         ds_put_cstr(ds, "Default:\n");
     }
 
-    SHASH_FOR_EACH (node, details) {
-        ds_put_format(ds, "\t%s: %s\n", node->name, (char *)node->data);
+    SMAP_FOR_EACH (node, details) {
+        ds_put_format(ds, "\t%s: %s\n", node->key, node->value);
     }
 
     error = netdev_get_queue_stats(iface->netdev, queue_id, &stats);
@@ -2283,10 +2255,10 @@ qos_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
                  const char *argv[], void *aux OVS_UNUSED)
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
-    struct shash sh = SHASH_INITIALIZER(&sh);
+    struct smap smap = SMAP_INITIALIZER(&smap);
     struct iface *iface;
     const char *type;
-    struct shash_node *node;
+    struct smap_node *node;
     struct qos_unixctl_show_cbdata data;
     int error;
 
@@ -2296,13 +2268,13 @@ qos_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
         return;
     }
 
-    netdev_get_qos(iface->netdev, &type, &sh);
+    netdev_get_qos(iface->netdev, &type, &smap);
 
     if (*type != '\0') {
         ds_put_format(&ds, "QoS: %s %s\n", iface->name, type);
 
-        SHASH_FOR_EACH (node, &sh) {
-            ds_put_format(&ds, "%s: %s\n", node->name, (char *)node->data);
+        SMAP_FOR_EACH (node, &smap) {
+            ds_put_format(&ds, "%s: %s\n", node->key, node->value);
         }
 
         data.ds = &ds;
@@ -2318,7 +2290,7 @@ qos_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
         unixctl_command_reply_error(conn, ds_cstr(&ds));
     }
 
-    shash_destroy_free_data(&sh);
+    smap_destroy(&smap);
     ds_destroy(&ds);
 }
 \f
@@ -2509,6 +2481,9 @@ bridge_add_del_ports(struct bridge *br,
         VLOG_WARN("bridge %s: no port named %s, synthesizing one",
                   br->name, br->name);
 
+        ovsrec_interface_init(&br->synth_local_iface);
+        ovsrec_port_init(&br->synth_local_port);
+
         br->synth_local_port.interfaces = &br->synth_local_ifacep;
         br->synth_local_port.n_interfaces = 1;
         br->synth_local_port.name = br->name;
@@ -2545,10 +2520,16 @@ bridge_add_del_ports(struct bridge *br,
         for (i = 0; i < port->n_interfaces; i++) {
             const struct ovsrec_interface *cfg = port->interfaces[i];
             struct iface *iface = iface_lookup(br, cfg->name);
+            const char *type = iface_get_type(cfg, br->cfg);
 
             if (iface) {
                 iface->cfg = cfg;
-                iface->type = iface_get_type(cfg, br->cfg);
+                iface->type = type;
+            } else if (!strcmp(type, "null")) {
+                VLOG_WARN_ONCE("%s: The null interface type is deprecated and"
+                               " may be removed in February 2013. Please email"
+                               " dev@openvswitch.org with concerns.",
+                               cfg->name);
             } else {
                 bridge_queue_if_cfg(br, cfg, port);
             }
@@ -2580,7 +2561,7 @@ static void
 bridge_ofproto_controller_from_ovsrec(const struct ovsrec_controller *c,
                                       struct ofproto_controller *oc)
 {
-    const char *config_str;
+    int dscp;
 
     oc->target = c->target;
     oc->max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8;
@@ -2592,16 +2573,11 @@ bridge_ofproto_controller_from_ovsrec(const struct ovsrec_controller *c,
                        ? *c->controller_burst_limit : 0);
     oc->enable_async_msgs = (!c->enable_async_messages
                              || *c->enable_async_messages);
-    config_str = ovsrec_controller_get_other_config_value(c, "dscp", NULL);
-
-    oc->dscp = DSCP_DEFAULT;
-    if (config_str) {
-        int dscp = atoi(config_str);
-
-        if (dscp >= 0 && dscp <= 63) {
-            oc->dscp = dscp;
-        }
+    dscp = smap_get_int(&c->other_config, "dscp", DSCP_DEFAULT);
+    if (dscp < 0 || dscp > 63) {
+        dscp = DSCP_DEFAULT;
     }
+    oc->dscp = dscp;
 }
 
 /* Configures the IP stack for 'br''s local interface properly according to the
@@ -2672,9 +2648,7 @@ static void
 bridge_configure_remotes(struct bridge *br,
                          const struct sockaddr_in *managers, size_t n_managers)
 {
-    const char *disable_ib_str, *queue_id_str;
-    bool disable_in_band = false;
-    int queue_id;
+    bool disable_in_band;
 
     struct ovsrec_controller **controllers;
     size_t n_controllers;
@@ -2686,19 +2660,13 @@ bridge_configure_remotes(struct bridge *br,
     size_t i;
 
     /* Check if we should disable in-band control on this bridge. */
-    disable_ib_str = ovsrec_bridge_get_other_config_value(br->cfg,
-                                                          "disable-in-band",
-                                                          NULL);
-    if (disable_ib_str && !strcmp(disable_ib_str, "true")) {
-        disable_in_band = true;
-    }
+    disable_in_band = smap_get_bool(&br->cfg->other_config, "disable-in-band",
+                                    false);
 
     /* Set OpenFlow queue ID for in-band control. */
-    queue_id_str = ovsrec_bridge_get_other_config_value(br->cfg,
-                                                        "in-band-queue",
-                                                        NULL);
-    queue_id = queue_id_str ? strtol(queue_id_str, NULL, 10) : -1;
-    ofproto_set_in_band_queue(br->ofproto, queue_id);
+    ofproto_set_in_band_queue(br->ofproto,
+                              smap_get_int(&br->cfg->other_config,
+                                           "in-band-queue", -1));
 
     if (disable_in_band) {
         ofproto_set_extra_in_band_remotes(br->ofproto, NULL, 0);
@@ -2945,8 +2913,7 @@ port_configure_lacp(struct port *port, struct lacp_settings *s)
 
     s->name = port->name;
 
-    system_id = ovsrec_port_get_other_config_value(port->cfg, "lacp-system-id",
-                                                   NULL);
+    system_id = smap_get(&port->cfg->other_config, "lacp-system-id");
     if (system_id) {
         if (sscanf(system_id, ETH_ADDR_SCAN_FMT,
                    ETH_ADDR_SCAN_ARGS(s->id)) != ETH_ADDR_SCAN_COUNT) {
@@ -2964,16 +2931,14 @@ port_configure_lacp(struct port *port, struct lacp_settings *s)
     }
 
     /* Prefer bondable links if unspecified. */
-    priority = atoi(ovsrec_port_get_other_config_value(port->cfg,
-                                                       "lacp-system-priority",
-                                                       "0"));
+    priority = smap_get_int(&port->cfg->other_config, "lacp-system-priority",
+                            0);
     s->priority = (priority > 0 && priority <= UINT16_MAX
                    ? priority
                    : UINT16_MAX - !list_is_short(&port->ifaces));
 
-    lacp_time = ovsrec_port_get_other_config_value(port->cfg, "lacp-time",
-                                                   "slow");
-    s->fast = !strcasecmp(lacp_time, "fast");
+    lacp_time = smap_get(&port->cfg->other_config, "lacp-time");
+    s->fast = lacp_time && !strcasecmp(lacp_time, "fast");
     return s;
 }
 
@@ -2982,16 +2947,10 @@ iface_configure_lacp(struct iface *iface, struct lacp_slave_settings *s)
 {
     int priority, portid, key;
 
-    portid = atoi(ovsrec_interface_get_other_config_value(iface->cfg,
-                                                          "lacp-port-id",
-                                                          "0"));
-    priority =
-        atoi(ovsrec_interface_get_other_config_value(iface->cfg,
-                                                     "lacp-port-priority",
-                                                     "0"));
-    key = atoi(ovsrec_interface_get_other_config_value(iface->cfg,
-                                                       "lacp-aggregation-key",
-                                                       "0"));
+    portid = smap_get_int(&iface->cfg->other_config, "lacp-port-id", 0);
+    priority = smap_get_int(&iface->cfg->other_config, "lacp-port-priority",
+                            0);
+    key = smap_get_int(&iface->cfg->other_config, "lacp-aggregation-key", 0);
 
     if (portid <= 0 || portid > UINT16_MAX) {
         portid = iface->ofp_port;
@@ -3044,17 +3003,14 @@ port_configure_bond(struct port *port, struct bond_settings *s,
                   port->name);
     }
 
-    miimon_interval =
-        atoi(ovsrec_port_get_other_config_value(port->cfg,
-                                                "bond-miimon-interval", "0"));
+    miimon_interval = smap_get_int(&port->cfg->other_config,
+                                   "bond-miimon-interval", 0);
     if (miimon_interval <= 0) {
         miimon_interval = 200;
     }
 
-    detect_s = ovsrec_port_get_other_config_value(port->cfg,
-                                                  "bond-detect-mode",
-                                                  "carrier");
-    if (!strcmp(detect_s, "carrier")) {
+    detect_s = smap_get(&port->cfg->other_config, "bond-detect-mode");
+    if (!detect_s || !strcmp(detect_s, "carrier")) {
         miimon_interval = 0;
     } else if (strcmp(detect_s, "miimon")) {
         VLOG_WARN("port %s: unsupported bond-detect-mode %s, "
@@ -3064,13 +3020,9 @@ port_configure_bond(struct port *port, struct bond_settings *s,
 
     s->up_delay = MAX(0, port->cfg->bond_updelay);
     s->down_delay = MAX(0, port->cfg->bond_downdelay);
-    s->basis = atoi(ovsrec_port_get_other_config_value(port->cfg,
-                                                       "bond-hash-basis",
-                                                       "0"));
-    s->rebalance_interval = atoi(
-        ovsrec_port_get_other_config_value(port->cfg,
-                                           "bond-rebalance-interval",
-                                           "10000"));
+    s->basis = smap_get_int(&port->cfg->other_config, "bond-hash-basis", 0);
+    s->rebalance_interval = smap_get_int(&port->cfg->other_config,
+                                           "bond-rebalance-interval", 10000);
     if (s->rebalance_interval && s->rebalance_interval < 1000) {
         s->rebalance_interval = 1000;
     }
@@ -3081,10 +3033,8 @@ port_configure_bond(struct port *port, struct bond_settings *s,
     LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
         long long stable_id;
 
-        stable_id =
-            atoll(ovsrec_interface_get_other_config_value(iface->cfg,
-                                                          "bond-stable-id",
-                                                          "0"));
+        stable_id = smap_get_int(&iface->cfg->other_config, "bond-stable-id",
+                                 0);
         if (stable_id <= 0 || stable_id >= UINT32_MAX) {
             stable_id = iface->ofp_port;
         }
@@ -3246,7 +3196,7 @@ iface_clear_db_record(const struct ovsrec_interface *if_cfg)
 {
     if (!ovsdb_idl_row_is_synthetic(&if_cfg->header_)) {
         iface_set_ofport(if_cfg, -1);
-        ovsrec_interface_set_status(if_cfg, NULL, NULL, 0);
+        ovsrec_interface_set_status(if_cfg, NULL);
         ovsrec_interface_set_admin_state(if_cfg, NULL);
         ovsrec_interface_set_duplex(if_cfg, NULL);
         ovsrec_interface_set_link_speed(if_cfg, NULL, 0);
@@ -3260,54 +3210,6 @@ iface_clear_db_record(const struct ovsrec_interface *if_cfg)
     }
 }
 
-/* Adds the 'n' key-value pairs in 'keys' in 'values' to 'shash'.
- *
- * The value strings in '*shash' are taken directly from values[], not copied,
- * so the caller should not modify or free them. */
-static void
-shash_from_ovs_idl_map(char **keys, char **values, size_t n,
-                       struct shash *shash)
-{
-    size_t i;
-
-    shash_init(shash);
-    for (i = 0; i < n; i++) {
-        shash_add(shash, keys[i], values[i]);
-    }
-}
-
-/* Creates 'keys' and 'values' arrays from 'shash'.
- *
- * Sets 'keys' and 'values' to heap allocated arrays representing the key-value
- * pairs in 'shash'.  The caller takes ownership of 'keys' and 'values'.  They
- * are populated with with strings taken directly from 'shash' and thus have
- * the same ownership of the key-value pairs in shash.
- */
-static void
-shash_to_ovs_idl_map(struct shash *shash,
-                     char ***keys, char ***values, size_t *n)
-{
-    size_t i, count;
-    char **k, **v;
-    struct shash_node *sn;
-
-    count = shash_count(shash);
-
-    k = xmalloc(count * sizeof *k);
-    v = xmalloc(count * sizeof *v);
-
-    i = 0;
-    SHASH_FOR_EACH(sn, shash) {
-        k[i] = sn->name;
-        v[i] = sn->data;
-        i++;
-    }
-
-    *n      = count;
-    *keys   = k;
-    *values = v;
-}
-
 struct iface_delete_queues_cbdata {
     struct netdev *netdev;
     const struct ovsdb_datum *queues;
@@ -3324,7 +3226,7 @@ queue_ids_include(const struct ovsdb_datum *queues, int64_t target)
 
 static void
 iface_delete_queues(unsigned int queue_id,
-                    const struct shash *details OVS_UNUSED, void *cbdata_)
+                    const struct smap *details OVS_UNUSED, void *cbdata_)
 {
     struct iface_delete_queues_cbdata *cbdata = cbdata_;
 
@@ -3344,15 +3246,11 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
         netdev_set_qos(iface->netdev, NULL, NULL);
     } else {
         struct iface_delete_queues_cbdata cbdata;
-        struct shash details;
         bool queue_zero;
         size_t i;
 
         /* Configure top-level Qos for 'iface'. */
-        shash_from_ovs_idl_map(qos->key_other_config, qos->value_other_config,
-                               qos->n_other_config, &details);
-        netdev_set_qos(iface->netdev, qos->type, &details);
-        shash_destroy(&details);
+        netdev_set_qos(iface->netdev, qos->type, &qos->other_config);
 
         /* Deconfigure queues that were deleted. */
         cbdata.netdev = iface->netdev;
@@ -3379,16 +3277,14 @@ iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos)
                 port_queue->dscp = queue->dscp[0];
             }
 
-            shash_from_ovs_idl_map(queue->key_other_config,
-                                   queue->value_other_config,
-                                   queue->n_other_config, &details);
-            netdev_set_queue(iface->netdev, queue_id, &details);
-            shash_destroy(&details);
+            netdev_set_queue(iface->netdev, queue_id, &queue->other_config);
         }
         if (!queue_zero) {
-            shash_init(&details);
+            struct smap details;
+
+            smap_init(&details);
             netdev_set_queue(iface->netdev, 0, &details);
-            shash_destroy(&details);
+            smap_destroy(&details);
         }
     }
 
@@ -3411,7 +3307,7 @@ static void
 iface_configure_cfm(struct iface *iface)
 {
     const struct ovsrec_interface *cfg = iface->cfg;
-    const char *extended_str, *opstate_str;
+    const char *opstate_str;
     const char *cfm_ccm_vlan;
     struct cfm_settings s;
 
@@ -3421,20 +3317,17 @@ iface_configure_cfm(struct iface *iface)
     }
 
     s.mpid = *cfg->cfm_mpid;
-    s.interval = atoi(ovsrec_interface_get_other_config_value(iface->cfg,
-                                                              "cfm_interval",
-                                                              "0"));
-    cfm_ccm_vlan = ovsrec_interface_get_other_config_value(iface->cfg,
-                                                           "cfm_ccm_vlan",
-                                                           "0");
-    s.ccm_pcp = atoi(ovsrec_interface_get_other_config_value(iface->cfg,
-                                                             "cfm_ccm_pcp",
-                                                             "0"));
+    s.interval = smap_get_int(&iface->cfg->other_config, "cfm_interval", 0);
+    cfm_ccm_vlan = smap_get(&iface->cfg->other_config, "cfm_ccm_vlan");
+    s.ccm_pcp = smap_get_int(&iface->cfg->other_config, "cfm_ccm_pcp", 0);
+
     if (s.interval <= 0) {
         s.interval = 1000;
     }
 
-    if (!strcasecmp("random", cfm_ccm_vlan)) {
+    if (!cfm_ccm_vlan) {
+        s.ccm_vlan = 0;
+    } else if (!strcasecmp("random", cfm_ccm_vlan)) {
         s.ccm_vlan = CFM_RANDOM_VLAN;
     } else {
         s.ccm_vlan = atoi(cfm_ccm_vlan);
@@ -3443,15 +3336,11 @@ iface_configure_cfm(struct iface *iface)
         }
     }
 
-    extended_str = ovsrec_interface_get_other_config_value(iface->cfg,
-                                                           "cfm_extended",
-                                                           "false");
-    s.extended = !strcasecmp("true", extended_str);
+    s.extended = smap_get_bool(&iface->cfg->other_config, "cfm_extended",
+                               false);
 
-    opstate_str = ovsrec_interface_get_other_config_value(iface->cfg,
-                                                          "cfm_opstate",
-                                                          "up");
-    s.opup = !strcasecmp("up", opstate_str);
+    opstate_str = smap_get(&iface->cfg->other_config, "cfm_opstate");
+    s.opup = !opstate_str || !strcasecmp("up", opstate_str);
 
     ofproto_port_set_cfm(iface->port->bridge->ofproto, iface->ofp_port, &s);
 }
@@ -3659,30 +3548,43 @@ mirror_configure(struct mirror *m)
  * devices are not used.  When broken device drivers are no longer in
  * widespread use, we will delete these interfaces. */
 
-static void **blocks;
-static size_t n_blocks, allocated_blocks;
+static struct ovsrec_port **recs;
+static size_t n_recs, allocated_recs;
 
-/* Adds 'block' to a list of blocks that have to be freed with free() when the
- * VLAN splinters are reconfigured. */
+/* Adds 'rec' to a list of recs that have to be destroyed when the VLAN
+ * splinters are reconfigured. */
 static void
-register_block(void *block)
+register_rec(struct ovsrec_port *rec)
 {
-    if (n_blocks >= allocated_blocks) {
-        blocks = x2nrealloc(blocks, &allocated_blocks, sizeof *blocks);
+    if (n_recs >= allocated_recs) {
+        recs = x2nrealloc(recs, &allocated_recs, sizeof *recs);
     }
-    blocks[n_blocks++] = block;
+    recs[n_recs++] = rec;
 }
 
-/* Frees all of the blocks registered with register_block(). */
+/* Frees all of the ports registered with register_reg(). */
 static void
-free_registered_blocks(void)
+free_registered_recs(void)
 {
     size_t i;
 
-    for (i = 0; i < n_blocks; i++) {
-        free(blocks[i]);
+    for (i = 0; i < n_recs; i++) {
+        struct ovsrec_port *port = recs[i];
+        size_t j;
+
+        for (j = 0; j < port->n_interfaces; j++) {
+            struct ovsrec_interface *iface = port->interfaces[j];
+            free(iface->name);
+            free(iface);
+        }
+
+        smap_destroy(&port->other_config);
+        free(port->interfaces);
+        free(port->name);
+        free(port->tag);
+        free(port);
     }
-    n_blocks = 0;
+    n_recs = 0;
 }
 
 /* Returns true if VLAN splinters are enabled on 'iface_cfg', false
@@ -3690,12 +3592,8 @@ free_registered_blocks(void)
 static bool
 vlan_splinters_is_enabled(const struct ovsrec_interface *iface_cfg)
 {
-    const char *value;
-
-    value = ovsrec_interface_get_other_config_value(iface_cfg,
-                                                    "enable-vlan-splinters",
-                                                    "");
-    return !strcmp(value, "true");
+    return smap_get_bool(&iface_cfg->other_config, "enable-vlan-splinters",
+                         false);
 }
 
 /* Figures out the set of VLANs that are in use for the purpose of VLAN
@@ -3723,7 +3621,7 @@ collect_splinter_vlans(const struct ovsrec_open_vswitch *ovs_cfg)
 
     /* Free space allocated for synthesized ports and interfaces, since we're
      * in the process of reconstructing all of them. */
-    free_registered_blocks();
+    free_registered_recs();
 
     splinter_vlans = bitmap_allocate(4096);
     sset_init(&splinter_ifaces);
@@ -3839,8 +3737,7 @@ configure_splinter_port(struct port *port)
     vlandev = CONTAINER_OF(list_front(&port->ifaces), struct iface,
                            port_elem);
 
-    realdev_name = ovsrec_port_get_other_config_value(port->cfg,
-                                                      "realdev", NULL);
+    realdev_name = smap_get(&port->cfg->other_config, "realdev");
     realdev = iface_lookup(port->bridge, realdev_name);
     realdev_ofp_port = realdev ? realdev->ofp_port : 0;
 
@@ -3855,33 +3752,23 @@ synthesize_splinter_port(const char *real_dev_name,
     struct ovsrec_interface *iface;
     struct ovsrec_port *port;
 
-    iface = xzalloc(sizeof *iface);
+    iface = xmalloc(sizeof *iface);
+    ovsrec_interface_init(iface);
     iface->name = xstrdup(vlan_dev_name);
     iface->type = "system";
 
-    port = xzalloc(sizeof *port);
+    port = xmalloc(sizeof *port);
+    ovsrec_port_init(port);
     port->interfaces = xmemdup(&iface, sizeof iface);
     port->n_interfaces = 1;
     port->name = xstrdup(vlan_dev_name);
     port->vlan_mode = "splinter";
     port->tag = xmalloc(sizeof *port->tag);
     *port->tag = vid;
-    port->key_other_config = xmalloc(sizeof *port->key_other_config);
-    port->key_other_config[0] = "realdev";
-    port->value_other_config = xmalloc(sizeof *port->value_other_config);
-    port->value_other_config[0] = xstrdup(real_dev_name);
-    port->n_other_config = 1;
-
-    register_block(iface);
-    register_block(iface->name);
-    register_block(port);
-    register_block(port->interfaces);
-    register_block(port->name);
-    register_block(port->tag);
-    register_block(port->key_other_config);
-    register_block(port->value_other_config);
-    register_block(port->value_other_config[0]);
 
+    smap_add(&port->other_config, "realdev", real_dev_name);
+
+    register_rec(port);
     return port;
 }
 
index 2d38833..c1b0a2b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
 #ifndef VSWITCHD_BRIDGE_H
 #define VSWITCHD_BRIDGE_H 1
 
+struct simap;
+
 void bridge_init(const char *remote);
 void bridge_exit(void);
 
@@ -23,4 +25,6 @@ void bridge_run(void);
 void bridge_run_fast(void);
 void bridge_wait(void);
 
+void bridge_get_memory_usage(struct simap *usage);
+
 #endif /* bridge.h */
index 8b1ad5d..df9332f 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -76,7 +76,7 @@ static char *appctl_program;
 static char *vsctl_program;
 
 /* Options that we should generally pass to ovs-vsctl. */
-#define VSCTL_OPTIONS "--timeout=5", "-vANY:console:WARN"
+#define VSCTL_OPTIONS "--timeout=5", "-vconsole:warn"
 
 /* Netlink socket to bridge compatibility kernel module. */
 static struct nl_sock *brc_sock;
@@ -280,25 +280,25 @@ parse_command(struct ofpbuf *buffer, uint32_t *seq, const char **br_name,
     return 0;
 }
 
-/* Composes and returns a reply to a request made by the datapath with Netlink
- * sequence number 'seq' and error code 'error'.  The caller may add additional
- * attributes to the message, then it may send it with send_reply(). */
+/* Composes and returns a reply to a request made by the datapath with error
+ * code 'error'.  The caller may add additional attributes to the message, then
+ * it may send it with send_reply(). */
 static struct ofpbuf *
-compose_reply(uint32_t seq, int error)
+compose_reply(int error)
 {
     struct ofpbuf *reply = ofpbuf_new(4096);
     nl_msg_put_genlmsghdr(reply, 32, brc_family, NLM_F_REQUEST,
                           BRC_GENL_C_DP_RESULT, 1);
-    ((struct nlmsghdr *) reply->data)->nlmsg_seq = seq;
     nl_msg_put_u32(reply, BRC_GENL_A_ERR_CODE, error);
     return reply;
 }
 
-/* Sends 'reply' to the datapath and frees it. */
+/* Sends 'reply' to the datapath, using sequence number 'nlmsg_seq', and frees
+ * it. */
 static void
-send_reply(struct ofpbuf *reply)
+send_reply(struct ofpbuf *reply, uint32_t nlmsg_seq)
 {
-    int retval = nl_sock_send(brc_sock, reply, false);
+    int retval = nl_sock_send_seq(brc_sock, reply, nlmsg_seq, false);
     if (retval) {
         VLOG_WARN_RL(&rl, "replying to brcompat request: %s",
                      strerror(retval));
@@ -311,7 +311,7 @@ send_reply(struct ofpbuf *reply)
 static void
 send_simple_reply(uint32_t seq, int error)
 {
-    send_reply(compose_reply(seq, error));
+    send_reply(compose_reply(error), seq);
 }
 
 static int
@@ -555,10 +555,10 @@ handle_fdb_query_cmd(struct ofpbuf *buffer)
     free(output);
 
     /* Compose and send reply to datapath. */
-    reply = compose_reply(seq, 0);
+    reply = compose_reply(0);
     nl_msg_put_unspec(reply, BRC_GENL_A_FDB_DATA,
                       query_data.data, query_data.size);
-    send_reply(reply);
+    send_reply(reply, seq);
 
     /* Free memory. */
     ofpbuf_uninit(&query_data);
@@ -594,10 +594,10 @@ send_ifindex_reply(uint32_t seq, char *output)
     }
 
     /* Compose and send reply. */
-    reply = compose_reply(seq, 0);
+    reply = compose_reply(0);
     nl_msg_put_unspec(reply, BRC_GENL_A_IFINDEXES,
                       indices, n_indices * sizeof *indices);
-    send_reply(reply);
+    send_reply(reply, seq);
 
     /* Free memory. */
     free(indices);
index 6c9f3e4..5a959dd 100644 (file)
@@ -206,7 +206,51 @@ enabled.
 .
 .so ofproto/ofproto-unixctl.man
 .so lib/vlog-unixctl.man
+.so lib/memory-unixctl.man
 .so lib/stress-unixctl.man
+.
+.SH "LIMITS"
+.
+.PP
+We believe these limits to be accurate as of this writing.  These
+limits assume the use of the Linux kernel datapath.
+.
+.IP \(bu
+Approximately 256 bridges given the allowance of 5,000 file
+descriptors that \fBovs\-ctl\fR(8) configures.  (\fBovs\-vswitchd\fR
+requires 17 file descriptors per datapath.)
+.
+.IP \(bu
+65,280 ports per bridge.  Performance will degrade beyond 1,024 ports
+per bridge due to fixed hash table sizing.
+.
+.IP \(bu
+2,048 MAC learning entries per bridge.
+.
+.IP \(bu
+Kernel flows are limited only by memory available to the kernel.
+Performance will degrade beyond 1,048,576 kernel flows per bridge with
+a 32-bit kernel, beyond 262,144 with a 64-bit kernel.
+(\fBovs\-vswitchd\fR should never install anywhere near that many
+flows.)
+.
+.IP \(bu
+OpenFlow flows are limited only by available memory.  Performance is
+linear in the number of unique wildcard patterns.  That is, an
+OpenFlow table that contains many flows that all match on the same
+fields in the same way has a constant-time lookup, but a table that
+contains many flows that match on different fields requires lookup
+time linear in the number of flows.
+.
+.IP \(bu
+255 ports per bridge participating in 802.1D Spanning Tree Protocol.
+.
+.IP \(bu
+32 mirrors per bridge.
+.
+.IP \(bu
+15 bytes for the name of a port.  (This is a Linux kernel limitation.)
+.
 .SH "SEE ALSO"
 .BR ovs\-appctl (8),
 .BR ovs\-brcompatd (8),
index f7ddee1..84e5ad1 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "dpif.h"
 #include "dummy.h"
 #include "leak-checker.h"
+#include "memory.h"
 #include "netdev.h"
 #include "openflow/openflow.h"
 #include "ovsdb-idl.h"
 #include "poll-loop.h"
 #include "process.h"
 #include "signals.h"
+#include "simap.h"
 #include "stream-ssl.h"
 #include "stream.h"
 #include "stress.h"
 #include "vconn.h"
 #include "vlog.h"
 #include "lib/vswitch-idl.h"
+#include "worker.h"
 
 VLOG_DEFINE_THIS_MODULE(vswitchd);
 
+/* --mlockall: If set, locks all process memory into physical RAM, preventing
+ * the kernel from paging any of its memory to disk. */
+static bool want_mlockall;
+
 static unixctl_cb_func ovs_vswitchd_exit;
 
 static char *parse_options(int argc, char *argv[], char **unixctl_path);
@@ -79,6 +86,18 @@ main(int argc, char *argv[])
 
     daemonize_start();
 
+    if (want_mlockall) {
+#ifdef HAVE_MLOCKALL
+        if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
+            VLOG_ERR("mlockall failed: %s", strerror(errno));
+        }
+#else
+        VLOG_ERR("mlockall not supported on this system");
+#endif
+    }
+
+    worker_start();
+
     retval = unixctl_server_create(unixctl_path, &unixctl);
     if (retval) {
         exit(EXIT_FAILURE);
@@ -90,16 +109,28 @@ main(int argc, char *argv[])
 
     exiting = false;
     while (!exiting) {
+        worker_run();
         if (signal_poll(sighup)) {
             vlog_reopen_log_file();
         }
+        memory_run();
+        if (memory_should_report()) {
+            struct simap usage;
+
+            simap_init(&usage);
+            bridge_get_memory_usage(&usage);
+            memory_report(&usage);
+            simap_destroy(&usage);
+        }
         bridge_run_fast();
         bridge_run();
         bridge_run_fast();
         unixctl_server_run(unixctl);
         netdev_run();
 
+        worker_wait();
         signal_wait(sighup);
+        memory_wait();
         bridge_wait();
         unixctl_server_wait(unixctl);
         netdev_wait();
@@ -163,13 +194,7 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
             exit(EXIT_SUCCESS);
 
         case OPT_MLOCKALL:
-#ifdef HAVE_MLOCKALL
-            if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
-                VLOG_ERR("mlockall failed: %s", strerror(errno));
-            }
-#else
-            VLOG_ERR("mlockall not supported on this system");
-#endif
+            want_mlockall = true;
             break;
 
         case OPT_UNIXCTL:
index a3b4db4..e0937a3 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010 Nicira Networks
+/* Copyright (c) 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
 
 #include <config.h>
 
+#include "system-stats.h"
+
 #include <assert.h>
 #include <ctype.h>
 #include <dirent.h>
 #include "daemon.h"
 #include "dirs.h"
 #include "dynamic-string.h"
+#include "json.h"
+#include "ofpbuf.h"
+#include "poll-loop.h"
 #include "shash.h"
-#include "system-stats.h"
+#include "smap.h"
 #include "timeval.h"
 #include "vlog.h"
+#include "worker.h"
 
 VLOG_DEFINE_THIS_MODULE(system_stats);
 
@@ -52,24 +58,23 @@ VLOG_DEFINE_THIS_MODULE(system_stats);
 #endif
 
 static void
-get_cpu_cores(struct shash *stats)
+get_cpu_cores(struct smap *stats)
 {
     long int n_cores = sysconf(_SC_NPROCESSORS_ONLN);
     if (n_cores > 0) {
-        shash_add(stats, "cpu", xasprintf("%ld", n_cores));
+        smap_add_format(stats, "cpu", "%ld", n_cores);
     }
 }
 
 static void
-get_load_average(struct shash *stats OVS_UNUSED)
+get_load_average(struct smap *stats OVS_UNUSED)
 {
 #if HAVE_GETLOADAVG
     double loadavg[3];
 
     if (getloadavg(loadavg, 3) == 3) {
-        shash_add(stats, "load_average",
-                  xasprintf("%.2f,%.2f,%.2f",
-                            loadavg[0], loadavg[1], loadavg[2]));
+        smap_add_format(stats, "load_average", "%.2f,%.2f,%.2f",
+                        loadavg[0], loadavg[1], loadavg[2]);
     }
 #endif
 }
@@ -90,7 +95,7 @@ get_page_size(void)
 }
 
 static void
-get_memory_stats(struct shash *stats)
+get_memory_stats(struct smap *stats)
 {
     if (!LINUX) {
         unsigned int pagesize = get_page_size();
@@ -108,7 +113,7 @@ get_memory_stats(struct shash *stats)
 
         mem_total = phys_pages * (pagesize / 1024);
         mem_used = (phys_pages - avphys_pages) * (pagesize / 1024);
-        shash_add(stats, "memory", xasprintf("%d,%d", mem_total, mem_used));
+        smap_add_format(stats, "memory", "%d,%d", mem_total, mem_used);
     } else {
         static const char file_name[] = "/proc/meminfo";
         int mem_used, mem_cache, swap_used;
@@ -152,9 +157,8 @@ get_memory_stats(struct shash *stats)
         mem_used = mem_total - mem_free;
         mem_cache = buffers + cached;
         swap_used = swap_total - swap_free;
-        shash_add(stats, "memory",
-                  xasprintf("%d,%d,%d,%d,%d", mem_total, mem_used, mem_cache,
-                            swap_total, swap_used));
+        smap_add_format(stats, "memory", "%d,%d,%d,%d,%d",
+                        mem_total, mem_used, mem_cache, swap_total, swap_used);
     }
 }
 
@@ -385,7 +389,7 @@ get_process_info(pid_t pid, struct process_info *pinfo)
 }
 
 static void
-get_process_stats(struct shash *stats)
+get_process_stats(struct smap *stats)
 {
     struct dirent *de;
     DIR *dir;
@@ -398,9 +402,9 @@ get_process_stats(struct shash *stats)
 
     while ((de = readdir(dir)) != NULL) {
         struct process_info pinfo;
-        char *key, *value;
         char *file_name;
         char *extension;
+        char *key;
         pid_t pid;
 
 #ifdef _DIRENT_HAVE_D_TYPE
@@ -423,27 +427,23 @@ get_process_stats(struct shash *stats)
 
         key = xasprintf("process_%.*s",
                         (int) (extension - de->d_name), de->d_name);
-        if (shash_find(stats, key)) {
-            free(key);
-            continue;
-        }
-
-        if (LINUX && get_process_info(pid, &pinfo)) {
-            value = xasprintf("%lu,%lu,%lld,%d,%lld,%lld",
-                              pinfo.vsz, pinfo.rss, pinfo.cputime,
-                              pinfo.crashes, pinfo.booted, pinfo.uptime);
-        } else {
-            value = xstrdup("");
+        if (!smap_get(stats, key)) {
+            if (LINUX && get_process_info(pid, &pinfo)) {
+                smap_add_format(stats, key, "%lu,%lu,%lld,%d,%lld,%lld",
+                                pinfo.vsz, pinfo.rss, pinfo.cputime,
+                                pinfo.crashes, pinfo.booted, pinfo.uptime);
+            } else {
+                smap_add(stats, key, "");
+            }
         }
-
-        shash_add_nocopy(stats, key, value);
+        free(key);
     }
 
     closedir(dir);
 }
 
 static void
-get_filesys_stats(struct shash *stats OVS_UNUSED)
+get_filesys_stats(struct smap *stats OVS_UNUSED)
 {
 #if HAVE_SETMNTENT && HAVE_STATVFS
     static const char file_name[] = "/etc/mtab";
@@ -489,18 +489,160 @@ get_filesys_stats(struct shash *stats OVS_UNUSED)
     endmntent(stream);
 
     if (s.length) {
-        shash_add(stats, "file_systems", ds_steal_cstr(&s));
+        smap_add(stats, "file_systems", ds_cstr(&s));
     }
     ds_destroy(&s);
 #endif  /* HAVE_SETMNTENT && HAVE_STATVFS */
 }
+\f
+#define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
+
+/* Whether the client wants us to report system stats. */
+static bool enabled;
+
+static enum {
+    S_DISABLED,                 /* Not enabled, nothing going on. */
+    S_WAITING,                  /* Sleeping for SYSTEM_STATS_INTERVAL ms. */
+    S_REQUEST_SENT,             /* Sent a request to worker. */
+    S_REPLY_RECEIVED            /* Received a reply from worker. */
+} state;
+
+/* In S_WAITING state: the next time to wake up.
+ * In other states: not meaningful. */
+static long long int next_refresh;
+
+/* In S_REPLY_RECEIVED: the stats that have just been received.
+ * In other states: not meaningful. */
+static struct smap *received_stats;
+
+static worker_request_func system_stats_request_cb;
+static worker_reply_func system_stats_reply_cb;
+
+/* Enables or disables system stats collection, according to 'new_enable'.
+ *
+ * Even if system stats are disabled, the caller should still periodically call
+ * system_stats_run(). */
+void
+system_stats_enable(bool new_enable)
+{
+    if (new_enable != enabled) {
+        if (new_enable) {
+            if (state == S_DISABLED) {
+                state = S_WAITING;
+                next_refresh = time_msec();
+            }
+        } else {
+            if (state == S_WAITING) {
+                state = S_DISABLED;
+            }
+        }
+        enabled = new_enable;
+    }
+}
 
+/* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL
+ * milliseconds.
+ *
+ * When a new snapshot is available (which only occurs if system stats are
+ * enabled), returns it as an smap owned by the caller.  The caller must use
+ * both smap_destroy() and free() to complete free the returned data.
+ *
+ * When no new snapshot is available, returns NULL. */
+struct smap *
+system_stats_run(void)
+{
+    switch (state) {
+    case S_DISABLED:
+        break;
+
+    case S_WAITING:
+        if (time_msec() >= next_refresh) {
+            worker_request(NULL, 0, NULL, 0, system_stats_request_cb,
+                           system_stats_reply_cb, NULL);
+            state = S_REQUEST_SENT;
+        }
+        break;
+
+    case S_REQUEST_SENT:
+        break;
+
+    case S_REPLY_RECEIVED:
+        if (enabled) {
+            state = S_WAITING;
+            next_refresh = time_msec() + SYSTEM_STATS_INTERVAL;
+            return received_stats;
+        } else {
+            smap_destroy(received_stats);
+            free(received_stats);
+            state = S_DISABLED;
+        }
+        break;
+    }
+
+    return NULL;
+}
+
+/* Causes poll_block() to wake up when system_stats_run() needs to be
+ * called. */
 void
-get_system_stats(struct shash *stats)
+system_stats_wait(void)
 {
-    get_cpu_cores(stats);
-    get_load_average(stats);
-    get_memory_stats(stats);
-    get_process_stats(stats);
-    get_filesys_stats(stats);
+    switch (state) {
+    case S_DISABLED:
+        break;
+
+    case S_WAITING:
+        poll_timer_wait_until(next_refresh);
+        break;
+
+    case S_REQUEST_SENT:
+        /* Someone else should be calling worker_wait() to wake up when the
+         * reply arrives, otherwise there's a bug. */
+        break;
+
+    case S_REPLY_RECEIVED:
+        poll_immediate_wake();
+        break;
+    }
+}
+
+static void
+system_stats_request_cb(struct ofpbuf *request OVS_UNUSED,
+                        const int fds[] OVS_UNUSED, size_t n_fds OVS_UNUSED)
+{
+    struct smap stats;
+    struct json *json;
+    char *s;
+
+    smap_init(&stats);
+    get_cpu_cores(&stats);
+    get_load_average(&stats);
+    get_memory_stats(&stats);
+    get_process_stats(&stats);
+    get_filesys_stats(&stats);
+
+    json = smap_to_json(&stats);
+    s = json_to_string(json, 0);
+    worker_reply(s, strlen(s) + 1, NULL, 0);
+
+    free(s);
+    json_destroy(json);
+    smap_destroy(&stats);
+}
+
+static void
+system_stats_reply_cb(struct ofpbuf *reply,
+                      const int fds[] OVS_UNUSED, size_t n_fds OVS_UNUSED,
+                      void *aux OVS_UNUSED)
+{
+    struct json *json = json_from_string(reply->data);
+
+    received_stats = xmalloc(sizeof *received_stats);
+    smap_init(received_stats);
+    smap_from_json(received_stats, json);
+
+    assert(state == S_REQUEST_SENT);
+    state = S_REPLY_RECEIVED;
+
+    json_destroy(json);
 }
index 559be69..83b4bcb 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010 Nicira Networks
+/* Copyright (c) 2010, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #ifndef VSWITCHD_SYSTEM_STATS
 #define VSWITCHD_SYSTEM_STATS 1
 
-void get_system_stats(struct shash *);
+#include <stdbool.h>
+
+void system_stats_enable(bool enable);
+struct smap *system_stats_run(void);
+void system_stats_wait(void);
 
 #endif /* vswitchd/system-stats.h */
index 9712003..bbfb01f 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "6.9.3",
- "cksum": "2110020336 16754",
+ "version": "6.10.0",
+ "cksum": "3699312094 16958",
  "tables": {
    "Open_vSwitch": {
      "columns": {
          "type": {
            "key": "string", "min": 0, "max": "unlimited"},
            "ephemeral": true},
+       "cfm_remote_opstate": {
+         "type": {"key": {"type": "string",
+                          "enum": ["set", ["up", "down"]]},
+                  "min": 0, "max": 1},
+         "ephemeral": true},
        "cfm_health": {
          "type": {"key": {"type": "integer",
                           "minInteger": 0,
index b0f6be8..a1b99f8 100644 (file)
           any defined controllers forever.</dd>
         </dl>
         </p>
-        <p>If this value is unset, the default is implementation-specific.</p>
+        <p>
+          The default is <code>standalone</code> if the value is unset, but
+          future versions of Open vSwitch may change the default.
+        </p>
+        <p>
+          The <code>standalone</code> mode can create forwarding loops on a
+          bridge that has more than one uplink port unless STP is enabled.  To
+          avoid loops on such a bridge, configure <code>secure</code> mode or
+          enable STP (see <ref column="stp_enable"/>).
+        </p>
         <p>When more than one controller is configured,
         <ref column="fail_mode"/> is considered only when none of the
         configured controllers can be contacted.</p>
         and if Open vSwitch node does not run STP, then this option
         should be enabled.  Default is disabled, set to
         <code>true</code> to enable.
+
+        The following destination MAC addresss will not be forwarded when this
+        option is enabled.
+        <dl>
+          <dt><code>01:80:c2:00:00:00</code></dt>
+          <dd>IEEE 802.1D Spanning Tree Protocol (STP).</dd>
+
+          <dt><code>01:80:c2:00:00:01</code></dt>
+          <dd>IEEE Pause frame.</dd>
+
+          <dt><code>01:80:c2:00:00:0<var>x</var></code></dt>
+          <dd>Other reserved protocols.</dd>
+
+          <dt><code>00:e0:2b:00:00:00</code></dt>
+          <dd>Extreme Discovery Protocol (EDP).</dd>
+
+          <dt>
+           <code>00:e0:2b:00:00:04</code> and <code>00:e0:2b:00:00:06</code>
+         </dt>
+          <dd>Ethernet Automatic Protection Switching (EAPS).</dd>
+
+          <dt><code>01:00:0c:cc:cc:cc</code></dt>
+          <dd>
+            Cisco Discovery Protocol (CDP), VLAN Trunking Protocol (VTP),
+            Dynamic Trunking Protocol (DTP), Port Aggregation Protocol (PAgP),
+            and others.
+          </dd>
+
+          <dt><code>01:00:0c:cc:cc:cd</code></dt>
+          <dd>Cisco Shared Spanning Tree Protocol PVSTP+.</dd>
+
+          <dt><code>01:00:0c:cd:cd:cd</code></dt>
+          <dd>Cisco STP Uplink Fast.</dd>
+
+          <dt><code>01:00:0c:00:00:00</code></dt>
+          <dd>Cisco Inter Switch Link.</dd>
+
+          <dt><code>01:00:0c:cc:cc:c<var>x</var></code></dt>
+          <dd>Cisco CFM.</dd>
+        </dl>
       </column>
 
       <column name="other_config" key="mac-aging-time"
 
     <group title="Bonding Configuration">
       <p>A port that has more than one interface is a ``bonded port.'' Bonding
-      allows for load balancing and fail-over.  Some kinds of bonding will
-      work with any kind of upstream switch:</p>
+      allows for load balancing and fail-over.</p>
+
+      <p>
+        The following types of bonding will work with any kind of upstream
+        switch.  On the upstream switch, do not configure the interfaces as a
+        bond:
+      </p>
 
       <dl>
         <dt><code>balance-slb</code></dt>
         <dt><code>active-backup</code></dt>
         <dd>
           Assigns all flows to one slave, failing over to a backup slave when
-          the active slave is disabled.
+          the active slave is disabled.  This is the only bonding mode in which
+          interfaces may be plugged into different upstream switches.
         </dd>
       </dl>
 
 
         <dt><code>stable</code></dt>
         <dd>
+          <p>Deprecated and slated for removal in February 2013.</p>
           <p>Attempts to always assign a given flow to the same slave
           consistently.  In an effort to maintain stability, no load
           balancing is done.  Uses a similar hashing strategy to
 
         <column name="bond_updelay">
           <p>
-            The number of milliseconds for which carrier must stay up on an
+            The number of milliseconds for which the link must stay up on an
             interface before the interface is considered to be up.  Specify
             <code>0</code> to enable the interface immediately.
           </p>
         </column>
 
         <column name="bond_downdelay">
-          The number of milliseconds for which carrier must stay down on an
+          The number of milliseconds for which the link must stay down on an
           interface before the interface is considered to be down.  Specify
           <code>0</code> to disable the interface immediately.
         </column>
         </column>
       </group>
 
-      <group title="SLB Configuration">
+      <group title="Rebalancing Configuration">
         <p>
           These settings control behavior when a bond is in
-          <code>balance-slb</code> mode, regardless of whether the bond was
-          intentionally configured in SLB mode or it fell back to SLB mode
-          because LACP negotiation failed.
+          <code>balance-slb</code> or <code>balance-tcp</code> mode.
         </p>
 
         <column name="other_config" key="bond-rebalance-interval"
           successive attempts to rebalance the bond, that is, to move flows
           from one interface on the bond to another in an attempt to keep usage
           of each interface roughly equal.  If zero, load balancing is disabled
-          on the bond (carrier status changes still cause flows to move).  If
+          on the bond (link failure still cause flows to move).  If
           less than 1000ms, the rebalance interval will be 1000ms.
         </column>
       </group>
           </dd>
 
           <dt><code>null</code></dt>
-          <dd>An ignored interface.</dd>
+          <dd>An ignored interface. Deprecated and slated for removal in
+              February 2013.</dd>
         </dl>
       </column>
     </group>
 
       <column name="options" key="tos">
         Optional.  The value of the ToS bits to be set on the encapsulating
-        packet.  It may also be the word <code>inherit</code>, in which case
+        packet.  ToS is interpreted as DSCP and ECN bits, ECN part must be
+        zero.  It may also be the word <code>inherit</code>, in which case
         the ToS will be copied from the inner packet if it is IPv4 or IPv6
         (otherwise it will be 0).  The ECN fields are always inherited.
         Default is 0.
         frame having an invalid interval.
       </column>
 
-      <column name="cfm_fault_status" key="sequence">
-        Indicates a CFM fault was triggered because the CFM module received
-        a CCM frame with a sequence number that it was not expecting.
+      <column name="cfm_remote_opstate">
+        <p>When in extended mode, indicates the operational state of the
+          remote endpoint as either <code>up</code> or <code>down</code>.  See
+          <ref column="other_config" key="cfm_opstate"/>.
+        </p>
       </column>
 
       <column name="cfm_health">
 
       <column name="other_config" key="cfm_interval"
               type='{"type": "integer"}'>
-        The interval, in milliseconds, between transmissions of CFM heartbeats.
-        Three missed heartbeat receptions indicate a connectivity fault.
-        Defaults to 1000.
+        <p>
+          The interval, in milliseconds, between transmissions of CFM
+          heartbeats.  Three missed heartbeat receptions indicate a
+          connectivity fault.
+        </p>
+
+        <p>
+          In standard operation only intervals of 3, 10, 100, 1,000, 10,000,
+          60,000, or 600,000 ms are supported.  Other values will be rounded
+          down to the nearest value on the list.  Extended mode (see <ref
+          column="other_config" key="cfm_extended"/>) supports any interval up
+          to 65,535 ms.  In either mode, the default is 1000 ms.
+        </p>
+
+        <p>We do not recommend using intervals less than 100 ms.</p>
       </column>
 
       <column name="other_config" key="cfm_extended"
       <p>
         The eviction process only considers flows that have an idle timeout or
         a hard timeout.  That is, eviction never deletes permanent flows.
-        (Permanent flows do count against <ref column="flow_limit"/>.
+        (Permanent flows do count against <ref column="flow_limit"/>.)
       </p>
 
       <p>
         sent out an implicit VLAN port, the frame will not be tagged.  This
         type of mirroring is sometimes called RSPAN.</p>
         <p>
-          The following destination MAC addresses will not be mirrored to a
-          VLAN to avoid confusing switches that interpret the protocols that
-          they represent:
+          See the documentation for
+          <ref column="other_config" key="forward-bpdu"/> in the
+          <ref table="Interface"/> table for a list of destination MAC
+          addresses which will not be mirrored to a VLAN to avoid confusing
+          switches that interpret the protocols that they represent.
         </p>
-        <dl>
-          <dt><code>01:80:c2:00:00:00</code></dt>
-          <dd>IEEE 802.1D Spanning Tree Protocol (STP).</dd>
-
-          <dt><code>01:80:c2:00:00:01</code></dt>
-          <dd>IEEE Pause frame.</dd>
-
-          <dt><code>01:80:c2:00:00:0<var>x</var></code></dt>
-          <dd>Other reserved protocols.</dd>
-
-          <dt><code>01:00:0c:cc:cc:cc</code></dt>
-          <dd>
-            Cisco Discovery Protocol (CDP), VLAN Trunking Protocol (VTP),
-            Dynamic Trunking Protocol (DTP), Port Aggregation Protocol (PAgP),
-            and others.
-          </dd>
-
-          <dt><code>01:00:0c:cc:cc:cd</code></dt>
-          <dd>Cisco Shared Spanning Tree Protocol PVSTP+.</dd>
-
-          <dt><code>01:00:0c:cd:cd:cd</code></dt>
-          <dd>Cisco STP Uplink Fast.</dd>
-
-          <dt><code>01:00:0c:00:00:00</code></dt>
-          <dd>Cisco Inter Switch Link.</dd>
-        </dl>
         <p><em>Please note:</em> Mirroring to a VLAN can disrupt a network that
         contains unmanaged switches.  Consider an unmanaged physical switch
         with two ports: port 1, connected to an end host, and port 2,
         Service (QoS) on IP networks.
 
         The DSCP value specified here is used when establishing the connection
-        between the controller and the Open vSwitch.  The connection must be
-        reset for the new DSCP values to take effect.  If no value is
-        specified, a default value of 48 is chosen.  Valid DSCP values must be
-        in the range 0 to 63.
+        between the controller and the Open vSwitch.  If no value is specified,
+        a default value of 48 is chosen.  Valid DSCP values must be in the
+        range 0 to 63.
       </column>
     </group>
 
         Service (QoS) on IP networks.
 
         The DSCP value specified here is used when establishing the connection
-        between the manager and the Open vSwitch.  The connection must be
-        reset for the new DSCP values to take effect.  If no value is
-        specified, a default value of 48 is chosen.  Valid DSCP values must be
-        in the range 0 to 63.
+        between the manager and the Open vSwitch.  If no value is specified, a
+        default value of 48 is chosen.  Valid DSCP values must be in the range
+        0 to 63.
       </column>
     </group>
 
index eed798f..1f29daf 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* 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.
index ad9ed8c..48c7461 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index 4efdc37..e72655f 100644 (file)
@@ -85,6 +85,7 @@ debugging.  The sources for the extensions are in
             * ovs-bugtool-lcap-show
             * ovs-bugtool-ovsdb-dump
             * ovs-bugtool-tc-class-show
+            * ovs-bugtool-bond-show
 
     system-configuration/openvswitch.xml
 
@@ -126,7 +127,7 @@ scripts are located in ../utilities/bugtool:
         Script to dump version information for all Open vSwitch daemons.
 
 ----------------------------------------------------------------------
-Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+Copyright (C) 2009, 2010, 2011 Nicira, Inc.
 
 Copying and distribution of this file, with or without modification,
 are permitted in any medium without royalty provided the copyright
index cb6016f..413e634 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
index aa65657..a199825 100755 (executable)
@@ -5,7 +5,7 @@
 # chkconfig: 2345 09 91
 # description: Manage Open vSwitch kernel modules and user-space daemons
 
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 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.
@@ -76,6 +76,7 @@ start () {
         touch /var/run/openvswitch.booted
         set "$@" --delete-bridges
     fi
+    set "$@" $OVS_CTL_OPTS
     "$@"
 
     start_ovs_xapi_sync
index 14d2cc9..12a9db2 100755 (executable)
@@ -5,7 +5,7 @@
 # chkconfig: 2345 95 01
 # description: Update Open vSwitch configuration from XAPI database at boot
 
-# Copyright (C) 2009, 2010 Nicira Networks, Inc.
+# 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.
index c1ead14..46b94b9 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
        missingok
        postrotate
        # Tell Open vSwitch daemons to reopen their log files
-       if [ -e /var/run/openvswitch/ovs-vswitchd.pid ]; then
-           /usr/bin/ovs-appctl -t ovs-vswitchd vlog/reopen
-       fi
-       if [ -e /var/run/openvswitch/ovsdb-server.pid ]; then   
-           /usr/bin/ovs-appctl -t ovsdb-server vlog/reopen
-       fi
+        for pidfile in `cd /var/run/openvswitch && echo *.pid`; do
+            ovs-appctl -t "${pidfile%%.pid}" vlog/reopen
+        done
        endscript
 }
index 7376f53..63b2543 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
index ef4d11a..02927f8 100755 (executable)
@@ -4,7 +4,7 @@
 # ovs-vswitchd configuration that are managed in the xapi database when 
 # integrated with Citrix management tools.
 
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -213,7 +213,7 @@ def setControllerCfg(controller):
                    "--", "set-manager", 'ssl:' + controller + ':6632'])
 
 def vswitchCfgQuery(action_args):
-    cmd = [vsctl, "--timeout=5", "-vANY:console:off"] + action_args
+    cmd = [vsctl, "--timeout=5", "-vconsole:off"] + action_args
     output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
     if len(output) == 0 or output[0] == None:
         output = ""
@@ -222,7 +222,7 @@ def vswitchCfgQuery(action_args):
     return output
 
 def vswitchCfgMod(action_args):
-    cmd = [vsctl, "--timeout=5", "-vANY:console:off"] + action_args
+    cmd = [vsctl, "--timeout=5", "-vconsole:off"] + action_args
     exitcode = subprocess.call(cmd)
     if exitcode != 0:
         raise XenAPIPlugin.Failure("VSWITCH_CONFIG_MOD_FAILURE",
index 1e8d764..c34c7db 100644 (file)
@@ -1,6 +1,6 @@
 # Spec file for Open vSwitch.
 
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Copying and distribution of this file, with or without modification,
 # are permitted in any medium without royalty provided the copyright
@@ -41,7 +41,7 @@ Name: openvswitch
 Summary: Open vSwitch daemon/database/utilities
 Group: System Environment/Daemons
 URL: http://www.openvswitch.org/
-Vendor: Nicira Networks, Inc.
+Vendor: Nicira, Inc.
 Version: %{openvswitch_version}
 
 License: ASL 2.0
@@ -125,13 +125,15 @@ rm \
     $RPM_BUILD_ROOT/usr/bin/ovs-benchmark \
     $RPM_BUILD_ROOT/usr/sbin/ovs-bugtool \
     $RPM_BUILD_ROOT/usr/bin/ovs-controller \
+    $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
     $RPM_BUILD_ROOT/usr/bin/ovs-pki \
     $RPM_BUILD_ROOT/usr/bin/ovs-test \
-    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \
     $RPM_BUILD_ROOT/usr/share/man/man1/ovs-benchmark.1 \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-bugtool.8 \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
-    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8
+    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \
+    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 \
+    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8
 
 install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch
 
@@ -232,11 +234,11 @@ if test ! -e /etc/openvswitch/conf.db; then
     install -d -m 755 -o root -g root /etc/openvswitch
 
     # Create ovs-vswitchd config database
-    ovsdb-tool -vANY:console:off create /etc/openvswitch/conf.db \
+    ovsdb-tool -vconsole:off create /etc/openvswitch/conf.db \
             /usr/share/openvswitch/vswitch.ovsschema
 
     # Create initial table in config database
-    ovsdb-tool -vANY:console:off transact /etc/openvswitch/conf.db \
+    ovsdb-tool -vconsole:off transact /etc/openvswitch/conf.db \
             '[{"op": "insert", "table": "Open_vSwitch", "row": {}}]' \
             > /dev/null
 fi
@@ -407,6 +409,7 @@ exit 0
 /etc/logrotate.d/openvswitch
 /etc/profile.d/openvswitch.sh
 /usr/share/openvswitch/python/
+/usr/share/openvswitch/scripts/ovs-check-dead-ifs
 /usr/share/openvswitch/scripts/ovs-xapi-sync
 /usr/share/openvswitch/scripts/interface-reconfigure
 /usr/share/openvswitch/scripts/InterfaceReconfigure.py
index 31e9b51..971f918 100644 (file)
@@ -1,5 +1,5 @@
 # Copyright (c) 2008,2009,2011 Citrix Systems, Inc.
-# Copyright (c) 2009,2010,2011 Nicira Networks.
+# Copyright (c) 2009,2010,2011,2012 Nicira, Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as published
@@ -721,7 +721,7 @@ class DatapathVswitch(Datapath):
 
 def vswitchCfgQuery(action_args):
     cmd = ['%s/usr/bin/ovs-vsctl' % root_prefix(),
-        '--timeout=5', '-vANY:console:off'] + action_args
+        '--timeout=5', '-vconsole:off'] + action_args
     output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
     if len(output) == 0 or output[0] == None:
         output = ""
index 6c6de9f..72bd5e4 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 #
 # Copyright (c) 2008,2009 Citrix Systems, Inc.
 #
index 93532c8..f62eaa8 100644 (file)
@@ -1,5 +1,5 @@
 # Copyright (c) 2007-2011 Citrix Systems Inc.
-# Copyright (c) 2009,2010,2011 Nicira Networks.
+# Copyright (c) 2009,2010,2011,2012 Nicira, Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -86,7 +86,7 @@ class VSwitchConfig:
     @staticmethod
     def Get(action):
         try:
-            arg = [vsctl, "--timeout=30", "-vANY:console:off"] + action.split()
+            arg = [vsctl, "--timeout=30", "-vconsole:off"] + action.split()
             output = ShellPipe(arg).Stdout()
         except StandardError, e:
             XSLogError("config retrieval error: " + str(e))
index 77c07bc..cb35e7a 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/python
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@ from ovs.db import types
 import ovs.daemon
 import ovs.db.idl
 import ovs.unixctl
+import ovs.unixctl.server
 
 vlog = ovs.vlog.Vlog("ovs-xapi-sync")
 session = None
@@ -81,10 +82,9 @@ def get_network_by_bridge(br_name):
                 " XAPI session could not be initialized" % br_name)
         return None
 
-    for n in session.xenapi.network.get_all():
-        rec = session.xenapi.network.get_record(n)
-        if rec['bridge'] == br_name:
-            return rec
+    recs = session.xenapi.network.get_all_records_where('field "bridge"="%s"' % br_name)
+    if len(recs) > 0:
+        return recs.values()[0]
 
     return None
 
@@ -236,7 +236,7 @@ def main():
     ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None)
     ovs.unixctl.command_register("flush-cache", "", 0, 0, unixctl_flush_cache,
                                  None)
-    error, unixctl_server = ovs.unixctl.UnixctlServer.create(None)
+    error, unixctl_server = ovs.unixctl.server.UnixctlServer.create(None)
     if error:
         ovs.util.ovs_fatal(error, "could not create unixctl server", vlog)
 
index 26543af..2c08452 100644 (file)
@@ -1,6 +1,6 @@
 ### Configuration options for openvswitch
 
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
 
 # FORCE_COREFILES: If 'yes' then core files will be enabled.
 # FORCE_COREFILES=yes
@@ -18,3 +18,7 @@
 #     system memory pressure in extraordinary situations, such as multiple
 #     concurrent VM import operations.
 # VSWITCHD_MLOCKALL=yes
+
+# OVS_CTL_OPTS: Extra options to pass to ovs-ctl.  This is, for example,
+# a suitable place to specify --ovs-vswitchd-wrapper=valgrind.
+# OVS_CTL_OPTS=