Merge branch 'ovs-dev' of /usr/local/git/openvswitch into ovs-dev
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Thu, 21 Jun 2012 09:24:15 +0000 (11:24 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Thu, 21 Jun 2012 09:24:15 +0000 (11:24 +0200)
151 files changed:
AUTHORS
DESIGN
FAQ [new file with mode: 0644]
IntegrationGuide [new file with mode: 0644]
Makefile.am
NEWS
README
datapath/datapath.c
datapath/dp_notify.c
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/workqueue.h
datapath/linux/compat/workqueue.c
datapath/tunnel.c
datapath/vport-internal_dev.c
datapath/vport-netdev.c
debian/automake.mk
debian/changelog
debian/dkms.conf.in
debian/openvswitch-common.docs [new file with mode: 0644]
debian/openvswitch-switch.init
debian/openvswitch-switch.logrotate
debian/openvswitch-switch.template
debian/ovs-monitor-ipsec
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
lib/automake.mk
lib/bond.c
lib/cfm.c
lib/classifier.c
lib/classifier.h
lib/dpif-linux.c
lib/dpif-netdev.c
lib/flow.c
lib/flow.h
lib/lacp.c
lib/learn.c
lib/learning-switch.c
lib/learning-switch.h
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/netdev-linux.c
lib/netdev-provider.h
lib/netdev-vport.c
lib/netdev.c
lib/netdev.h
lib/nx-match.c
lib/nx-match.h
lib/odp-util.c
lib/odp-util.h
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-print.h
lib/ofp-util.c
lib/ofp-util.h
lib/ovsdb-idl-provider.h
lib/ovsdb-idl.c
lib/packets.c
lib/packets.h
lib/poll-loop.c
lib/rconn.c
lib/rconn.h
lib/shash.c
lib/shash.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/vlog.c
lib/vlog.man
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/fail-open.c
ofproto/ofproto-dpif-governor.c
ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/ofproto.h
ofproto/pinsched.c
ofproto/pinsched.h
ofproto/pktbuf.c
ofproto/pktbuf.h
ovsdb/jsonrpc-server.c
ovsdb/jsonrpc-server.h
ovsdb/ovsdb-client.1.in
ovsdb/ovsdb-idlc.in
ovsdb/ovsdb-server.c
ovsdb/ovsdb.c
ovsdb/ovsdb.h
python/automake.mk
python/ovs/db/types.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 75% similarity]
python/ovs/vlog.py
rhel/etc_init.d_openvswitch
rhel/etc_logrotate.d_openvswitch
rhel/openvswitch.spec.in
rhel/usr_share_openvswitch_scripts_sysconfig.template
tests/appctl.py
tests/atlocal.in
tests/automake.mk
tests/interface-reconfigure.at
tests/lacp.at
tests/odp.at
tests/ofp-print.at
tests/ofproto.at
tests/ovs-monitor-ipsec.at
tests/ovs-ofctl.at
tests/ovs-vsctl.at
tests/ovs-xapi-sync.at
tests/test-classifier.c
tests/test-flows.c
tests/test-odp.c
tests/test-unixctl.py
tests/unixctl-py.at
tests/vlog.at
utilities/.gitignore
utilities/bugtool/automake.mk
utilities/bugtool/ovs-bugtool-memory-show [new file with mode: 0755]
utilities/bugtool/plugins/network-status/openvswitch.xml
utilities/ovs-appctl.8.in
utilities/ovs-controller.c
utilities/ovs-ctl.8
utilities/ovs-ctl.in
utilities/ovs-dpctl.c
utilities/ovs-lib.in
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-vsctl.8.in
utilities/ovs-vsctl.c
vswitchd/INTERNALS
vswitchd/bridge.c
vswitchd/bridge.h
vswitchd/ovs-brcompatd.c
vswitchd/ovs-vswitchd.c
vswitchd/vswitch.xml
xenserver/etc_init.d_openvswitch
xenserver/etc_logrotate.d_openvswitch
xenserver/etc_xapi.d_plugins_openvswitch-cfg-update
xenserver/openvswitch-xen.spec.in
xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py
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 6e344ab..c03b596 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -9,6 +9,7 @@ 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
@@ -29,10 +30,12 @@ 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
@@ -116,6 +119,7 @@ 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
 Krishna Miriyala        krishna@nicira.com
 Luca Falavigna          dktrkranz@debian.org
@@ -135,6 +139,7 @@ Paul Ingram             paul@nicira.com
 Paulo Cravero           pcravero@as2594.net
 Peter Balland           peter@nicira.com
 Peter Phaal             peter.phaal@inmon.com
+Ralf Heiringhoff        ralf@frosty-geek.net
 Ram Jothikumar          rjothikumar@nicira.com
 Ramana Reddy            gtvrreddy@gmail.com
 Rob Sherwood            rob.sherwood@bigswitch.com
diff --git a/DESIGN b/DESIGN
index e59dc6e..f9345d1 100644 (file)
--- a/DESIGN
+++ b/DESIGN
@@ -198,11 +198,16 @@ behavior with the following extensions:
           arbitrary masks.  This is much like the equivalent OpenFlow
           1.1 feature.
 
-        - However, unlike OpenFlow 1.1, OFPC_MODIFY and
-          OFPFC_MODIFY_STRICT, regardless of whether there was a match
-          based on a cookie or not, always add a new flow if there is
-          no match, and they always update the cookies of flows that
-          they do match.
+        - 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
@@ -210,6 +215,21 @@ behavior with the following extensions:
           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
 ======================
diff --git a/FAQ b/FAQ
new file mode 100644 (file)
index 0000000..7f3a784
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,497 @@
+                 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 and patch virtual ports, that is, interfaces with type
+         "gre", "ipsec_gre", "capwap", or "patch".  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.
+
+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.
+
+
+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: 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.
+
+
+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.
+
+
+Contact 
+-------
+
+bugs@openvswitch.org
+http://openvswitch.org/
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 c0c100e..115162d 100644 (file)
@@ -22,6 +22,16 @@ AM_CPPFLAGS += -DNDEBUG
 AM_CFLAGS += -fomit-frame-pointer
 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 =
@@ -31,6 +41,7 @@ PYCOV_CLEAN_FILES = build-aux/check-structs,cover
 EXTRA_DIST = \
        CodingStyle \
        DESIGN \
+       FAQ \
        INSTALL.KVM \
        INSTALL.Libvirt \
        INSTALL.Linux \
@@ -39,6 +50,7 @@ EXTRA_DIST = \
        INSTALL.XenServer \
        INSTALL.bridge \
        INSTALL.userspace \
+       IntegrationGuide \
        NOTICE \
        PORTING \
        README-gcov \
diff --git a/NEWS b/NEWS
index f8f09e6..28e3cf0 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,20 @@
 post-v1.7.0
 ------------------------
+    - New FAQ.  Please send updates and additions!
+    - ovs-ofctl:
+        - "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.)
+    - 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.
 
 
 v1.7.0 - xx xxx xxxx
@@ -50,8 +65,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.
@@ -78,7 +91,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
@@ -92,6 +105,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/README b/README
index 251a92e..aa31782 100644 (file)
--- a/README
+++ b/README
@@ -90,6 +90,8 @@ What other documentation is available?
 
 To install Open vSwitch on a regular Linux machine, read INSTALL.Linux.
 
+For answers to common questions, read FAQ.
+
 To use Open vSwitch as a drop-in replacement for the Linux bridge,
 read INSTALL.bridge.
 
index a4376a0..28e0573 100644 (file)
@@ -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,5,0)
+#error Kernels before 2.6.18 or after 3.4 are not supported by this version of Open vSwitch.
 #endif
 
 #define REHASH_FLOW_INTERVAL (10 * 60 * HZ)
@@ -411,8 +410,8 @@ static int queue_gso_packets(struct net *net, int dp_ifindex,
        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;
index 683f624..13085d6 100644 (file)
@@ -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 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 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 c2133bb..d651c11 100644 (file)
@@ -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 af7fe64..4dc2eb4 100644 (file)
@@ -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 e24e588..0098554 100644 (file)
@@ -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 046b7c4..ae82168 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 \
index b0b7b4f..2a78eb3 100644 (file)
@@ -94,7 +94,7 @@ 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
 
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/openvswitch-common.docs b/debian/openvswitch-common.docs
new file mode 100644 (file)
index 0000000..d6360a1
--- /dev/null
@@ -0,0 +1 @@
+FAQ
index b44daad..3c93720 100755 (executable)
@@ -70,6 +70,7 @@ 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
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 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 9404804..ffaa979 100755 (executable)
@@ -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 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 62fc103..2f46311 100644 (file)
@@ -1149,11 +1149,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 +1211,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 +1368,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 +1461,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 +1531,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 +1608,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 +1637,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)
@@ -1777,10 +1781,8 @@ OFP_ASSERT(sizeof(struct nx_set_flow_format) == 20);
 /* 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;
index 890676c..831f6a9 100644 (file)
@@ -286,7 +286,7 @@ OFP_ASSERT(sizeof(struct ofp_action_nw_addr) == 8);
 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 nw_tos;                 /* DSCP in high 6 bits, rest ignored. */
     uint8_t pad[3];
 };
 OFP_ASSERT(sizeof(struct ofp_action_nw_tos) == 8);
@@ -372,41 +372,43 @@ enum ofp_flow_mod_command {
 
 /* 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
@@ -425,7 +427,7 @@ enum ofp_flow_wildcards {
 #define OFP_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 +445,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. */
@@ -462,7 +464,7 @@ enum ofp_flow_mod_flags {
 /* Flow setup and teardown (controller -> datapath). */
 struct ofp_flow_mod {
     struct ofp_header header;
-    struct ofp_match match;      /* Fields to match */
+    struct ofp10_match match;    /* Fields to match */
     ovs_be64 cookie;             /* Opaque controller-issued identifier. */
 
     /* Flow actions. */
@@ -486,7 +488,7 @@ OFP_ASSERT(sizeof(struct ofp_flow_mod) == 72);
 /* 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. */
@@ -545,7 +547,7 @@ 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_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. */
@@ -560,7 +562,7 @@ struct ofp_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. */
@@ -592,7 +594,7 @@ struct ofp_table_stats {
                                 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. */
index 2b8e95b..8461dad 100644 (file)
@@ -269,19 +269,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
@@ -471,7 +471,7 @@ 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];
@@ -489,6 +489,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
@@ -537,7 +551,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
index 7f59c07..bb55881 100644 (file)
@@ -121,9 +121,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)
@@ -211,11 +213,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. */
index 6217526..6d3667c 100644 (file)
@@ -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 \
@@ -135,8 +137,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 \
@@ -298,7 +304,7 @@ 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
index cffdae2..54f2d0e 100644 (file)
@@ -531,8 +531,7 @@ bond_compose_learning_packet(struct bond *bond,
     slave = choose_output_slave(bond, &flow, vlan);
 
     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));
     }
index 41a27a0..670f037 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -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;
@@ -118,7 +118,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
@@ -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;
 }
 
@@ -404,6 +405,7 @@ cfm_run(struct cfm *cfm)
         }
 
         timer_set_duration(&cfm->fault_timer, interval);
+        VLOG_DBG("%s: new fault interval", cfm->name);
     }
 }
 
@@ -465,6 +467,16 @@ cfm_compose_ccm(struct cfm *cfm, struct ofpbuf *packet,
     if (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
index e11a585..d19840c 100644 (file)
@@ -149,16 +149,31 @@ cls_rule_set_dl_type(struct cls_rule *rule, ovs_be16 dl_type)
 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);
+    memset(rule->wc.dl_src_mask, 0xff, ETH_ADDR_LEN);
+}
+
+/* Modifies 'rule' so that the 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])
+{
+    size_t i;
+
+    for (i = 0; i < ETH_ADDR_LEN; i++) {
+        rule->flow.dl_src[i] = dl_src[i] & mask[i];
+        rule->wc.dl_src_mask[i] = mask[i];
+    }
 }
 
 /* Modifies 'rule' so that the 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);
+    memset(rule->wc.dl_dst_mask, 0xff, ETH_ADDR_LEN);
 }
 
 /* Modifies 'rule' so that the Ethernet address must match 'dl_dst' after each
@@ -171,12 +186,11 @@ 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];
+        rule->wc.dl_dst_mask[i] = mask[i];
     }
 }
 
@@ -448,6 +462,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 +525,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 == 11);
 
     if (rule->priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%d,", rule->priority);
@@ -597,24 +622,8 @@ 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));
     }
@@ -1179,7 +1188,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 == 11);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
@@ -1195,16 +1204,10 @@ 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))
index 92ccc2f..9e4b33e 100644 (file)
@@ -96,6 +96,8 @@ void cls_rule_set_tun_id_masked(struct cls_rule *,
 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]);
index 256c9d6..62f6917 100644 (file)
@@ -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 = 17 };
-BUILD_ASSERT_DECL(IS_POW2(N_UPCALL_SOCKS - 1));
-BUILD_ASSERT_DECL(N_UPCALL_SOCKS > 1);
-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. */
@@ -130,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. */
@@ -249,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
@@ -275,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);
 }
@@ -293,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);
     }
@@ -464,9 +528,9 @@ dpif_linux_port_get_pid(const struct dpif *dpif_, uint16_t port_no)
         int idx;
 
         idx = (port_no != UINT16_MAX
-               ? 1 + (port_no & (N_UPCALL_SOCKS - 2))
+               ? 1 + (port_no & (N_CHANNELS - 2))
                : 0);
-        return nl_sock_pid(dpif->upcall_socks[idx]);
+        return nl_sock_pid(dpif->channels[idx].sock);
     }
 }
 
@@ -988,37 +1052,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_);
@@ -1105,12 +1174,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);
@@ -1124,7 +1193,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);
 
@@ -1136,7 +1205,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;
@@ -1146,6 +1225,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) {
@@ -1173,14 +1259,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);
     }
 }
 
@@ -1806,3 +1892,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 fb0a863..cade79e 100644 (file)
@@ -956,7 +956,7 @@ dpif_netdev_recv(struct dpif *dpif, struct dpif_upcall *upcall,
         free(u);
 
         ofpbuf_uninit(buf);
-        *buf = *u->packet;
+        *buf = *upcall->packet;
 
         return 0;
     } else {
index fc61610..46e0e2d 100644 (file)
@@ -444,7 +444,7 @@ 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 == 11);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         flow->regs[i] &= wildcards->reg_masks[i];
@@ -461,16 +461,8 @@ 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;
     }
@@ -506,7 +498,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11);
 
     fmd->tun_id = flow->tun_id;
     fmd->tun_id_mask = htonll(UINT64_MAX);
@@ -595,7 +587,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 == 11);
 
     wc->wildcards = FWW_ALL;
     wc->tun_id_mask = htonll(0);
@@ -609,6 +601,8 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
     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->zeros, 0, sizeof wc->zeros);
 }
 
@@ -617,7 +611,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 == 11);
 
     wc->wildcards = 0;
     wc->tun_id_mask = htonll(UINT64_MAX);
@@ -631,6 +625,8 @@ flow_wildcards_init_exact(struct flow_wildcards *wc)
     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->zeros, 0, sizeof wc->zeros);
 }
 
@@ -641,7 +637,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 == 11);
 
     if (wc->wildcards
         || wc->tun_id_mask != htonll(UINT64_MAX)
@@ -650,6 +646,8 @@ 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)
+        || !eth_mask_is_exact(wc->dl_src_mask)
+        || !eth_mask_is_exact(wc->dl_dst_mask)
         || !ipv6_mask_is_exact(&wc->ipv6_src_mask)
         || !ipv6_mask_is_exact(&wc->ipv6_dst_mask)
         || !ipv6_mask_is_exact(&wc->nd_target_mask)
@@ -673,7 +671,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 == 11);
 
     if (wc->wildcards != FWW_ALL
         || wc->tun_id_mask != htonll(0)
@@ -682,6 +680,8 @@ 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)
+        || !eth_addr_is_zero(wc->dl_src_mask)
+        || !eth_addr_is_zero(wc->dl_dst_mask)
         || !ipv6_mask_is_any(&wc->ipv6_src_mask)
         || !ipv6_mask_is_any(&wc->ipv6_dst_mask)
         || !ipv6_mask_is_any(&wc->nd_target_mask)
@@ -708,7 +708,7 @@ flow_wildcards_combine(struct flow_wildcards *dst,
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11);
 
     dst->wildcards = src1->wildcards | src2->wildcards;
     dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask;
@@ -726,6 +726,8 @@ flow_wildcards_combine(struct flow_wildcards *dst,
     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;
+    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);
 }
 
 /* Returns a hash of the wildcards in 'wc'. */
@@ -735,7 +737,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 == 88 + FLOW_N_REGS * 4);
     return hash_bytes(wc, sizeof *wc, basis);
 }
 
@@ -747,7 +749,7 @@ flow_wildcards_equal(const struct flow_wildcards *a,
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11);
 
     if (a->wildcards != b->wildcards
         || a->tun_id_mask != b->tun_id_mask
@@ -758,7 +760,9 @@ flow_wildcards_equal(const struct flow_wildcards *a,
         || !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_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
+        || !eth_addr_equals(a->dl_src_mask, b->dl_src_mask)
+        || !eth_addr_equals(a->dl_dst_mask, b->dl_dst_mask)) {
         return false;
     }
 
@@ -778,9 +782,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 == 11);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
@@ -788,6 +793,16 @@ 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;
+    }
+
     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;
@@ -820,83 +835,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)
index 7ee9a26..2958ff5 100644 (file)
 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 11
 
 #define FLOW_N_REGS 8
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -100,7 +99,7 @@ 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 == 142 && FLOW_WC_SEQ == 11);
 
 void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id,
                   uint16_t in_port, struct flow *);
@@ -145,26 +144,21 @@ 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_ARP_SHA     ((OVS_FORCE flow_wildcards_t) (1 << 3))
+#define FWW_ARP_THA     ((OVS_FORCE flow_wildcards_t) (1 << 6))
+#define FWW_IPV6_LABEL  ((OVS_FORCE flow_wildcards_t) (1 << 7))
+#define FWW_NW_TTL      ((OVS_FORCE flow_wildcards_t) (1 << 8))
+#define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 9)) - 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 << 9) - 1) && FLOW_WC_SEQ == 11);
 
 /* Information on wildcards for a flow, as a supplement to "struct flow".
  *
@@ -184,11 +178,13 @@ struct flow_wildcards {
     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 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) == 120 && FLOW_WC_SEQ == 11);
 
 void flow_wildcards_init_catchall(struct flow_wildcards *);
 void flow_wildcards_init_exact(struct flow_wildcards *);
index e1d365d..374d915 100644 (file)
@@ -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 e995c29..cbecb10 100644 (file)
@@ -184,7 +184,7 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
                      * 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);
+                    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11);
                 }
             }
         }
@@ -204,8 +204,9 @@ learn_execute(const struct nx_action_learn *learn, const struct flow *flow,
     struct ofpbuf actions;
 
     cls_rule_init_catchall(&fm->cr, ntohs(learn->priority));
-    fm->cookie = learn->cookie;
-    fm->cookie_mask = htonll(UINT64_MAX);
+    fm->cookie = htonll(0);
+    fm->cookie_mask = htonll(0);
+    fm->new_cookie = learn->cookie;
     fm->table_id = learn->table_id;
     fm->command = OFPFC_MODIFY_STRICT;
     fm->idle_timeout = ntohs(learn->idle_timeout);
index 4e7ceda..6b74f82 100644 (file)
@@ -37,6 +37,7 @@
 #include "poll-loop.h"
 #include "rconn.h"
 #include "shash.h"
+#include "simap.h"
 #include "timeval.h"
 #include "vconn.h"
 #include "vlog.h"
@@ -110,27 +111,27 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
             /* 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);
+            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);
         } else {
             ofpfw = cfg->wildcards;
         }
 
-        ofputil_wildcard_from_openflow(ofpfw, &sw->wc);
+        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);
         }
     }
index e42aec1..1b760d8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010, 2011 Nicira, Inc.
+ * 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,8 +55,8 @@ 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;
 };
 
 struct lswitch *lswitch_create(struct rconn *, const struct lswitch_config *);
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 8b60b35..b3a4bff 100644 (file)
@@ -113,7 +113,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 +122,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,
@@ -175,7 +175,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 +184,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 +195,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 +204,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,
@@ -281,7 +281,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 +290,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,
@@ -407,7 +407,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,
@@ -441,6 +441,7 @@ struct nxm_field {
 };
 
 static struct hmap all_nxm_fields = HMAP_INITIALIZER(&all_nxm_fields);
+static struct hmap all_oxm_fields = HMAP_INITIALIZER(&all_oxm_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. */
@@ -476,40 +477,70 @@ mf_from_name(const char *name)
 }
 
 static void
-add_nxm_field(uint32_t nxm_header, const struct mf_field *mf)
+add_nxm_field(struct hmap *all_fields, uint32_t nxm_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));
+    hmap_insert(all_fields, &f->hmap_node, hash_int(nxm_header, 0));
     f->nxm_header = nxm_header;
     f->mf = mf;
 }
 
+static struct hmap *
+get_all_fields(uint32_t header)
+{
+        return IS_OXM_HEADER(header) ? &all_oxm_fields : &all_nxm_fields;
+}
+
+static void
+nxm_init_add_field(const struct mf_field *mf, uint32_t header)
+{
+    struct hmap *all_fields = get_all_fields(header);
+
+    if (!header) {
+        return;
+    }
+    add_nxm_field(all_fields, header, mf);
+    if (mf->maskable == MFM_NONE) {
+        return;
+    }
+    add_nxm_field(all_fields, NXM_MAKE_WILD_HEADER(header), mf);
+}
+
+#ifndef NDEBUG
+static void
+nxm_init_verify_field(const struct mf_field *mf, uint32_t header)
+{
+    if (!header) {
+        return;
+    }
+    assert(mf_from_nxm_header(header) == mf);
+    /* Some OXM fields are not maskable while their NXM
+     * counterparts are, just skip this check for now */
+    if (mf->maskable == MFM_NONE || IS_OXM_HEADER(header)) {
+        return;
+    }
+    assert(mf_from_nxm_header(NXM_MAKE_WILD_HEADER(mf->nxm_header)) == mf);
+}
+#endif
+
 static void
 nxm_init(void)
 {
     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);
-            }
-        }
+        nxm_init_add_field(mf, mf->nxm_header);
+        nxm_init_add_field(mf, mf->oxm_header);
     }
 
 #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_verify_field(mf, mf->nxm_header);
+        nxm_init_verify_field(mf, mf->oxm_header);
     }
 #endif
 }
@@ -518,13 +549,13 @@ const struct mf_field *
 mf_from_nxm_header(uint32_t header)
 {
     const struct nxm_field *f;
+    struct hmap *all_fields = get_all_fields(header);
 
-    if (hmap_is_empty(&all_nxm_fields)) {
+    if (hmap_is_empty(all_fields)) {
         nxm_init();
     }
 
-    HMAP_FOR_EACH_IN_BUCKET (f, hmap_node, hash_int(header, 0),
-                             &all_nxm_fields) {
+    HMAP_FOR_EACH_IN_BUCKET (f, hmap_node, hash_int(header, 0), all_fields) {
         if (f->nxm_header == header) {
             return f->mf;
         }
@@ -543,7 +574,6 @@ 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:
@@ -590,9 +620,10 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
 #endif
         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_VLAN_TCI:
         return !wc->vlan_tci_mask;
@@ -651,7 +682,6 @@ 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:
@@ -702,8 +732,11 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
         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:
@@ -781,14 +814,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();
@@ -1532,13 +1557,13 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
 #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:
@@ -1678,7 +1703,6 @@ 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_VLAN_PCP:
@@ -1734,9 +1758,11 @@ mf_set(const struct mf_field *mf,
         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_VLAN_TCI:
@@ -2060,12 +2086,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;
@@ -2273,10 +2301,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:
index 632cb46..ffde5cc 100644 (file)
@@ -144,8 +144,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. */
index 4d2f3ac..61363c6 100644 (file)
@@ -181,7 +181,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
@@ -221,7 +221,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.
@@ -232,7 +232,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'.
@@ -248,7 +248,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
@@ -262,7 +262,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'.
@@ -1845,7 +1845,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));
@@ -1864,7 +1864,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));
@@ -1901,7 +1901,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));
@@ -1920,7 +1920,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));
@@ -2002,7 +2002,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;
 
@@ -2014,10 +2014,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) {
@@ -2026,7 +2026,7 @@ netdev_linux_dump_queues(const struct netdev *netdev,
             last_error = error;
         }
     }
-    shash_destroy(&details);
+    smap_destroy(&details);
 
     return last_error;
 }
@@ -2271,7 +2271,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 =
@@ -2279,17 +2279,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;
 }
 
@@ -2651,11 +2652,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;
@@ -2670,13 +2671,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);
@@ -2734,7 +2735,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;
 
@@ -2826,15 +2827,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;
@@ -2850,24 +2851,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;
@@ -3127,13 +3128,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) {
@@ -3149,7 +3150,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;
@@ -3157,8 +3158,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);
@@ -3259,7 +3260,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;
@@ -3327,16 +3328,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;
@@ -3354,21 +3355,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;
@@ -3473,7 +3474,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;
index 080b76a..5fb1bbf 100644 (file)
@@ -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. */
@@ -381,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'.
@@ -401,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
@@ -420,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
@@ -440,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'.
      *
@@ -475,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);
 
@@ -550,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
index 1721f6b..7fe169e 100644 (file)
@@ -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,60 @@ 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') {
+                    nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, tos);
+                }
             }
-        } 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 +656,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,19 +664,19 @@ 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);
         }
     }
 
@@ -687,7 +691,7 @@ parse_tunnel_config(const char *name, const char *type,
             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 +758,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 +777,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 +791,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 +811,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 +839,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 +872,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 e4e7ab1..1b76785 100644 (file)
@@ -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"
@@ -243,15 +244,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));
     }
@@ -260,23 +261,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;
@@ -759,18 +760,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);
 }
 
@@ -1065,10 +1066,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'.
  *
@@ -1078,7 +1078,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;
@@ -1087,7 +1087,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 {
@@ -1117,7 +1117,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;
 
@@ -1127,7 +1127,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);
@@ -1147,12 +1147,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;
@@ -1161,7 +1161,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;
 }
@@ -1180,7 +1180,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 4b86c21..d2cc8b5 100644 (file)
@@ -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 34c8354..920184c 100644 (file)
@@ -127,7 +127,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 +148,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. */
@@ -354,20 +365,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 +419,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 +428,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));
@@ -462,7 +478,7 @@ 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,
+nx_put_match(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;
@@ -471,72 +487,91 @@ 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 == 11);
 
     /* 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. */
+    /* 802.1Q.
+     *
+     * XXX missing OXM support */
     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);
+        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);
 
         if (!(wc & FWW_IPV6_LABEL)) {
-            nxm_put_32(b, NXM_NX_IPV6_LABEL, flow->ipv6_label);
+            nxm_put_32(b, oxm ? OXM_OF_IPV6_FLABEL : NXM_NX_IPV6_LABEL,
+                       flow->ipv6_label);
         }
 
         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);
+            nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_ND_TARGET : 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_eth(b, oxm ? OXM_OF_IPV6_ND_SLL : NXM_NX_ND_SLL,
+                            flow->arp_sha);
             }
             if (!(wc & FWW_ARP_THA)
                 && flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
-                nxm_put_eth(b, NXM_NX_ND_TLL, flow->arp_tha);
+                nxm_put_eth(b, oxm ? OXM_OF_IPV6_ND_TLL : NXM_NX_ND_TLL,
+                            flow->arp_tha);
             }
         }
     } 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_16(b, oxm ? OXM_OF_ARP_OP : 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);
+        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);
         if (!(wc & FWW_ARP_SHA)) {
-            nxm_put_eth(b, NXM_NX_ARP_SHA, flow->arp_sha);
+            nxm_put_eth(b, oxm ? OXM_OF_ARP_SHA : 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_eth(b, oxm ? OXM_OF_ARP_THA : NXM_NX_ARP_THA,
+                        flow->arp_tha);
         }
     }
 
@@ -617,7 +652,7 @@ 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 +679,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 {
index a039225..22db477 100644 (file)
@@ -43,7 +43,7 @@ enum ofperr nx_pull_match(struct ofpbuf *, unsigned int match_len,
 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);
-int nx_put_match(struct ofpbuf *, const struct cls_rule *,
+int nx_put_match(struct ofpbuf *, bool oxm, const struct cls_rule *,
                  ovs_be64 cookie, ovs_be64 cookie_mask);
 
 char *nx_match_to_string(const uint8_t *, unsigned int match_len);
@@ -90,7 +90,7 @@ 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);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11);
 /* Upper bound on the length of an nx_match.  The longest nx_match (an
  * IPV6 neighbor discovery message using 5 registers) would be:
  *
@@ -98,7 +98,7 @@ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
  *                   ------  -----  ----  -----
  *  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_SRC_W    4       6     6     16
  *  NXM_OF_ETH_TYPE     4       2    --      6
  *  NXM_OF_VLAN_TCI     4       2     2      8
  *  NXM_OF_IP_TOS       4       1    --      5
@@ -123,7 +123,7 @@ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
  *  NXM_NX_REG_W(7)     4       4     4     12
  *  NXM_NX_TUN_ID_W     4       8     8     20
  *  -------------------------------------------
- *  total                                  327
+ *  total                                  333
  *
  * So this value is conservative.
  */
index ce45635..0574c9f 100644 (file)
@@ -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);
 
@@ -355,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
@@ -380,12 +379,11 @@ 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;
         }
     }
@@ -561,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;
@@ -878,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
@@ -925,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;
         }
     }
@@ -1209,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;
@@ -1248,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)
 {
index 08b0a2a..f902b76 100644 (file)
@@ -30,7 +30,7 @@ struct ds;
 struct flow;
 struct nlattr;
 struct ofpbuf;
-struct shash;
+struct simap;
 
 #define OVSP_NONE ((uint16_t) -1)
 
@@ -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 *);
index fc86442..73a70c6 100644 (file)
@@ -592,6 +592,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;
@@ -646,17 +652,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")
@@ -670,6 +683,13 @@ 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;
 
index 1757a30..9f77c5a 100644 (file)
@@ -826,25 +826,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,");
@@ -865,44 +865,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) == ',') {
@@ -952,7 +954,7 @@ 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);
+        ofp10_match_print(s, &ofm->match, verbosity);
 
         /* ofp_print_match() doesn't print priority. */
         need_priority = true;
@@ -977,8 +979,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);
index 8704f57..ae868a4 100644 (file)
@@ -23,7 +23,7 @@
 #include <stdio.h>
 
 struct ofp_flow_mod;
-struct ofp_match;
+struct ofp10_match;
 struct ds;
 union ofp_action;
 
@@ -35,10 +35,10 @@ 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);
 
 #ifdef  __cplusplus
index 501ccab..6d820b2 100644 (file)
@@ -64,26 +64,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 +96,66 @@ 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 == 11);
 
     /* 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. */
+    /* Wildcard fields that aren't defined by ofp10_match or tun_id. */
     wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_ECN | FWW_NW_TTL
                       | FWW_IPV6_LABEL);
 
-    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 +170,12 @@ 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(OFP_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 +194,53 @@ 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);
     } 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,8 +262,265 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
     memset(match->pad2, '\0', sizeof match->pad2);
 }
 
+/* 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;
+
+    cls_rule_init_catchall(rule, priority);
+
+    if (!(wc & OFPFW11_IN_PORT)) {
+        uint16_t ofp_port;
+        enum ofperr error;
+
+        error = ofputil_port_from_ofp11(match->in_port, &ofp_port);
+        if (error) {
+            return OFPERR_OFPBMC_BAD_VALUE;
+        }
+        cls_rule_set_in_port(rule, ofp_port);
+    }
+
+    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);
+
+    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);
+
+    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 (!(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;
+                }
+            }
+        }
+    }
+
+    if (!(wc & OFPFW11_DL_TYPE)) {
+        cls_rule_set_dl_type(rule,
+                             ofputil_dl_type_from_openflow(match->dl_type));
+    }
+
+    ipv4 = rule->flow.dl_type == htons(ETH_TYPE_IP);
+    arp = rule->flow.dl_type == htons(ETH_TYPE_ARP);
+
+    if (ipv4 && !(wc & OFPFW11_NW_TOS)) {
+        if (match->nw_tos & ~IP_DSCP_MASK) {
+            /* Invalid TOS. */
+            return OFPERR_OFPBMC_BAD_VALUE;
+        }
+
+        cls_rule_set_nw_dscp(rule, match->nw_tos);
+    }
+
+    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);
+    }
+
+#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;
+
+        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;
+
+        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;
+
+        default:
+            /* OF1.1 says explicitly to ignore this. */
+            break;
+        }
+    }
+
+    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 };
+
+        if ((wc & OFPFW11_MPLS_ALL) != OFPFW11_MPLS_ALL) {
+            /* MPLS not supported. */
+            return OFPERR_OFPBMC_BAD_TAG;
+        }
+    }
+
+    if (match->metadata_mask != htonll(UINT64_MAX)) {
+        /* Metadata field not yet supported because we haven't decided how to
+         * map it onto our existing fields (or whether to add a new field). */
+        return OFPERR_OFPBMC_BAD_FIELD;
+    }
+
+    return 0;
+}
+
+/* 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)
+{
+    uint32_t wc = 0;
+    int i;
+
+    memset(match, 0, sizeof *match);
+    match->omh.type = htons(OFPMT_STANDARD);
+    match->omh.length = htons(OFPMT11_STANDARD_LENGTH);
+
+    if (rule->wc.wildcards & FWW_IN_PORT) {
+        wc |= OFPFW11_IN_PORT;
+    } else {
+        match->in_port = ofputil_port_to_ofp11(rule->flow.in_port);
+    }
+
+
+    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];
+    }
+
+    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];
+    }
+
+    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));
+        }
+
+        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);
+        }
+    }
+
+    if (rule->wc.wildcards & FWW_DL_TYPE) {
+        wc |= OFPFW11_DL_TYPE;
+    } else {
+        match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type);
+    }
+
+    if (rule->wc.wildcards & FWW_NW_DSCP) {
+        wc |= OFPFW11_NW_TOS;
+    } else {
+        match->nw_tos = rule->flow.nw_tos & IP_DSCP_MASK;
+    }
+
+    if (rule->wc.wildcards & FWW_NW_PROTO) {
+        wc |= OFPFW11_NW_PROTO;
+    } else {
+        match->nw_proto = rule->flow.nw_proto;
+    }
+
+    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;
+
+    if (!rule->wc.tp_src_mask) {
+        wc |= OFPFW11_TP_SRC;
+    } else {
+        match->tp_src = rule->flow.tp_src;
+    }
+
+    if (!rule->wc.tp_dst_mask) {
+        wc |= OFPFW11_TP_DST;
+    } else {
+        match->tp_dst = rule->flow.tp_dst;
+    }
+
+    /* MPLS not supported. */
+    wc |= OFPFW11_MPLS_LABEL;
+    wc |= OFPFW11_MPLS_TC;
+
+    /* Metadata field not yet supported */
+    match->metadata_mask = htonll(UINT64_MAX);
+
+    match->wildcards = htonl(wc);
+}
+
 /* 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. */
+ * 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)
 {
@@ -261,7 +529,7 @@ ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type)
             : flow_dl_type);
 }
 
-/* Given a 'dl_type' value in the format used in an OpenFlow ofp_match
+/* 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
@@ -1174,10 +1442,15 @@ 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 == 11);
 
-    /* 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;
     }
 
@@ -1222,6 +1495,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))) {
@@ -1393,18 +1671,19 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
          * 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))) {
+        if (!(ofm->match.wildcards & htonl(OFPFW10_ALL))) {
             priority = UINT16_MAX;
         }
 
         /* Translate the rule. */
-        ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr);
+        ofputil_cls_rule_from_ofp10_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);
+        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);
@@ -1429,17 +1708,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
 
         /* 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);
-            }
+        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);
@@ -1461,10 +1735,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
 }
 
 /* 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)
@@ -1485,8 +1756,8 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
     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;
+        ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match);
+        ofm->cookie = fm->new_cookie;
         ofm->command = htons(command);
         ofm->idle_timeout = htons(fm->idle_timeout);
         ofm->hard_timeout = htons(fm->hard_timeout);
@@ -1502,14 +1773,9 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         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);
-        }
+        nfm->cookie = fm->new_cookie;
+        match_len = nx_put_match(msg, false, &fm->cr,
+                                 fm->cookie, fm->cookie_mask);
         nfm->idle_timeout = htons(fm->idle_timeout);
         nfm->hard_timeout = htons(fm->hard_timeout);
         nfm->priority = htons(fm->cr.priority);
@@ -1548,7 +1814,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;
         }
     }
@@ -1566,7 +1834,7 @@ ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
         (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);
@@ -1653,7 +1921,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
 
         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);
+        ofputil_cls_rule_to_ofp10_match(&fsr->match, &ofsr->match);
         ofsr->table_id = fsr->table_id;
         ofsr->out_port = htons(fsr->out_port);
         break;
@@ -1667,7 +1935,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
 
         subtype = fsr->aggregate ? NXST_AGGREGATE : NXST_FLOW;
         ofputil_make_stats_request(sizeof *nfsr, OFPST_VENDOR, subtype, &msg);
-        match_len = nx_put_match(msg, &fsr->match,
+        match_len = nx_put_match(msg, false, &fsr->match,
                                  fsr->cookie, fsr->cookie_mask);
 
         nfsr = msg->data;
@@ -1764,8 +2032,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         }
 
         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);
@@ -1858,7 +2126,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         ofs->length = htons(len);
         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);
@@ -1894,7 +2162,7 @@ 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(nx_put_match(msg, false, &fs->rule, 0, 0));
         nfs->cookie = fs->cookie;
         nfs->packet_count = htonll(fs->packet_count);
         nfs->byte_count = htonll(fs->byte_count);
@@ -1954,8 +2222,8 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
         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);
+        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);
@@ -2010,7 +2278,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
 
         ofr = make_openflow_xid(sizeof *ofr, OFPT_FLOW_REMOVED, htonl(0),
                                 &msg);
-        ofputil_cls_rule_to_match(&fr->rule, &ofr->match);
+        ofputil_cls_rule_to_ofp10_match(&fr->rule, &ofr->match);
         ofr->cookie = fr->cookie;
         ofr->priority = htons(fr->rule.priority);
         ofr->reason = fr->reason;
@@ -2028,7 +2296,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
         int match_len;
 
         make_nxmsg_xid(sizeof *nfr, NXT_FLOW_REMOVED, htonl(0), &msg);
-        match_len = nx_put_match(msg, &fr->rule, 0, 0);
+        match_len = nx_put_match(msg, false, &fr->rule, 0, 0);
 
         nfr = msg->data;
         nfr->cookie = fr->cookie;
@@ -2050,7 +2318,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
     return msg;
 }
 
-int
+enum ofperr
 ofputil_decode_packet_in(struct ofputil_packet_in *pin,
                          const struct ofp_header *oh)
 {
@@ -2082,7 +2350,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
 
         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;
         }
@@ -2163,7 +2431,7 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
         cls_rule_set_in_port(&rule, pin->fmd.in_port);
 
         ofpbuf_put_zeros(packet, sizeof *npi);
-        match_len = nx_put_match(packet, &rule, 0, 0);
+        match_len = nx_put_match(packet, false, &rule, 0, 0);
         ofpbuf_put_zeros(packet, 2);
         ofpbuf_put(packet, pin->packet, send_len);
 
@@ -3128,7 +3396,7 @@ make_flow_mod(uint16_t command, const struct cls_rule *rule,
     ofm->header.length = htons(size);
     ofm->cookie = 0;
     ofm->priority = htons(MIN(rule->priority, UINT16_MAX));
-    ofputil_cls_rule_to_match(rule, &ofm->match);
+    ofputil_cls_rule_to_ofp10_match(rule, &ofm->match);
     ofm->command = htons(command);
     return out;
 }
@@ -3145,32 +3413,6 @@ make_add_flow(const struct cls_rule *rule, uint32_t buffer_id,
     return out;
 }
 
-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;
-}
-
-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;
-
-        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);
-    }
-}
-
 struct ofpbuf *
 make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason,
                const struct ofpbuf *payload, int max_send_len)
index e671663..aaab83c 100644 (file)
@@ -112,8 +112,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);
 
@@ -177,12 +177,21 @@ 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_cls_rule_to_ofp10_match(const struct cls_rule *,
+                                     struct ofp10_match *);
+
+/* Work with ofp11_match. */
+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);
@@ -197,11 +206,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;
@@ -307,8 +334,8 @@ 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 nx_packet_in_format);
 
@@ -519,10 +546,6 @@ 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);
index 95254b7..546acbb 100644 (file)
@@ -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 3d4cbd4..3eca2fe 100644 (file)
@@ -906,6 +906,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);
index 84ca590..5729167 100644 (file)
@@ -43,6 +43,71 @@ dpid_from_string(const char *s, uint64_t *dpidp)
     return *dpidp != 0;
 }
 
+/* Returns true if 'ea' is a reserved multicast address, that a bridge must
+ * never forward, false otherwise.  Includes some proprietary vendor protocols
+ * that shouldn't be forwarded as well.
+ *
+ * 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 masked_eth_addr {
+        uint8_t ea[ETH_ADDR_LEN];
+        uint8_t mask[ETH_ADDR_LEN];
+    };
+
+    static struct masked_eth_addr mea[] = {
+        { /* STP, IEEE pause frames, and other reserved protocols. */
+            {0x01, 0x08, 0xc2, 0x00, 0x00, 0x00},
+            {0xff, 0xff, 0xff, 0xff, 0xff, 0xf0}},
+
+        { /* VRRP IPv4. */
+            {0x00, 0x00, 0x5e, 0x00, 0x01, 0x00},
+            {0xff, 0xff, 0xff, 0xff, 0xff, 0x00}},
+
+        { /* VRRP IPv6. */
+            {0x00, 0x00, 0x5e, 0x00, 0x02, 0x00},
+            {0xff, 0xff, 0xff, 0xff, 0xff, 0x00}},
+
+        { /* HSRPv1. */
+            {0x00, 0x00, 0x0c, 0x07, 0xac, 0x00},
+            {0xff, 0xff, 0xff, 0xff, 0xff, 0x00}},
+
+        { /* HSRPv2. */
+            {0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x00},
+            {0xff, 0xff, 0xff, 0xff, 0xf0, 0x00}},
+
+        { /* GLBP. */
+            {0x00, 0x07, 0xb4, 0x00, 0x00, 0x00},
+            {0xff, 0xff, 0xff, 0x00, 0x00, 0x00}},
+
+        { /* Extreme Discovery Protocol. */
+            {0x00, 0xE0, 0x2B, 0x00, 0x00, 0x00},
+            {0xff, 0xff, 0xff, 0xff, 0xf0, 0x00}},
+
+        { /* Cisco Inter Switch Link. */
+            {0x01, 0x00, 0x0c, 0x00, 0x00, 0x00},
+            {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+
+        { /* Cisco 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) */
+            {0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc},
+            {0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe}}};
+
+    size_t i;
+
+    for (i = 0; i < ARRAY_SIZE(mea); i++) {
+        if (eth_addr_equal_except(ea, mea[i].ea, mea[i].mask)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 bool
 eth_addr_from_string(const char *s, uint8_t ea[ETH_ADDR_LEN])
 {
@@ -55,27 +120,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 rarp_header *rarp;
 
-    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
+                             + RARP_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);
+
+    rarp = ofpbuf_put_uninit(b, sizeof *rarp);
+    rarp->hw_addr_space = htons(ARP_HTYPE_ETH);
+    rarp->proto_addr_space = htons(ETH_TYPE_IP);
+    rarp->hw_addr_length = ETH_ADDR_LEN;
+    rarp->proto_addr_length = sizeof rarp->src_proto_addr;
+    rarp->opcode = htons(RARP_REQUEST_REVERSE);
+    memcpy(rarp->src_hw_addr, eth_src, ETH_ADDR_LEN);
+    rarp->src_proto_addr = htonl(0);
+    memcpy(rarp->target_hw_addr, eth_src, ETH_ADDR_LEN);
+    rarp->target_proto_addr = htonl(0);
 }
 
 /* Insert VLAN header according to given TCI. Packet passed must be Ethernet
@@ -148,13 +223,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 +350,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 +361,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 +436,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 f9e5bb6..ad5631d 100644 (file)
@@ -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. */
@@ -216,6 +229,25 @@ struct llc_snap_header {
 } __attribute__((packed));
 BUILD_ASSERT_DECL(LLC_SNAP_HEADER_LEN == sizeof(struct llc_snap_header));
 
+#define ARP_HTYPE_ETH 0x0001
+#define RARP_REQUEST_REVERSE 0x0003
+
+#define RARP_HEADER_LEN 28
+/* RARP header only for Ethernet-IP. */
+struct rarp_header {
+    ovs_be16 hw_addr_space;        /* ARP_HTYPE_ETH. */
+    ovs_be16 proto_addr_space;     /* ETH_TYPE_IP. */
+    uint8_t hw_addr_length;        /* ETH_ADDR_LEN. */
+    uint8_t proto_addr_length;     /* IPV4_ADDR_LEN. */
+    ovs_be16 opcode;               /* RARP_REQUEST_REVERSE. */
+    uint8_t src_hw_addr[ETH_ADDR_LEN];
+    ovs_be32 src_proto_addr;
+    uint8_t target_hw_addr[ETH_ADDR_LEN];
+    ovs_be32 target_proto_addr;
+} __attribute__((packed));
+BUILD_ASSERT_DECL(RARP_HEADER_LEN == sizeof(struct rarp_header));
+
+
 #define VLAN_VID_MASK 0x0fff
 #define VLAN_VID_SHIFT 0
 
@@ -307,6 +339,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
index ba6c3a1..516cf13 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, 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.
@@ -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 aa8b7e3..2ddfc69 100644 (file)
@@ -855,6 +855,13 @@ 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)
index 2397640..2b1332c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, 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.
@@ -91,6 +91,7 @@ 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. */
 struct rconn_packet_counter {
index af917b3..7480176 100644 (file)
@@ -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 decfcbc..97d36ba 100644 (file)
@@ -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
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..8665321
--- /dev/null
@@ -0,0 +1,269 @@
+/* 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"
+
+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);
+}
+
+/* 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;
+    }
+}
+\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..51f6397
--- /dev/null
@@ -0,0 +1,63 @@
+/* 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"
+
+/* 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 *smap, struct smap_node *);
+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 *);
+
+#endif /* smap.h */
index a7d9e48..899072e 100644 (file)
@@ -477,6 +477,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,7 +485,13 @@ 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) {
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]
index e80d20c..a0315b2 100644 (file)
@@ -33,6 +33,7 @@
 #include "pktbuf.h"
 #include "rconn.h"
 #include "shash.h"
+#include "simap.h"
 #include "stream.h"
 #include "timeval.h"
 #include "vconn.h"
@@ -338,6 +339,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)
index 8ac0b8d..dec5b71 100644 (file)
@@ -30,6 +30,7 @@ struct ofopgroup;
 struct ofputil_flow_removed;
 struct ofputil_packet_in;
 struct ofputil_phy_port;
+struct simap;
 struct sset;
 
 /* ofproto supports two kinds of OpenFlow connections:
@@ -70,6 +71,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 *);
index 99bbccd..912dc4e 100644 (file)
@@ -121,7 +121,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;
index 7e3ecfa..458f8d7 100644 (file)
@@ -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
@@ -162,7 +164,7 @@ 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",
index be1a45c..962df15 100644 (file)
@@ -47,6 +47,7 @@
 #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"
@@ -790,6 +791,7 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
 
     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;
@@ -1056,6 +1058,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_)
 {
@@ -5845,47 +5856,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)
 {
@@ -5960,8 +5930,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) {
@@ -6119,7 +6089,7 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
 
     /* 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)) {
         return false;
     }
 
@@ -7098,6 +7068,7 @@ const struct ofproto_class ofproto_dpif_class = {
     run,
     run_fast,
     wait,
+    get_memory_usage,
     flush,
     get_features,
     get_tables,
index a74bf9a..3551fc9 100644 (file)
@@ -30,6 +30,7 @@
 #include "timeval.h"
 
 struct ofputil_flow_mod;
+struct simap;
 
 /* An OpenFlow switch.
  *
@@ -396,6 +397,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 +435,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.
      *
index 60dd36a..f538869 100644 (file)
@@ -45,6 +45,7 @@
 #include "poll-loop.h"
 #include "random.h"
 #include "shash.h"
+#include "simap.h"
 #include "sset.h"
 #include "timeval.h"
 #include "unaligned.h"
@@ -116,7 +117,6 @@ 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. */
@@ -1149,6 +1149,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)
@@ -2112,7 +2137,7 @@ handle_table_stats_request(struct ofconn *ofconn,
     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));
     }
@@ -2759,7 +2784,7 @@ 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;
@@ -2859,7 +2884,9 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
         } else {
             rule->modified = time_msec();
         }
-        rule->flow_cookie = fm->cookie;
+        if (fm->new_cookie != htonll(UINT64_MAX)) {
+            rule->flow_cookie = fm->new_cookie;
+        }
     }
     ofopgroup_submit(group);
 
@@ -2882,9 +2909,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
@@ -2903,11 +2934,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. */
@@ -3053,8 +3089,8 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
         return error;
     }
 
-    /* We do not support the emergency flow cache.  It will hopefully get
-     * dropped from OpenFlow in the near future. */
+    /* 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. */
     if (fm.flags & OFPFF_EMERG) {
         /* There isn't a good fit for an error code, so just state that the
          * flow table is full. */
@@ -3472,7 +3508,6 @@ 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->flow_cookie = rule->flow_cookie;
 
     if (type == OFOPERATION_DELETE) {
@@ -3538,7 +3573,6 @@ ofoperation_complete(struct ofoperation *op, enum ofperr error)
     struct ofproto *ofproto = rule->ofproto;
 
     assert(rule->pending == op);
-    assert(op->status < 0);
 
     if (!error
         && !group->error
@@ -3580,6 +3614,7 @@ ofoperation_complete(struct ofoperation *op, enum ofperr error)
         } else {
             oftable_substitute_rule(rule, op->victim);
             ofproto_rule_destroy__(rule);
+            op->rule = NULL;
         }
         break;
 
index c8d9857..ea988e7 100644 (file)
@@ -39,6 +39,7 @@ struct netdev;
 struct ofproto;
 struct ofport;
 struct shash;
+struct simap;
 struct netdev_stats;
 
 struct ofproto_controller_info {
@@ -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(). */
index 9053ea2..41e9c8d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, 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.
@@ -320,3 +320,11 @@ pinsched_set_limits(struct pinsched *ps, int rate_limit, int 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 26a4d7a..061cb01 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, 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.
@@ -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 acc0d34..71be34a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, 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.
@@ -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 990f2ea..ec99aea 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
+ * 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 0cc8bdf..bb887d0 100644 (file)
@@ -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 8312a00..2dc0c78 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira, 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.
@@ -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 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 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 28bf901..7f53e17 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira, 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.
@@ -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"
@@ -143,6 +145,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 +170,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());
index 584433c..6b53f4a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira, 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.
@@ -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 ea7a9c2..6e4ff79 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira, 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.
@@ -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 59db000..96869e3 100644 (file)
@@ -1,5 +1,3 @@
-run_python = PYTHONPATH=$(top_srcdir)/python:$$PYTHON_PATH $(PYTHON)
-
 ovstest_pyfiles = \
        python/ovstest/__init__.py \
        python/ovstest/args.py \
@@ -29,7 +27,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
index 96cdae8..5865acd 100644 (file)
@@ -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))
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 75%
rename from python/ovs/unixctl.py
rename to python/ovs/unixctl/server.py
index 0fadaeb..18e1cf2 100644 (file)
@@ -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 b585591..ed95012 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (c) 2011 Nicira, 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.
 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
@@ -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 6642f28..ad7579c 100755 (executable)
@@ -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 2fdd6c4..46b94b9 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011 Nicira, 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 12045cb..54442fb 100644 (file)
@@ -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
 
@@ -152,5 +152,6 @@ exit 0
 /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 812060d..cad1f53 100644 (file)
@@ -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 e5698ef..e5bcf2c 100644 (file)
@@ -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..81d2942 100644 (file)
@@ -74,7 +74,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 '----------------------------------------------------------------------'
@@ -192,7 +192,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 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 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
index caedc94..9617af2 100644 (file)
@@ -46,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
index 4b94fb4..b302dee 100644 (file)
@@ -365,7 +365,8 @@ 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.
+# The flow is formatted with ofp10_match_to_string() for the
+# high-verbosity case.
 AT_SETUP([OFPT_FLOW_MOD - high verbosity])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
@@ -833,7 +834,8 @@ NXT_FLOW_MOD (xid=0x2): ADD reg0=0x7b,tun_id=0x1c8 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 "\
index 84868d7..dbe31c4 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
@@ -55,7 +55,7 @@ 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
 ])
@@ -68,12 +68,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
@@ -147,11 +154,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
@@ -182,7 +189,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])
@@ -194,7 +315,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
@@ -203,7 +324,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])
index befa8b7..e9d6389 100644 (file)
@@ -32,7 +32,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..a4fa7e4 100644 (file)
@@ -230,9 +230,12 @@ 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(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)
 
 # eth type
 NXM_OF_ETH_TYPE(0800)
@@ -271,12 +274,14 @@ 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(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_IP_DST(ac100014)
 NXM_OF_ETH_TYPE(0806) NXM_OF_IP_DST_W(C0D80000/FFFF0000)
 
@@ -320,12 +325,14 @@ 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(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(0800) NXM_OF_ARP_TPA(ac100014)
 NXM_OF_ARP_TPA_W(C0D80000/FFFF0000)
 
@@ -343,14 +350,21 @@ 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(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(0800) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
 
+# 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(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)
@@ -427,9 +441,12 @@ NXM_OF_ETH_DST_W(000000000000/010000000000)
 NXM_OF_ETH_DST_W(010000000000/010000000000)
 NXM_OF_ETH_DST(0002e30f80a4)
 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)
 
 # eth type
 NXM_OF_ETH_TYPE(0800)
@@ -468,12 +485,14 @@ 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)
 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)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
@@ -517,12 +536,14 @@ 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)
 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)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
@@ -540,14 +561,21 @@ 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)
 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)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
+# 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(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)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
@@ -612,6 +640,316 @@ nx_pull_match() returned error OFPBMC_BAD_FIELD
 ])
 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 not yet supported:
+# bad ofp11_match: OFPBMC_BAD_FIELD
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000001fffffffffffffffe
+
+])
+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,6 +965,285 @@ NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800)
 ])
 AT_CLEANUP
 
+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)
+
+# 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/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)
+
+# 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(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_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(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(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(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(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(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(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)
+
+# 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)
+
+# 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(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(0800) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+
+# 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)
+
+# 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)
+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)
+
+# 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)
+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)
+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)
+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)
+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)
+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)
+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)
+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)
+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
+
+# 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
+
+# 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)
+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)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# 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 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])
@@ -682,3 +1299,4 @@ ofp_util|INFO|post: @&t@
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
index 7872bfe..b9346cc 100644 (file)
@@ -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], ...)
@@ -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([ovs-vsctl --bare --timeout=5 --no-wait -vreconnect:emer --db=unix:socket -- --columns=name find bridge '$1' | sort | xargs echo], [0], [$2
 ])])
 
 # Arithmetic relational operators without keys.
@@ -985,19 +985,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 \
index c57bbc9..5fa61ec 100644 (file)
@@ -21,7 +21,7 @@ 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 fcafdb2..25f6edb 100644 (file)
@@ -52,8 +52,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,6 +202,12 @@ 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);
@@ -471,6 +477,10 @@ 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) {
index 59f88fd..33417e0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011 Nicira, 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.
@@ -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 2b4cfe1..dd80766 100644 (file)
@@ -28,6 +28,7 @@
 static int
 parse_keys(void)
 {
+    int exit_code = 0;
     struct ds in;
 
     ds_init(&in);
@@ -71,6 +72,12 @@ parse_keys(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);
@@ -82,7 +89,7 @@ parse_keys(void)
     }
     ds_destroy(&in);
 
-    return 0;
+    return exit_code;
 }
 
 static int
index 038c1bd..ab03479 100644 (file)
@@ -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 f9caa60..0374602 100644 (file)
@@ -104,7 +104,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])
index bb3b3c1..27ec79e 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>,/' \
+-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,124 @@ 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_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+trap 'kill `cat test-unixctl.py.pid`' 0
+
+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])
+trap '' 0
+
+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_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+trap 'kill `cat test-unixctl.py.pid`' 0
+
+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_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+trap 'kill `cat test-unixctl.py.pid`' 0
+
+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_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+trap 'kill `cat test-unixctl.py.pid`' 0
+
+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 d9d4419..90f47ff 100644 (file)
@@ -7,6 +7,7 @@
 /ovs-benchmark.1
 /ovs-cfg-mod
 /ovs-cfg-mod.8
+/ovs-check-dead-ifs
 /ovs-controller
 /ovs-controller.8
 /ovs-ctl
index c09380d..cd422aa 100644 (file)
@@ -15,6 +15,7 @@ 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 \
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 b8f086f..1316071 100644 (file)
@@ -24,4 +24,5 @@
   <command label="ovs-appctl-cfm-show">/usr/share/openvswitch/scripts/ovs-bugtool-cfm-show</command>
   <command label="ovs-appctl-coverage-show">/usr/share/openvswitch/scripts/ovs-bugtool-coverage-show</command>
   <command label="ovs-appctl-bond-show">/usr/share/openvswitch/scripts/ovs-bugtool-bond-show</command>
+  <command label="ovs-appctl-memory-show">/usr/share/openvswitch/scripts/ovs-bugtool-memory-show</command>
 </collect>
index 4606f57..c2ed26b 100644 (file)
@@ -214,6 +214,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
@@ -228,7 +232,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 d70b630..04b16e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, 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.
@@ -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"
@@ -62,7 +62,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 +76,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;
@@ -274,8 +274,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 +397,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 988b500..1a9ee2a 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
index 0af90f0..552cef3 100755 (executable)
@@ -73,7 +73,7 @@ ovs_vsctl () {
 }
 
 ovsdb_tool () {
-    ovsdb-tool -vANY:console:off "$@"
+    ovsdb-tool -vconsole:off "$@"
 }
 
 create_db () {
@@ -182,13 +182,14 @@ start () {
 
        # Start ovsdb-server.
        set ovsdb-server "$DB_FILE"
-       set "$@" -vANY:CONSOLE:EMER -vANY:SYSLOG:ERR -vANY:FILE:INFO
+       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" "$@" || return 1
+       start_daemon "$OVSDB_SERVER_PRIORITY" "$OVSDB_SERVER_WRAPPER" "$@" \
+            || return 1
 
        # Initialize database settings.
        ovs_vsctl -- init -- set Open_vSwitch . db-version="$schemaver" \
@@ -211,19 +212,19 @@ start () {
 
        # Start ovs-vswitchd.
        set ovs-vswitchd unix:"$DB_SOCK"
-       set "$@" -vANY:CONSOLE:EMER -vANY:SYSLOG:ERR -vANY:FILE:INFO
+       set "$@" -vconsole:emer -vsyslog:err -vfile:info
        if test X"$MLOCKALL" != Xno; then
            set "$@" --mlockall
        fi
-       start_daemon "$OVS_VSWITCHD_PRIORITY" "$@"
+       start_daemon "$OVS_VSWITCHD_PRIORITY" "$OVS_VSWITCHD_WRAPPER" "$@"
     fi
 
     if daemon_is_running ovs-brcompatd; then
        log_success_msg "ovs-brcompatd is already running"
     elif test X"$BRCOMPAT" = Xyes; then
         set ovs-brcompatd
-       set "$@" -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
 }
 
@@ -377,6 +378,9 @@ 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_SOCK=$rundir/db.sock
@@ -442,6 +446,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
 
index 7c19116..950e8f4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, 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.
@@ -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"
@@ -245,7 +247,7 @@ 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;
         char *option;
         int error;
 
@@ -258,7 +260,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 +273,7 @@ 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 (!smap_add_once(&args, key, value)) {
                 ovs_error(0, "duplicate \"%s\" option", key);
             }
         }
@@ -322,7 +324,7 @@ do_set_if(int argc, char *argv[])
         char *save_ptr = NULL;
         char *type = NULL;
         const char *name;
-        struct shash args;
+        struct smap args;
         char *option;
         int error;
 
@@ -349,7 +351,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);
@@ -374,9 +376,9 @@ do_set_if(int argc, char *argv[])
                     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);
             }
         }
 
@@ -499,26 +501,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 {
@@ -826,7 +828,7 @@ sort_output_actions(struct nlattr *actions, size_t length)
 static void
 do_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 +843,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 +851,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]);
         }
index b8dc060..f8e2609 100644 (file)
@@ -88,7 +88,8 @@ pid_exists () {
 
 start_daemon () {
     priority=$1
-    shift
+    wrapper=$2
+    shift; shift
     daemon=$1
 
     # drop core files in a sensible place
@@ -105,6 +106,30 @@ 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
+                set strace -D -ff -o "$logdir/$daemon.strace.log" "$@"
+            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" "$@"
index 4f54208..9c4ea0c 100644 (file)
@@ -79,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,
@@ -363,11 +381,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,
@@ -398,7 +418,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
@@ -654,7 +676,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
@@ -1076,26 +1102,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
index 92808d9..7413455 100644 (file)
@@ -1270,10 +1270,29 @@ do_packet_out(int argc, char *argv[])
 static void
 do_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);
 
@@ -1283,25 +1302,26 @@ 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);
@@ -1593,7 +1613,7 @@ 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);
@@ -1702,7 +1722,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;
@@ -1897,11 +1919,8 @@ do_parse_flows(int argc OVS_UNUSED, char *argv[])
     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)
+do_parse_nxm__(bool oxm)
 {
     struct ds in;
 
@@ -1932,7 +1951,8 @@ do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
             /* 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);
+            match_len = nx_put_match(&nx_match, oxm, &rule,
+                                     cookie, cookie_mask);
 
             /* Convert nx_match to string. */
             out = nx_match_to_string(nx_match.data, match_len);
@@ -1948,6 +1968,81 @@ 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
+do_parse_nxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    return do_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
+do_parse_oxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+    return do_parse_nxm__(true);
+}
+
+/* "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
+do_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;
+        int i;
+
+        /* 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);
+
+        for (i = 0; i < sizeof match_out; i++) {
+            uint8_t in = ((const uint8_t *) match_in.data)[i];
+            uint8_t out = ((const uint8_t *) &match_out)[i];
+
+            if (in != out) {
+                printf("%2d: %02"PRIx8" -> %02"PRIx8"\n", i, in, out);
+            }
+        }
+        putchar('\n');
+
+        ofpbuf_uninit(&match_in);
+    }
+    ds_destroy(&in);
+}
+
 /* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow
  * version. */
 static void
@@ -2021,7 +2116,10 @@ static const struct command all_commands[] = {
     /* 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 },
+    { "parse-nx-match", 0, 0, do_parse_nxm },
+    { "parse-nxm", 0, 0, do_parse_nxm },
+    { "parse-oxm", 0, 0, do_parse_oxm },
+    { "parse-ofp11-match", 0, 0, do_parse_ofp11_match },
     { "print-error", 1, 1, do_print_error },
     { "ofp-print", 1, 2, do_ofp_print },
 
index 9ec8d33..0bc0105 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.
@@ -918,4 +916,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 bd47258..0a7c2c6 100644 (file)
@@ -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);
     }
 }
 
@@ -3936,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);
 
index 645752d..7aa3c29 100644 (file)
@@ -57,14 +57,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
 -----------------
index 13eb80b..5a2b251 100644 (file)
@@ -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"
@@ -245,10 +246,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.)
  *
@@ -687,8 +684,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);
@@ -878,9 +875,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", false)) {
         port_s->enable = false;
         return;
     } else {
@@ -912,8 +907,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;
@@ -942,8 +936,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 +953,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 +974,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 +989,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 +1131,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));
@@ -1420,10 +1394,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 +1408,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 +1421,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 +1442,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 +1567,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,7 +1613,7 @@ 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;
@@ -1663,22 +1626,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;
-
-        shash_to_ovs_idl_map(&sh, &keys, &values, &n);
-        ovsrec_interface_set_status(iface->cfg, keys, values, n);
+    smap_init(&smap);
 
-        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);
+    smap_destroy(&smap);
 
     error = netdev_get_flags(iface->netdev, &flags);
     if (!error) {
@@ -1816,32 +1772,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);
-
-    ovsrec_bridge_set_status(br->cfg, keys, values, ARRAY_SIZE(values));
+    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);
 
-    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 +1801,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 +1811,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 +1822,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,18 +1851,7 @@ port_refresh_stp_status(struct port *port)
 static bool
 enable_system_stats(const struct ovsrec_open_vswitch *cfg)
 {
-    const char *enable;
-
-    /* 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");
-    }
-
-    /* Disable by default. */
-    return false;
+    return smap_get_bool(&cfg->other_config, "enable-statistics", false);
 }
 
 static void
@@ -1974,15 +1905,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);
         }
     }
 
@@ -2228,6 +2167,18 @@ bridge_wait(void)
         }
     }
 }
+
+/* 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
 /* QoS unixctl user interface functions. */
 
@@ -2238,14 +2189,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 +2206,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 +2234,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 +2247,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 +2269,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 +2460,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,11 +2499,12 @@ 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);
-            } else {
+                iface->type = type;
+            } else if (strcmp(type, "null")) {
                 bridge_queue_if_cfg(br, cfg, port);
             }
         }
@@ -2580,7 +2535,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 +2547,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 +2622,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 +2634,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 +2887,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 +2905,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 +2921,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 +2977,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 +2994,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 +3007,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 +3170,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 +3184,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 +3200,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 +3220,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 +3251,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 +3281,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 +3291,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 +3310,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 +3522,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 +3566,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 +3595,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 +3711,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 +3726,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 ecd6ff5..c1b0a2b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011 Nicira, 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.
@@ -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 6c19edb..adabe89 100644 (file)
@@ -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;
index f97df8d..8ef3b10 100644 (file)
 #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"
@@ -93,6 +95,15 @@ main(int argc, char *argv[])
         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();
@@ -100,6 +111,7 @@ main(int argc, char *argv[])
         netdev_run();
 
         signal_wait(sighup);
+        memory_wait();
         bridge_wait();
         unixctl_server_wait(unixctl);
         netdev_wait();
index 94760cc..32d4c59 100644 (file)
         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:00:5e:00:01:<var>x</var><var>x</var></code></dt>
+          <dd> VRRP IPv4 virtual router MAC address. </dd>
+
+          <dt><code>00:00:5e:00:02:<var>x</var><var>x</var></code></dt>
+          <dd> VRRP IPv6 virtual router MAC address. </dd>
+
+          <dt><code>00:00:0c:07:ac:<var>x</var><var>x</var></code></dt>
+          <dd> HSRP Version 1. </dd>
+
+          <dt><code>00:00:0c:9f:f<var>x</var>:<var>x</var><var>x</var></code>
+          </dt>
+          <dd> HSRP Version 2. </dd>
+
+          <dt><code>00:07:b4:<var>x</var><var>x</var>:<var>x</var><var>x</var>:<var>x</var><var>x</var></code></dt>
+          <dd> GLBP. </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>
       </column>
 
       <column name="other_config" key="mac-aging-time"
 
       <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.
         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,
index b2248b7..a199825 100755 (executable)
@@ -76,6 +76,7 @@ start () {
         touch /var/run/openvswitch.booted
         set "$@" --delete-bridges
     fi
+    set "$@" $OVS_CTL_OPTS
     "$@"
 
     start_ovs_xapi_sync
index a677ca6..46b94b9 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, 2010, 2011 Nicira, 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 3945974..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, 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 673cbaf..e051b31 100644 (file)
@@ -232,11 +232,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
index 2262c0b..971f918 100644 (file)
@@ -1,5 +1,5 @@
 # Copyright (c) 2008,2009,2011 Citrix Systems, Inc.
-# Copyright (c) 2009,2010,2011 Nicira, Inc.
+# 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 e176405..f62eaa8 100644 (file)
@@ -1,5 +1,5 @@
 # Copyright (c) 2007-2011 Citrix Systems Inc.
-# Copyright (c) 2009,2010,2011 Nicira, Inc.
+# 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 fc21582..5083bbd 100755 (executable)
@@ -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
@@ -236,7 +237,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 bb85d67..2c08452 100644 (file)
@@ -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=