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
David S. Miller davem@davemloft.net
Devendra Naga devendra.aaru@gmail.com
Dominic Curran dominic.curran@citrix.com
+Ed Maste emaste at freebsd.org
Edward Tomasz Napierała trasz@freebsd.org
Ethan Jackson ethan@nicira.com
Gaetano Catalli gaetano.catalli@gmail.com
+Giuseppe Lettieri g.lettieri@iet.unipi.it
Glen Gibb grg@stanford.edu
Gurucharan Shetty gshetty@nicira.com
Hao Zheng hzheng@nicira.com
Ian Campbell Ian.Campbell@citrix.com
+Isaku Yamahata yamahata@valinux.co.jp
Jean Tourrilhes jt@hpl.hp.com
Jeremy Stribling strib@nicira.com
Jesse Gross jesse@nicira.com
Joe Perches joe@perches.com
+Joe Stringer joe@wand.net.nz
Jun Nakajima jun.nakajima@intel.com
Justin Pettit jpettit@nicira.com
Keith Amidon keith@nicira.com
+Kyle Mestery kmestery@cisco.com
+Leo Alterman lalterman@nicira.com
Luca Giraudo lgiraudo@nicira.com
Martin Casado casado@nicira.com
+Mehak Mahajan mmahajan@nicira.com
Natasha Gude natasha@nicira.com
Neil McKee neil.mckee@inmon.com
Paul Fazzone pfazzone@nicira.com
Shan Wei davidshan@tencent.com
Shih-Hao Li shli@nicira.com
Simon Horman horms@verge.net.au
+Tadaaki Nagao nagao@stratosphere.co.jp
Tetsuo NAKAGAWA nakagawa@mxc.nes.nec.co.jp
Thomas Goirand zigo@debian.org
Thomas Lacroix thomas.lacroix@citrix.com
Alban Browaeys prahal@yahoo.com
Alex Yip alex@nicira.com
Alexey I. Froloff raorn@altlinux.org
+Amey Bhide abhide@nicira.com
+André Ruß andre.russ@hybris.com
Andreas Beckmann debian@abeckmann.de
+Atzm Watanabe atzm@stratosphere.co.jp
+Bastian Blank waldi@debian.org
Ben Basler bbasler@nicira.com
Bob Ball bob.ball@citrix.com
Brad Hall brad@nicira.com
Edwin Chiu echiu@nicira.com
Eivind Bulie Haanaes
Eric Lopez elopez@nicira.com
+Frido Roose fr.roose@gmail.com
Gaetano Catalli gaetano.catalli@gmail.com
George Shuklin amarao@desunote.ru
Ghanem Bahri bahri.ghanem@gmail.com
Giuseppe de Candia giuseppe.decandia@gmail.com
Gordon Good ggood@nicira.com
+Greg Dahlman gdahlman@hotmail.com
Gregor Schaffrath grsch@net.t-labs.tu-berlin.de
Hassan Khan hassan.khan@seecs.edu.pk
Hector Oron hector.oron@gmail.com
Henrik Amren henrik@nicira.com
Hiroshi Tanaka htanaka@nicira.com
+Igor Ganichev iganichev@nicira.com
Jacob Cherkas jcherkas@nicira.com
Jad Naous jnaous@gmail.com
Jamal Hadi Salim hadi@cyberus.ca
Jeongkeun Lee jklee@hp.com
Joan Cirer joan@ev0.net
John Galgay john@galgay.net
+Kevin Mancuso kevin.mancuso@rackspace.com
+Kirill Kabardin
Koichi Yagishita yagishita.koichi@jrc.co.jp
+Konstantin Khorenko khorenko@openvz.org
+Kris zhang zhang.kris@gmail.com
Krishna Miriyala krishna@nicira.com
Luca Falavigna dktrkranz@debian.org
Luiz Henrique Ozaki luiz.ozaki@gmail.com
Paulo Cravero pcravero@as2594.net
Peter Balland peter@nicira.com
Peter Phaal peter.phaal@inmon.com
+Prabina Pattnaik Prabina.Pattnaik@nechclst.in
+Pratap Reddy preddy@nicira.com
+Ralf Heiringhoff ralf@frosty-geek.net
Ram Jothikumar rjothikumar@nicira.com
Ramana Reddy gtvrreddy@gmail.com
+Rob Sherwood rob.sherwood@bigswitch.com
Roger Leigh rleigh@codelibre.net
Rogério Vinhal Nunes
+Scott Hendricks shendricks@nicira.com
Sean Brady sbrady@gtfservices.com
Sebastian Andrzej Siewior sebastian@breakpoint.cc
Sébastien RICCIO sr@swisscenter.com
Teemu Koponen koponen@nicira.com
Timothy Chen tchen@nicira.com
Vishal Swarankar vishal.swarnkar@gmail.com
+Vjekoslav Brajkovic balkan@cs.washington.edu
Voravit T. voravit@kth.se
YAMAMOTO Takashi yamamoto@valinux.co.jp
Yongqiang Liu liuyq7809@gmail.com
section describes how all of these features interact.
First, a service controller never receives any asynchronous messages
-unless it explicitly configures a miss_send_len greater than zero with
-an OFPT_SET_CONFIG message.
+unless it changes its miss_send_len from the service controller
+default of zero in one of the following ways:
+
+ - Sending an OFPT_SET_CONFIG message with nonzero miss_send_len.
+
+ - Sending any NXT_SET_ASYNC_CONFIG message: as a side effect, this
+ message changes the miss_send_len to
+ OFP_DEFAULT_MISS_SEND_LEN (128) for service controllers.
Second, OFPT_FLOW_REMOVED and NXT_FLOW_REMOVED messages are generated
only if the flow that was removed had the OFPFF_SEND_FLOW_REM flag
receive the generated messages.)
+VLAN Matching
+=============
+
+The 802.1Q VLAN header causes more trouble than any other 4 bytes in
+networking. More specifically, three versions of OpenFlow and Open
+vSwitch have among them four different ways to match the contents and
+presence of the VLAN header. The following table describes how each
+version works.
+
+ Match NXM OF1.0 OF1.1 OF1.2
+ ----- --------- ----------- ----------- ------------
+ [1] 0000/0000 ????/1,??/? ????/1,??/? 0000/0000,--
+ [2] 0000/ffff ffff/0,??/? ffff/0,??/? 0000/ffff,--
+ [3] 1xxx/1fff 0xxx/0,??/1 0xxx/0,??/1 1xxx/ffff,--
+ [4] z000/f000 ????/1,0y/0 fffe/0,0y/0 1000/1000,0y
+ [5] zxxx/ffff 0xxx/0,0y/0 0xxx/0,0y/0 1xxx/ffff,0y
+ [6] 0000/0fff <none> <none> <none>
+ [7] 0000/f000 <none> <none> <none>
+ [8] 0000/efff <none> <none> <none>
+ [9] 1001/1001 <none> <none> 1001/1001,--
+ [10] 3000/3000 <none> <none> <none>
+
+Each column is interpreted as follows.
+
+ - Match: See the list below.
+
+ - NXM: xxxx/yyyy means NXM_OF_VLAN_TCI_W with value xxxx and mask
+ yyyy. A mask of 0000 is equivalent to omitting
+ NXM_OF_VLAN_TCI(_W), a mask of ffff is equivalent to
+ NXM_OF_VLAN_TCI.
+
+ - OF1.0 and OF1.1: wwww/x,yy/z means dl_vlan wwww, OFPFW_DL_VLAN
+ x, dl_vlan_pcp yy, and OFPFW_DL_VLAN_PCP z. ? means that the
+ given nibble is ignored (and conventionally 0 for wwww or z,
+ conventionally 1 for x or z). <none> means that the given match
+ is not supported.
+
+ - OF1.2: xxxx/yyyy,zz means OXM_OF_VLAN_VID_W with value xxxx and
+ mask yyyy, and OXM_OF_VLAN_PCP (which is not maskable) with
+ value zz. A mask of 0000 is equivalent to omitting
+ OXM_OF_VLAN_VID(_W), a mask of ffff is equivalent to
+ OXM_OF_VLAN_VID. -- means that OXM_OF_VLAN_PCP is omitted.
+ <none> means that the given match is not supported.
+
+The matches are:
+
+ [1] Matches any packet, that is, one without an 802.1Q header or with
+ an 802.1Q header with any TCI value.
+
+ [2] Matches only packets without an 802.1Q header.
+
+ NXM: Any match with (vlan_tci == 0) and (vlan_tci_mask & 0x1000)
+ != 0 is equivalent to the one listed in the table.
+
+ OF1.0: The spec doesn't define behavior if dl_vlan is set to
+ 0xffff and OFPFW_DL_VLAN_PCP is not set.
+
+ OF1.1: The spec says explicitly to ignore dl_vlan_pcp when
+ dl_vlan is set to 0xffff.
+
+ OF1.2: The spec doesn't say what should happen if (vlan_vid == 0)
+ and (vlan_vid_mask & 0x1000) != 0 but (vlan_vid_mask != 0x1000),
+ but it would be straightforward to also interpret as [2].
+
+ [3] Matches only packets that have an 802.1Q header with VID xxx (and
+ any PCP).
+
+ [4] Matches only packets that have an 802.1Q header with PCP y (and
+ any VID).
+
+ NXM: z is ((y << 1) | 1).
+
+ OF1.0: The spec isn't very clear, but OVS implements it this way.
+
+ OF1.2: Presumably other masks such that (vlan_vid_mask & 0x1fff)
+ == 0x1000 would also work, but the spec doesn't define their
+ behavior.
+
+ [5] Matches only packets that have an 802.1Q header with VID xxx and
+ PCP y.
+
+ NXM: z is ((y << 1) | 1).
+
+ OF1.2: Presumably other masks such that (vlan_vid_mask & 0x1fff)
+ == 0x1fff would also work.
+
+ [6] Matches packets with no 802.1Q header or with an 802.1Q header
+ with a VID of 0. Only possible with NXM.
+
+ [7] Matches packets with no 802.1Q header or with an 802.1Q header
+ with a PCP of 0. Only possible with NXM.
+
+ [8] Matches packets with no 802.1Q header or with an 802.1Q header
+ with both VID and PCP of 0. Only possible with NXM.
+
+ [9] Matches only packets that have an 802.1Q header with an
+ odd-numbered VID (and any PCP). Only possible with NXM and
+ OF1.2. (This is just an example; one can match on any desired
+ VID bit pattern.)
+
+[10] Matches only packets that have an 802.1Q header with an
+ odd-numbered PCP (and any VID). Only possible with NXM. (This
+ is just an example; one can match on any desired VID bit
+ pattern.)
+
+Additional notes:
+
+ - OF1.2: The top three bits of OXM_OF_VLAN_VID are fixed to zero,
+ so bits 13, 14, and 15 in the masks listed in the table may be
+ set to arbitrary values, as long as the corresponding value bits
+ are also zero. The suggested ffff mask for [2], [3], and [5]
+ allows a shorter OXM representation (the mask is omitted) than
+ the minimal 1fff mask.
+
+
+Flow Cookies
+============
+
+OpenFlow 1.0 and later versions have the concept of a "flow cookie",
+which is a 64-bit integer value attached to each flow. The treatment
+of the flow cookie has varied greatly across OpenFlow versions,
+however.
+
+In OpenFlow 1.0:
+
+ - OFPFC_ADD set the cookie in the flow that it added.
+
+ - OFPFC_MODIFY and OFPFC_MODIFY_STRICT updated the cookie for
+ the flow or flows that it modified.
+
+ - OFPST_FLOW messages included the flow cookie.
+
+ - OFPT_FLOW_REMOVED messages reported the cookie of the flow
+ that was removed.
+
+OpenFlow 1.1 made the following changes:
+
+ - Flow mod operations OFPFC_MODIFY, OFPFC_MODIFY_STRICT,
+ OFPFC_DELETE, and OFPFC_DELETE_STRICT, plus flow stats
+ requests and aggregate stats requests, gained the ability to
+ match on flow cookies with an arbitrary mask.
+
+ - OFPFC_MODIFY and OFPFC_MODIFY_STRICT were changed to add a
+ new flow, in the case of no match, only if the flow table
+ modification operation did not match on the cookie field.
+ (In OpenFlow 1.0, modify operations always added a new flow
+ when there was no match.)
+
+ - OFPFC_MODIFY and OFPFC_MODIFY_STRICT no longer updated flow
+ cookies.
+
+OpenFlow 1.2 made the following changes:
+
+ - OFPC_MODIFY and OFPFC_MODIFY_STRICT were changed to never
+ add a new flow, regardless of whether the flow cookie was
+ used for matching.
+
+Open vSwitch support for OpenFlow 1.0 implements the OpenFlow 1.0
+behavior with the following extensions:
+
+ - An NXM extension field NXM_NX_COOKIE(_W) allows the NXM
+ versions of OFPFC_MODIFY, OFPFC_MODIFY_STRICT, OFPFC_DELETE,
+ and OFPFC_DELETE_STRICT flow_mods, plus flow stats requests
+ and aggregate stats requests, to match on flow cookies with
+ arbitrary masks. This is much like the equivalent OpenFlow
+ 1.1 feature.
+
+ - Like OpenFlow 1.1, OFPC_MODIFY and OFPFC_MODIFY_STRICT add a
+ new flow if there is no match and the mask is zero (or not
+ given).
+
+ - The "cookie" field in OFPT_FLOW_MOD and NXT_FLOW_MOD messages
+ is used as the cookie value for OFPFC_ADD commands, as
+ described in OpenFlow 1.0. For OFPFC_MODIFY and
+ OFPFC_MODIFY_STRICT commands, the "cookie" field is used as a
+ new cookie for flows that match unless it is UINT64_MAX, in
+ which case the flow's cookie is not updated.
+
+ - NXT_PACKET_IN (the Nicira extended version of
+ OFPT_PACKET_IN) reports the cookie of the rule that
+ generated the packet, or all-1-bits if no rule generated the
+ packet. (Older versions of OVS used all-0-bits instead of
+ all-1-bits.)
+
+The following table shows the handling of different protocols when
+receiving OFPFC_MODIFY and OFPFC_MODIFY_STRICT messages. A mask of 0
+indicates either an explicit mask of zero or an implicit one by not
+specifying the NXM_NX_COOKIE(_W) field.
+
+ Match Update Add on miss Add on miss
+ cookie cookie mask!=0 mask==0
+ ====== ====== =========== ===========
+OpenFlow 1.0 no yes <always add on miss>
+OpenFlow 1.1 yes no no yes
+OpenFlow 1.2 yes no no no
+NXM yes yes* no yes
+
+* Updates the flow's cookie unless the "cookie" field is UINT64_MAX.
+
+
Multiple Table Support
======================
gateway.
+Action Reproduction
+===================
+
+It seems likely that many controllers, at least at startup, use the
+OpenFlow "flow statistics" request to obtain existing flows, then
+compare the flows' actions against the actions that they expect to
+find. Before version 1.8.0, Open vSwitch always returned exact,
+byte-for-byte copies of the actions that had been added to the flow
+table. The current version of Open vSwitch does not always do this in
+some exceptional cases. This section lists the exceptions that
+controller authors must keep in mind if they compare actual actions
+against desired actions in a bytewise fashion:
+
+ - Open vSwitch zeros padding bytes in action structures,
+ regardless of their values when the flows were added.
+
+ - Open vSwitch "normalizes" the instructions in OpenFlow 1.1
+ (and later) in the following way:
+
+ * OVS sorts the instructions into the following order:
+ Apply-Actions, Clear-Actions, Write-Actions,
+ Write-Metadata, Goto-Table.
+
+ * OVS drops Apply-Actions instructions that have empty
+ action lists.
+
+ * OVS drops Write-Actions instructions that have empty
+ action sets.
+
+Please report other discrepancies, if you notice any, so that we can
+fix or document them.
+
+
Suggestions
===========
--- /dev/null
+ Open vSwitch <http://openvswitch.org>
+
+Frequently Asked Questions
+==========================
+
+General
+-------
+
+Q: What is Open vSwitch?
+
+A: Open vSwitch is a production quality open source software switch
+ designed to be used as a vswitch in virtualized server environments. A
+ vswitch forwards traffic between different VMs on the same physical host
+ and also forwards traffic between VMs and the physical network. Open
+ vSwitch supports standard management interfaces (e.g. sFlow, NetFlow,
+ RSPAN, CLI), and is open to programmatic extension and control using
+ OpenFlow and the OVSDB management protocol.
+
+ Open vSwitch as designed to be compatible with modern switching
+ chipsets. This means that it can be ported to existing high-fanout
+ switches allowing the same flexible control of the physical
+ infrastructure as the virtual infrastructure. It also means that
+ Open vSwitch will be able to take advantage of on-NIC switching
+ chipsets as their functionality matures.
+
+Q: What virtualization platforms can use Open vSwitch?
+
+A: Open vSwitch can currently run on any Linux-based virtualization
+ platform (kernel 2.6.18 and newer), including: KVM, VirtualBox, Xen,
+ Xen Cloud Platform, XenServer. As of Linux 3.3 it is part of the
+ mainline kernel. The bulk of the code is written in platform-
+ independent C and is easily ported to other environments. We welcome
+ inquires about integrating Open vSwitch with other virtualization
+ platforms.
+
+Q: How can I try Open vSwitch?
+
+A: The Open vSwitch source code can be built on a Linux system. You can
+ build and experiment with Open vSwitch on any Linux machine.
+ Packages for various Linux distributions are available on many
+ platforms, including: Debian, Ubuntu, Fedora.
+
+ You may also download and run a virtualization platform that already
+ has Open vSwitch integrated. For example, download a recent ISO for
+ XenServer or Xen Cloud Platform. Be aware that the version
+ integrated with a particular platform may not be the most recent Open
+ vSwitch release.
+
+Q: Does Open vSwitch only work on Linux?
+
+A: No, Open vSwitch has been ported to a number of different operating
+ systems and hardware platforms. Most of the development work occurs
+ on Linux, but the code should be portable to any POSIX system. We've
+ seen Open vSwitch ported to a number of different platforms,
+ including FreeBSD, Windows, and even non-POSIX embedded systems.
+
+ By definition, the Open vSwitch Linux kernel module only works on
+ Linux and will provide the highest performance. However, a userspace
+ datapath is available that should be very portable.
+
+Q: What's involved with porting Open vSwitch to a new platform or
+ switching ASIC?
+
+A: The PORTING document describes how one would go about porting Open
+ vSwitch to a new operating system or hardware platform.
+
+Q: Why would I use Open vSwitch instead of the Linux bridge?
+
+A: Open vSwitch is specially designed to make it easier to manage VM
+ network configuration and monitor state spread across many physical
+ hosts in dynamic virtualized environments. Please see WHY-OVS for a
+ more detailed description of how Open vSwitch relates to the Linux
+ Bridge.
+
+Q: How is Open vSwitch related to distributed virtual switches like the
+ VMware vNetwork distributed switch or the Cisco Nexus 1000V?
+
+A: Distributed vswitch applications (e.g., VMware vNetwork distributed
+ switch, Cisco Nexus 1000V) provide a centralized way to configure and
+ monitor the network state of VMs that are spread across many physical
+ hosts. Open vSwitch is not a distributed vswitch itself, rather it
+ runs on each physical host and supports remote management in a way
+ that makes it easier for developers of virtualization/cloud
+ management platforms to offer distributed vswitch capabilities.
+
+ To aid in distribution, Open vSwitch provides two open protocols that
+ are specially designed for remote management in virtualized network
+ environments: OpenFlow, which exposes flow-based forwarding state,
+ and the OVSDB management protocol, which exposes switch port state.
+ In addition to the switch implementation itself, Open vSwitch
+ includes tools (ovs-controller, ovs-ofctl, ovs-vsctl) that developers
+ can script and extend to provide distributed vswitch capabilities
+ that are closely integrated with their virtualization management
+ platform.
+
+Q: Why doesn't Open vSwitch support distribution?
+
+A: Open vSwitch is intended to be a useful component for building
+ flexible network infrastructure. There are many different approaches
+ to distribution which balance trade-offs between simplicity,
+ scalability, hardware compatibility, convergence times, logical
+ forwarding model, etc. The goal of Open vSwitch is to be able to
+ support all as a primitive building block rather than choose a
+ particular point in the distributed design space.
+
+Q: How can I contribute to the Open vSwitch Community?
+
+A: You can start by joining the mailing lists and helping to answer
+ questions. You can also suggest improvements to documentation. If
+ you have a feature or bug you would like to work on, send a mail to
+ one of the mailing lists:
+
+ http://openvswitch.org/mlists/
+
+
+
+Releases
+--------
+
+Q: What does it mean for an Open vSwitch release to be LTS (long-term
+ support)?
+
+A: All official releases have been through a comprehensive testing
+ process and are suitable for production use. Planned releases will
+ occur several times a year. If a significant bug is identified in an
+ LTS release, we will provide an updated release that includes the
+ fix. Releases that are not LTS may not be fixed and may just be
+ supplanted by the next major release. The current LTS release is
+ 1.4.x.
+
+Q: What features are not available in the Open vSwitch kernel datapath
+ that ships as part of the upstream Linux kernel?
+
+A: The kernel module in upstream Linux 3.3 and later does not include
+ the following features:
+
+ - Bridge compatibility, that is, support for the ovs-brcompatd
+ daemon that (if you enable it) lets "brctl" and other Linux
+ bridge tools transparently work with Open vSwitch instead.
+
+ We do not expect bridge compatibility to ever be available in
+ upstream Linux. If you need bridge compatibility, use the
+ kernel module from the Open vSwitch distribution instead of the
+ upstream Linux kernel module.
+
+ - Tunnel virtual ports, that is, interfaces with type "gre",
+ "ipsec_gre", "capwap". It is possible to create tunnels in
+ Linux and attach them to Open vSwitch as system devices.
+ However, they cannot be dynamically created through the OVSDB
+ protocol or set the tunnel ids as a flow action.
+
+ Work is in progress in adding these features to the upstream
+ Linux version of the Open vSwitch kernel module. For now, if
+ you need these features, use the kernel module from the Open
+ vSwitch distribution instead of the upstream Linux kernel
+ module.
+
+ - Patch virtual ports, that is, interfaces with type "patch".
+ You can use Linux "veth" devices as a substitute.
+
+ We don't have any plans to add patch ports upstream.
+
+Q: What features are not available when using the userspace datapath?
+
+A: Tunnel and patch virtual ports are not supported, as described in the
+ previous answer. It is also not possible to use queue-related
+ actions. On Linux kernels before 2.6.39, maximum-sized VLAN packets
+ may not be transmitted.
+
+
+Basic Configuration
+-------------------
+
+Q: How do I configure a port as an access port?
+
+A: Add "tag=VLAN" to your "ovs-vsctl add-port" command. For example,
+ the following commands configure br0 with eth0 as a trunk port (the
+ default) and tap0 as an access port for VLAN 9:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 eth0
+ ovs-vsctl add-port br0 tap0 tag=9
+
+ If you want to configure an already added port as an access port,
+ use "ovs-vsctl set", e.g.:
+
+ ovs-vsctl set port tap0 tag=9
+
+Q: How do I configure a port as a SPAN port, that is, enable mirroring
+ of all traffic to that port?
+
+A: The following commands configure br0 with eth0 and tap0 as trunk
+ ports. All traffic coming in or going out on eth0 or tap0 is also
+ mirrored to tap1; any traffic arriving on tap1 is dropped:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 eth0
+ ovs-vsctl add-port br0 tap0
+ ovs-vsctl add-port br0 tap1 \
+ -- --id=@p get port tap1 \
+ -- --id=@m create mirror name=m0 select-all=true output-port=@p \
+ -- set bridge br0 mirrors=@m
+
+ To later disable mirroring, run:
+
+ ovs-vsctl clear bridge br0 mirrors
+
+Q: How do I configure a VLAN as an RSPAN VLAN, that is, enable
+ mirroring of all traffic to that VLAN?
+
+A: The following commands configure br0 with eth0 as a trunk port and
+ tap0 as an access port for VLAN 10. All traffic coming in or going
+ out on tap0, as well as traffic coming in or going out on eth0 in
+ VLAN 10, is also mirrored to VLAN 15 on eth0. The original tag for
+ VLAN 10, in cases where one is present, is dropped as part of
+ mirroring:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 eth0
+ ovs-vsctl add-port br0 tap0 tag=10
+ ovs-vsctl \
+ -- --id=@m create mirror name=m0 select-all=true select-vlan=10 \
+ output-vlan=15 \
+ -- set bridge br0 mirrors=@m
+
+ To later disable mirroring, run:
+
+ ovs-vsctl clear bridge br0 mirrors
+
+ Mirroring to a VLAN can disrupt a network that contains unmanaged
+ switches. See ovs-vswitchd.conf.db(5) for details. Mirroring to a
+ GRE tunnel has fewer caveats than mirroring to a VLAN and should
+ generally be preferred.
+
+Q: Can I mirror more than one input VLAN to an RSPAN VLAN?
+
+A: Yes, but mirroring to a VLAN strips the original VLAN tag in favor
+ of the specified output-vlan. This loss of information may make
+ the mirrored traffic too hard to interpret.
+
+ To mirror multiple VLANs, use the commands above, but specify a
+ comma-separated list of VLANs as the value for select-vlan. To
+ mirror every VLAN, use the commands above, but omit select-vlan and
+ its value entirely.
+
+ When a packet arrives on a VLAN that is used as a mirror output
+ VLAN, the mirror is disregarded. Instead, in standalone mode, OVS
+ floods the packet across all the ports for which the mirror output
+ VLAN is configured. (If an OpenFlow controller is in use, then it
+ can override this behavior through the flow table.) If OVS is used
+ as an intermediate switch, rather than an edge switch, this ensures
+ that the RSPAN traffic is distributed through the network.
+
+ Mirroring to a VLAN can disrupt a network that contains unmanaged
+ switches. See ovs-vswitchd.conf.db(5) for details. Mirroring to a
+ GRE tunnel has fewer caveats than mirroring to a VLAN and should
+ generally be preferred.
+
+Q: How do I configure mirroring of all traffic to a GRE tunnel?
+
+A: The following commands configure br0 with eth0 and tap0 as trunk
+ ports. All traffic coming in or going out on eth0 or tap0 is also
+ mirrored to gre0, a GRE tunnel to the remote host 192.168.1.10; any
+ traffic arriving on gre0 is dropped:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 eth0
+ ovs-vsctl add-port br0 tap0
+ ovs-vsctl add-port br0 gre0 \
+ -- set interface gre0 type=gre options:remote_ip=192.168.1.10 \
+ -- --id=@p get port gre0 \
+ -- --id=@m create mirror name=m0 select-all=true output-port=@p \
+ -- set bridge br0 mirrors=@m
+
+ To later disable mirroring and destroy the GRE tunnel:
+
+ ovs-vsctl clear bridge br0 mirrors
+ ovs-vcstl del-port br0 gre0
+
+Q: Does Open vSwitch support ERSPAN?
+
+A: No. ERSPAN is an undocumented proprietary protocol. As an
+ alternative, Open vSwitch supports mirroring to a GRE tunnel (see
+ above).
+
+
+Configuration Problems
+----------------------
+
+Q: I created a bridge and added my Ethernet port to it, using commands
+ like these:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 eth0
+
+ and as soon as I ran the "add-port" command I lost all connectivity
+ through eth0. Help!
+
+A: A physical Ethernet device that is part of an Open vSwitch bridge
+ should not have an IP address. If one does, then that IP address
+ will not be fully functional.
+
+ You can restore functionality by moving the IP address to an Open
+ vSwitch "internal" device, such as the network device named after
+ the bridge itself. For example, assuming that eth0's IP address is
+ 192.168.128.5, you could run the commands below to fix up the
+ situation:
+
+ ifconfig eth0 0.0.0.0
+ ifconfig br0 192.168.128.5
+
+ (If your only connection to the machine running OVS is through the
+ IP address in question, then you would want to run all of these
+ commands on a single command line, or put them into a script.) If
+ there were any additional routes assigned to eth0, then you would
+ also want to use commands to adjust these routes to go through br0.
+
+ If you use DHCP to obtain an IP address, then you should kill the
+ DHCP client that was listening on the physical Ethernet interface
+ (e.g. eth0) and start one listening on the internal interface
+ (e.g. br0). You might still need to manually clear the IP address
+ from the physical interface (e.g. with "ifconfig eth0 0.0.0.0").
+
+ There is no compelling reason why Open vSwitch must work this way.
+ However, this is the way that the Linux kernel bridge module has
+ always worked, so it's a model that those accustomed to Linux
+ bridging are already used to. Also, the model that most people
+ expect is not implementable without kernel changes on all the
+ versions of Linux that Open vSwitch supports.
+
+ By the way, this issue is not specific to physical Ethernet
+ devices. It applies to all network devices except Open vswitch
+ "internal" devices.
+
+Q: I created a bridge and added a couple of Ethernet ports to it,
+ using commands like these:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 eth0
+ ovs-vsctl add-port br0 eth1
+
+ and now my network seems to have melted: connectivity is unreliable
+ (even connectivity that doesn't go through Open vSwitch), all the
+ LEDs on my physical switches are blinking, wireshark shows
+ duplicated packets, and CPU usage is very high.
+
+A: More than likely, you've looped your network. Probably, eth0 and
+ eth1 are connected to the same physical Ethernet switch. This
+ yields a scenario where OVS receives a broadcast packet on eth0 and
+ sends it out on eth1, then the physical switch connected to eth1
+ sends the packet back on eth0, and so on forever. More complicated
+ scenarios, involving a loop through multiple switches, are possible
+ too.
+
+ The solution depends on what you are trying to do:
+
+ - If you added eth0 and eth1 to get higher bandwidth or higher
+ reliability between OVS and your physical Ethernet switch,
+ use a bond. The following commands create br0 and then add
+ eth0 and eth1 as a bond:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-bond br0 bond0 eth0 eth1
+
+ Bonds have tons of configuration options. Please read the
+ documentation on the Port table in ovs-vswitchd.conf.db(5)
+ for all the details.
+
+ - Perhaps you don't actually need eth0 and eth1 to be on the
+ same bridge. For example, if you simply want to be able to
+ connect each of them to virtual machines, then you can put
+ each of them on a bridge of its own:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 eth0
+
+ ovs-vsctl add-br br1
+ ovs-vsctl add-port br1 eth1
+
+ and then connect VMs to br0 and br1. (A potential
+ disadvantage is that traffic cannot directly pass between br0
+ and br1. Instead, it will go out eth0 and come back in eth1,
+ or vice versa.)
+
+ - If you have a redundant or complex network topology and you
+ want to prevent loops, turn on spanning tree protocol (STP).
+ The following commands create br0, enable STP, and add eth0
+ and eth1 to the bridge. The order is important because you
+ don't want have to have a loop in your network even
+ transiently:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl set bridge br0 stp_enable=true
+ ovs-vsctl add-port br0 eth0
+ ovs-vsctl add-port br0 eth1
+
+ The Open vSwitch implementation of STP is not well tested.
+ Please report any bugs you observe, but if you'd rather avoid
+ acting as a beta tester then another option might be your
+ best shot.
+
+Q: I can't seem to use Open vSwitch in a wireless network.
+
+A: Wireless base stations generally only allow packets with the source
+ MAC address of NIC that completed the initial handshake.
+ Therefore, without MAC rewriting, only a single device can
+ communicate over a single wireless link.
+
+ This isn't specific to Open vSwitch, it's enforced by the access
+ point, so the same problems will show up with the Linux bridge or
+ any other way to do bridging.
+
+Q: Is there any documentation on the database tables and fields?
+
+A: Yes. ovs-vswitchd.conf.db(5) is a comprehensive reference.
+
+
+VLANs
+-----
+
+Q: What's a VLAN?
+
+A: At the simplest level, a VLAN (short for "virtual LAN") is a way to
+ partition a single switch into multiple switches. Suppose, for
+ example, that you have two groups of machines, group A and group B.
+ You want the machines in group A to be able to talk to each other,
+ and you want the machine in group B to be able to talk to each
+ other, but you don't want the machines in group A to be able to
+ talk to the machines in group B. You can do this with two
+ switches, by plugging the machines in group A into one switch and
+ the machines in group B into the other switch.
+
+ If you only have one switch, then you can use VLANs to do the same
+ thing, by configuring the ports for machines in group A as VLAN
+ "access ports" for one VLAN and the ports for group B as "access
+ ports" for a different VLAN. The switch will only forward packets
+ between ports that are assigned to the same VLAN, so this
+ effectively subdivides your single switch into two independent
+ switches, one for each group of machines.
+
+ So far we haven't said anything about VLAN headers. With access
+ ports, like we've described so far, no VLAN header is present in
+ the Ethernet frame. This means that the machines (or switches)
+ connected to access ports need not be aware that VLANs are
+ involved, just like in the case where we use two different physical
+ switches.
+
+ Now suppose that you have a whole bunch of switches in your
+ network, instead of just one, and that some machines in group A are
+ connected directly to both switches 1 and 2. To allow these
+ machines to talk to each other, you could add an access port for
+ group A's VLAN to switch 1 and another to switch 2, and then
+ connect an Ethernet cable between those ports. That works fine,
+ but it doesn't scale well as the number of switches and the number
+ of VLANs increases, because you use up a lot of valuable switch
+ ports just connecting together your VLANs.
+
+ This is where VLAN headers come in. Instead of using one cable and
+ two ports per VLAN to connect a pair of switches, we configure a
+ port on each switch as a VLAN "trunk port". Packets sent and
+ received on a trunk port carry a VLAN header that says what VLAN
+ the packet belongs to, so that only two ports total are required to
+ connect the switches, regardless of the number of VLANs in use.
+ Normally, only switches (either physical or virtual) are connected
+ to a trunk port, not individual hosts, because individual hosts
+ don't expect to see a VLAN header in the traffic that they receive.
+
+ None of the above discussion says anything about particular VLAN
+ numbers. This is because VLAN numbers are completely arbitrary.
+ One must only ensure that a given VLAN is numbered consistently
+ throughout a network and that different VLANs are given different
+ numbers. (That said, VLAN 0 is usually synonymous with a packet
+ that has no VLAN header, and VLAN 4095 is reserved.)
+
+Q: VLANs don't work.
+
+A: Many drivers in Linux kernels before version 3.3 had VLAN-related
+ bugs. If you are having problems with VLANs that you suspect to be
+ driver related, then you have several options:
+
+ - Upgrade to Linux 3.3 or later.
+
+ - Build and install a fixed version of the particular driver
+ that is causing trouble, if one is available.
+
+ - Use a NIC whose driver does not have VLAN problems.
+
+ - Use "VLAN splinters", a feature in Open vSwitch 1.4 and later
+ that works around bugs in kernel drivers. To enable VLAN
+ splinters on interface eth0, use the command:
+
+ ovs-vsctl set interface eth0 other-config:enable-vlan-splinters=true
+
+ For VLAN splinters to be effective, Open vSwitch must know
+ which VLANs are in use. See the "VLAN splinters" section in
+ the Interface table in ovs-vswitchd.conf.db(5) for details on
+ how Open vSwitch infers in-use VLANs.
+
+ VLAN splinters increase memory use and reduce performance, so
+ use them only if needed.
+
+ - Apply the "vlan workaround" patch from the XenServer kernel
+ patch queue, build Open vSwitch against this patched kernel,
+ and then use ovs-vlan-bug-workaround(8) to enable the VLAN
+ workaround for each interface whose driver is buggy.
+
+ (This is a nontrivial exercise, so this option is included
+ only for completeness.)
+
+ It is not always easy to tell whether a Linux kernel driver has
+ buggy VLAN support. The ovs-vlan-test(8) and ovs-test(8) utilities
+ can help you test. See their manpages for details. Of the two
+ utilities, ovs-test(8) is newer and more thorough, but
+ ovs-vlan-test(8) may be easier to use.
+
+Q: VLANs still don't work. I've tested the driver so I know that it's OK.
+
+A: Do you have VLANs enabled on the physical switch that OVS is
+ attached to? Make sure that the port is configured to trunk the
+ VLAN or VLANs that you are using with OVS.
+
+Q: Outgoing VLAN-tagged traffic goes through OVS to my physical switch
+ and to its destination host, but OVS seems to drop incoming return
+ traffic.
+
+A: It's possible that you have the VLAN configured on your physical
+ switch as the "native" VLAN. In this mode, the switch treats
+ incoming packets either tagged with the native VLAN or untagged as
+ part of the native VLAN. It may also send outgoing packets in the
+ native VLAN without a VLAN tag.
+
+ If this is the case, you have two choices:
+
+ - Change the physical switch port configuration to tag packets
+ it forwards to OVS with the native VLAN instead of forwarding
+ them untagged.
+
+ - Change the OVS configuration for the physical port to a
+ native VLAN mode. For example, the following sets up a
+ bridge with port eth0 in "native-tagged" mode in VLAN 9:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 eth0 tag=9 vlan_mode=native-tagged
+
+ In this situation, "native-untagged" mode will probably work
+ equally well. Refer to the documentation for the Port table
+ in ovs-vswitchd.conf.db(5) for more information.
+
+Q: Can I configure an IP address on a VLAN?
+
+A: Yes. Use an "internal port" configured as an access port. For
+ example, the following configures IP address 192.168.0.7 on VLAN 9.
+ That is, OVS will forward packets from eth0 to 192.168.0.7 only if
+ they have an 802.1Q header with VLAN 9. Conversely, traffic
+ forwarded from 192.168.0.7 to eth0 will be tagged with an 802.1Q
+ header with VLAN 9:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 eth0
+ ovs-vsctl add-port br0 vlan9 tag=9 -- set interface vlan9 type=internal
+ ifconfig vlan9 192.168.0.7
+
+Q: My OpenFlow controller doesn't see the VLANs that I expect.
+
+A: The configuration for VLANs in the Open vSwitch database (e.g. via
+ ovs-vsctl) only affects traffic that goes through Open vSwitch's
+ implementation of the OpenFlow "normal switching" action. By
+ default, when Open vSwitch isn't connected to a controller and
+ nothing has been manually configured in the flow table, all traffic
+ goes through the "normal switching" action. But, if you set up
+ OpenFlow flows on your own, through a controller or using ovs-ofctl
+ or through other means, then you have to implement VLAN handling
+ yourself.
+
+ You can use "normal switching" as a component of your OpenFlow
+ actions, e.g. by putting "normal" into the lists of actions on
+ ovs-ofctl or by outputting to OFPP_NORMAL from an OpenFlow
+ controller. This will only be suitable for some situations,
+ though.
+
+Q: I configured ports on a bridge as access ports with different VLAN
+ tags, like this:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl set-controller br0 tcp:192.168.0.10:6633
+ ovs-vsctl add-port br0 eth0
+ ovs-vsctl add-port br0 tap0 tag=9
+ ovs-vsctl add-port br0 tap1 tag=10
+
+ but the VMs running behind tap0 and tap1 can still communicate,
+ that is, they are not isolated from each other even though they are
+ on different VLANs.
+
+A: Do you have a controller configured on br0 (as the commands above
+ do)? If so, then this is a variant on the previous question, "My
+ OpenFlow controller doesn't see the VLANs that I expect," and you
+ can refer to the answer there for more information.
+
+
+Controllers
+-----------
+
+Q: What versions of OpenFlow does Open vSwitch support?
+
+A: Open vSwitch supports OpenFlow 1.0. It also includes a number of
+ extensions that bring many of the features from later versions of
+ OpenFlow. Work is underway to provide support for later versions and
+ can be tracked here:
+
+ http://openvswitch.org/development/openflow-1-x-plan/
+
+Q: I'm getting "error type 45250 code 0". What's that?
+
+A: This is a Open vSwitch extension to OpenFlow error codes. Open
+ vSwitch uses this extension when it must report an error to an
+ OpenFlow controller but no standard OpenFlow error code is
+ suitable.
+
+ Open vSwitch logs the errors that it sends to controllers, so the
+ easiest thing to do is probably to look at the ovs-vswitchd log to
+ find out what the error was.
+
+ If you want to dissect the extended error message yourself, the
+ format is documented in include/openflow/nicira-ext.h in the Open
+ vSwitch source distribution. The extended error codes are
+ documented in lib/ofp-errors.h.
+
+Q1: Some of the traffic that I'd expect my OpenFlow controller to see
+ doesn't actually appear through the OpenFlow connection, even
+ though I know that it's going through.
+Q2: Some of the OpenFlow flows that my controller sets up don't seem
+ to apply to certain traffic, especially traffic between OVS and
+ the controller itself.
+
+A: By default, Open vSwitch assumes that OpenFlow controllers are
+ connected "in-band", that is, that the controllers are actually
+ part of the network that is being controlled. In in-band mode,
+ Open vSwitch sets up special "hidden" flows to make sure that
+ traffic can make it back and forth between OVS and the controllers.
+ These hidden flows are higher priority than any flows that can be
+ set up through OpenFlow, and they are not visible through normal
+ OpenFlow flow table dumps.
+
+ Usually, the hidden flows are desirable and helpful, but
+ occasionally they can cause unexpected behavior. You can view the
+ full OpenFlow flow table, including hidden flows, on bridge br0
+ with the command:
+
+ ovs-appctl bridge/dump-flows br0
+
+ to help you debug. The hidden flows are those with priorities
+ greater than 65535 (the maximum priority that can be set with
+ OpenFlow).
+
+ The DESIGN file at the top level of the Open vSwitch source
+ distribution describes the in-band model in detail.
+
+ If your controllers are not actually in-band (e.g. they are on
+ localhost via 127.0.0.1, or on a separate network), then you should
+ configure your controllers in "out-of-band" mode. If you have one
+ controller on bridge br0, then you can configure out-of-band mode
+ on it with:
+
+ ovs-vsctl set controller br0 connection-mode=out-of-band
+
+Q: I configured all my controllers for out-of-band control mode but
+ "ovs-appctl bridge/dump-flows" still shows some hidden flows.
+
+A: You probably have a remote manager configured (e.g. with "ovs-vsctl
+ set-manager"). By default, Open vSwitch assumes that managers need
+ in-band rules set up on every bridge. You can disable these rules
+ on bridge br0 with:
+
+ ovs-vsctl set bridge br0 other-config:disable-in-band=true
+
+ This actually disables in-band control entirely for the bridge, as
+ if all the bridge's controllers were configured for out-of-band
+ control.
+
+Q: My OpenFlow controller doesn't see the VLANs that I expect.
+
+A: See answer under "VLANs", above.
+
+Q: I ran "ovs-ofctl add-flow br0 nw_dst=192.168.0.1,actions=drop"
+ but I got a funny message like this:
+
+ ofp_util|INFO|normalization changed ofp_match, details:
+ ofp_util|INFO| pre: nw_dst=192.168.0.1
+ ofp_util|INFO|post:
+
+ and when I ran "ovs-ofctl dump-flows br0" I saw that my nw_dst
+ match had disappeared, so that the flow ends up matching every
+ packet.
+
+A: The term "normalization" in the log message means that a flow
+ cannot match on an L3 field without saying what L3 protocol is in
+ use. The "ovs-ofctl" command above didn't specify an L3 protocol,
+ so the L3 field match was dropped.
+
+ In this case, the L3 protocol could be IP or ARP. A correct
+ command for each possibility is, respectively:
+
+ ovs-ofctl add-flow br0 ip,nw_dst=192.168.0.1,actions=drop
+
+ and
+
+ ovs-ofctl add-flow br0 arp,nw_dst=192.168.0.1,actions=drop
+
+ Similarly, a flow cannot match on an L4 field without saying what
+ L4 protocol is in use. For example, the flow match "tp_src=1234"
+ is, by itself, meaningless and will be ignored. Instead, to match
+ TCP source port 1234, write "tcp,tp_src=1234", or to match UDP
+ source port 1234, write "udp,tp_src=1234".
+
+Q: How can I figure out the OpenFlow port number for a given port?
+
+A: The OFPT_FEATURES_REQUEST message requests an OpenFlow switch to
+ respond with an OFPT_FEATURES_REPLY that, among other information,
+ includes a mapping between OpenFlow port names and numbers. From a
+ command prompt, "ovs-ofctl show br0" makes such a request and
+ prints the response for switch br0.
+
+ The Interface table in the Open vSwitch database also maps OpenFlow
+ port names to numbers. To print the OpenFlow port number
+ associated with interface eth0, run:
+
+ ovs-vsctl get Interface eth0 ofport
+
+ You can print the entire mapping with:
+
+ ovs-vsctl -- --columns=name,ofport list Interface
+
+ but the output mixes together interfaces from all bridges in the
+ database, so it may be confusing if more than one bridge exists.
+
+ In the Open vSwitch database, ofport value -1 means that the
+ interface could not be created due to an error. (The Open vSwitch
+ log should indicate the reason.) ofport value [] (the empty set)
+ means that the interface hasn't been created yet. The latter is
+ normally an intermittent condition (unless ovs-vswitchd is not
+ running).
+
+Contact
+-------
+
+bugs@openvswitch.org
+http://openvswitch.org/
- How to Install Open vSwitch on Linux
- ====================================
+ How to Install Open vSwitch on Linux and FreeBSD
+ ================================================
This document describes how to build and install Open vSwitch on a
-generic Linux host. If you want to install Open vSwitch on a Citrix
-XenServer, see INSTALL.XenServer instead.
+generic Linux or FreeBSD host. If you want to install Open vSwitch on
+a Citrix XenServer, see INSTALL.XenServer instead.
This version of Open vSwitch may be built manually with "configure"
and "make", as described below. You may also build Debian packages by
libssl is installed, then Open vSwitch will automatically build
with support for it.
-To compile the kernel module, you must also install the following. If
-you cannot build or install the kernel module, you may use the
-userspace-only implementation, at a cost in performance. The
+To compile the kernel module on Linux, you must also install the
+following. If you cannot build or install the kernel module, you may
+use the userspace-only implementation, at a cost in performance. The
userspace implementation may also lack some features. Refer to
INSTALL.userspace for more information.
- Python 2.x, for x >= 4.
+If you are going to extensively modify Open vSwitch, please consider
+installing the following to obtain better warnings:
+
+ - "sparse" version 0.4.4 or later
+ (http://www.kernel.org/pub/software/devel/sparse/dist/).
+
+ - GNU make.
+
Installation Requirements
-------------------------
- libssl compatible with the libssl used for build, if OpenSSL was
used for the build.
- - The Linux kernel version configured as part of the build.
+ - On Linux, the same kernel version configured as part of the build.
- - For optional support of ingress policing, the "tc" program from
- iproute2 (part of all major distributions and available at
+ - For optional support of ingress policing on Linux, the "tc" program
+ from iproute2 (part of all major distributions and available at
http://www.linux-foundation.org/en/Net:Iproute2).
-You should ensure that /dev/urandom exists. To support TAP devices,
-you must also ensure that /dev/net/tun exists.
+On Linux you should ensure that /dev/urandom exists. To support TAP
+devices, you must also ensure that /dev/net/tun exists.
To run the ovsdbmonitor tool, the machine must also have the following
software:
(On Debian "lenny" the above can be installed with "apt-get install
python-json python-qt4 python-zopeinterface python-twisted-conch".)
-Building and Installing Open vSwitch for Linux
-==============================================
+Building and Installing Open vSwitch for Linux or FreeBSD
+=========================================================
Once you have installed all the prerequisites listed above in the Base
Prerequisites section, follow the procedure below to build.
-1. If you pulled the sources directly from an Open vSwitch Git tree,
+1. If you pulled the sources directly from an Open vSwitch Git tree,
run boot.sh in the top source directory:
% ./boot.sh
additional environment variables. For a full list, invoke
configure with the --help option.
-3. Run make in the top source directory:
+3. Run make in the top source directory:
% make
+ On FreeBSD you may need to use GNU make (gmake) or NetBSD make
+ (bmake) instead of the native make.
+
+ For improved warnings if you installed "sparse" (see
+ "Prerequisites"), add C=1 to the "make" command line.
+
4. Become root by running "su" or another program.
5. Run "make install" to install the executables and manpages into the
- If there is no important data in your database, then you may
delete the database file and recreate it with ovsdb-tool,
following the instructions under "Building and Installing Open
- vSwitch for Linux".
+ vSwitch for Linux or FreeBSD".
- If you want to preserve the contents of your database, back it
up first, then use "ovsdb-tool convert" to upgrade it, e.g.:
% ovsdb-tool convert /usr/local/etc/openvswitch/conf.db vswitchd/vswitch.ovsschema
4. Start the Open vSwitch daemons as described under "Building and
- Installing Open vSwitch for Linux" above.
+ Installing Open vSwitch for Linux or FreeBSD" above.
Bug Reporting
-------------
This document describes how to use Open vSwitch with the Kernel-based
Virtual Machine (KVM). This document assumes that you have read and
-followed INSTALL.Linux to get Open vSwitch setup on your Linux system.
+followed INSTALL to get Open vSwitch setup on your Linux system.
Setup
-----
-First, follow the setup instructions in INSTALL.Linux to get a working
+First, follow the setup instructions in INSTALL to get a working
Open vSwitch installation.
KVM uses tunctl to handle various bridging modes, which you can
ovs-vsctl del-port ${switch} $1
--------------------------------------------------------------------
-At the end of INSTALL.Linux, it describes basic usage of creating
+At the end of INSTALL, it describes basic usage of creating
bridges and ports. If you haven't already, create a bridge named
br0 with the following command:
This document describes how to use Open vSwitch with Libvirt 0.9.11 or
later. The Open vSwitch support in Libvirt 0.9.11 eliminates the need to
use OVS Linux Bridge compatibility layer (brcompatd) and interface up/down
-scripts. This document assumes that you followed INSTALL.Linux or installed
+scripts. This document assumes that you followed INSTALL or installed
Open vSwitch from distribution packaging such as a .deb or .rpm. The Open
vSwitch support is included by default in Libvirt 0.9.11. Consult
www.libvirt.org for instructions on how to build the latest Libvirt, if your
This document describes how to build and install Open vSwitch on a Red
Hat Enterprise Linux (RHEL) host. If you want to install Open vSwitch
-on a generic Linux host, see INSTALL.Linux instead.
+on a generic Linux host, see INSTALL instead.
We have tested these instructions with RHEL 5.6 and RHEL 6.0.
the OpenFlow connections, increasing network security.
This file explains how to configure an Open vSwitch to connect to an
-OpenFlow controller over SSL. Refer to INSTALL.Linux for instructions
-on building Open vSwitch with SSL support.
+OpenFlow controller over SSL. Refer to INSTALL for instructions on
+building Open vSwitch with SSL support.
Open vSwitch uses TLS version 1.0 or later (TLSv1), as specified by
RFC 2246, which is very similar to SSL version 3.0. TLSv1 was
"tcp:" connection methods are still allowed even after SSL has been
configured, so for security you should use only "ssl:" connections.
-Unlike most Open vSwitch settings, the SSL settings are read only
-once, at ovs-vswitchd startup time. For changes to take effect,
-ovs-vswitchd must be killed and restarted.
-
Reporting Bugs
--------------
This document describes how to build and install Open vSwitch on a
Citrix XenServer host. If you want to install Open vSwitch on a
-generic Linux host, see INSTALL.Linux instead.
+generic Linux or BSD host, see INSTALL instead.
These instructions have been tested with XenServer 5.6 FP1.
./configure; make dist" in the Git tree. You cannot run this in
the DDK VM, because it lacks tools that are necessary to bootstrap
the Open vSwitch distribution. Instead, you must run this on a
- machine that has the tools listed in INSTALL.Linux as prerequisites
- for building from a Git tree.
+ machine that has the tools listed in INSTALL as prerequisites for
+ building from a Git tree.
2. Copy the distribution tarball into /usr/src/redhat/SOURCES inside
the DDK VM.
kernel bridge module.
1. Build, install, and start up the Open vSwitch kernel modules and
- userspace programs as described in INSTALL.Linux.
+ userspace programs as described in INSTALL.
It is important to run "make install", because some Open vSwitch
programs expect to find files in locations selected at installation
-----------------------
The requirements and procedure for building, installing, and
-configuring Open vSwitch are the same as those given in INSTALL.Linux.
+configuring Open vSwitch are the same as those given in INSTALL.
You may omit configuring, building, and installing the kernel module,
and the related requirements.
then create /dev/net (if necessary) with "mkdir /dev/net", then create
/dev/net/tun with "mknod /dev/net/tun c 10 200".
+On FreeBSD, the userspace switch additionally requires the kernel
+tap(4) driver to be available, either built into the kernel or loaded
+as a module.
+
Using the Userspace Datapath with ovs-vswitchd
----------------------------------------------
named the same as the bridge, as well as for each configured internal
interface.
+Firewall Rules
+--------------
+
+On Linux, when a physical interface is in use by the userspace
+datapath, packets received on the interface still also pass into the
+kernel TCP/IP stack. This can cause surprising and incorrect
+behavior. You can use "iptables" to avoid this behavior, by using it
+to drop received packets. For example, to drop packets received on
+eth0:
+
+ iptables -A INPUT -i eth0 -j DROP
+ iptables -A FORWARD -i eth0 -j DROP
+
Bug Reporting
-------------
--- /dev/null
+ 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}"'
+
-# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
if NDEBUG
AM_CPPFLAGS += -DNDEBUG
AM_CFLAGS += -fomit-frame-pointer
-else
-AM_LDFLAGS = -export-dynamic
endif
+# PYTHONDONTWRITEBYTECODE=yes keeps Python from creating .pyc and .pyo
+# files. Creating .py[co] works OK for any given version of Open
+# vSwitch, but it causes trouble if you switch from a version with
+# foo/__init__.py into an (older) version with plain foo.py, since
+# foo/__init__.pyc will cause Python to ignore foo.py.
+run_python = \
+ PYTHONDONTWRITEBYTECODE=yes \
+ PYTHONPATH=$(top_srcdir)/python:$$PYTHONPATH \
+ $(PYTHON)
+
ALL_LOCAL =
BUILT_SOURCES =
CLEANFILES =
EXTRA_DIST = \
CodingStyle \
DESIGN \
+ FAQ \
+ INSTALL \
INSTALL.KVM \
INSTALL.Libvirt \
- INSTALL.Linux \
INSTALL.RHEL \
INSTALL.SSL \
INSTALL.XenServer \
INSTALL.bridge \
INSTALL.userspace \
+ IntegrationGuide \
NOTICE \
PORTING \
README-gcov \
sed \
-e 's,[@]PKIDIR[@],$(PKIDIR),g' \
-e 's,[@]LOGDIR[@],$(LOGDIR),g' \
+ -e 's,[@]DBDIR[@],$(DBDIR),g' \
-e 's,[@]PERL[@],$(PERL),g' \
-e 's,[@]PYTHON[@],$(PYTHON),g' \
-e 's,[@]RUNDIR[@],$(RUNDIR),g' \
# If we're checked out from a Git repository, make sure that every
# file that is in Git is distributed.
+#
+# We only enable this check when GNU make is in use because the
+# Makefile in datapath/linux, needed to get the list of files to
+# distribute, requires GNU make extensions.
+if GNU_MAKE
ALL_LOCAL += dist-hook-git
dist-hook-git: distfiles
@if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1; then \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t" | sort -u > $@
CLEANFILES += distfiles
+endif
+.PHONY: dist-hook-git
# Check that "struct vlog_ratelimit" is always declared "static".
ALL_LOCAL += rate-limit-check
echo "'struct vlog_rate_limit' must always be 'static'"; \
exit 1; \
fi
+.PHONY: rate-limit-check
if HAVE_GROFF
ALL_LOCAL += manpage-check
-post-v1.6.0
+post-v1.8.0
+------------------------
+ - FreeBSD is now a supported platform, thanks to code contributions from
+ Gaetano Catalli, Ed Maste, and Giuseppe Lettieri.
+ - ovs-bugtool: New --ovs option to report only OVS related information.
+ - New %t and %T log escapes to identify the subprogram within a
+ cooperating group of processes or threads that emitted a log message.
+ The default log patterns now include this information.
+ - OpenFlow:
+ - Allow bitwise masking for SHA and THA fields in ARP, SLL and TLL
+ fields in IPv6 neighbor discovery messages, and IPv6 flow label.
+ - ovs-dpctl:
+ - Support requesting the port number with the "port_no" option in
+ the "add-if" command.
+ - ovs-pki: The "online PKI" features have been removed, along with
+ the ovs-pki-cgi program that facilitated it, because of some
+ alarmist insecurity claims. We do not believe that these claims
+ are true, but because we do not know of any users for this
+ feature it seems better on balance to remove it. (The ovs-pki-cgi
+ program was not included in distribution packaging.)
+ - Stable bond mode is deprecated and will be removed no earlier than
+ February 2013. Please email dev@openvswitch.org with concerns.
+ - The autopath action is deprecated and will be removed no earlier than
+ February 2013. Please email dev@openvswitch.org with concerns.
+ - The null interface type is deprecated and will be removed no earlier
+ than February 2013. Please email dev@openvswitch.org with concerns.
+
+
+v1.8.0 - xx xxx xxxx
+------------------------
+ - New FAQ. Please send updates and additions!
+ - Authors of controllers, please read the new section titled "Action
+ Reproduction" in DESIGN, which describes an Open vSwitch change in
+ behavior in corner cases that may affect some controllers.
+ - ovs-l3ping:
+ - A new test utility that can create L3 tunnel between two Open
+ vSwitches and detect connectivity issues.
+ - ovs-ofctl:
+ - New --sort and --rsort options for "dump-flows" command.
+ - "mod-port" command can now control all OpenFlow config flags.
+ - OpenFlow:
+ - Allow general bitwise masking for IPv4 and IPv6 addresses in
+ IPv4, IPv6, and ARP packets. (Previously, only CIDR masks
+ were allowed.)
+ - Allow support for arbitrary Ethernet masks. (Previously, only
+ the multicast bit in the destination address could be individually
+ masked.)
+ - New field OXM_OF_METADATA, to align with OpenFlow 1.1.
+ - The OFPST_QUEUE request now reports an error if a specified port or
+ queue does not exist, or for requests for a specific queue on all
+ ports, if the specified queue does not exist on any port. (Previous
+ versions generally reported an empty set of results.)
+ - New "flow monitor" feature to allow controllers to be notified of
+ flow table changes as they happen.
+ - Additional protocols are not mirrored and dropped when forward-bpdu is
+ false. For a full list, see the ovs-vswitchd.conf.db man page.
+ - Open vSwitch now sends RARP packets in situations where it previously
+ sent a custom protocol, making it consistent with behavior of QEMU and
+ VMware.
+ - All Open vSwitch programs and log files now show timestamps in UTC,
+ instead the local timezone, by default.
+
+
+v1.7.0 - 30 Jul 2012
------------------------
- kernel modules are renamed. openvswitch_mod.ko is now
openvswitch.ko and brcompat_mod.ko is now brcompat.ko.
- Increased the number of NXM registers to 8.
- - Added ability to configure dscp setting for manager and controller
+ - Added ability to configure DSCP setting for manager and controller
connections. By default, these connections have a DSCP value of
Internetwork Control (0xc0).
- Added the granular link health statistics, 'cfm_health', to an
interface.
- OpenFlow:
- Added support to mask nd_target for ICMPv6 neighbor discovery flows.
+ - Added support for OpenFlow 1.3 port description (OFPMP_PORT_DESC)
+ multipart messages.
+ - ovs-ofctl:
+ - Added the "dump-ports-desc" command to retrieve port
+ information using the new port description multipart messages.
- ovs-test:
- Added support for spawning ovs-test server from the client.
- Now ovs-test is able to automatically create test bridges and ports.
- "ovs-dpctl dump-flows" now prints observed TCP flags in TCP flows.
+ - Tripled flow setup performance.
- The "coverage/log" command previously available through ovs-appctl
has been replaced by "coverage/show". The new command replies with
coverage counter values, instead of logging them.
-v1.6.0 - xx xxx xxxx
+v1.6.1 - 25 Jun 2012
+------------------------
+ - Allow OFPP_CONTROLLER as the in_port for packet-out messages.
+
+
+v1.6.0 - 24 Feb 2012
------------------------
+ *** Internal only release ***
- bonding
- LACP bonds no longer fall back to balance-slb when negotiations fail.
Instead they drop traffic.
- 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.
- 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
{=}, {!=}, {<}, {>}, {<=}, 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
available at http://www.apache.org/licenses/LICENSE-2.0.html
Open vSwitch
-Copyright (c) 2007, 2008, 2009, 2010, 2011 Nicira Networks.
+Copyright (c) 2007, 2008, 2009, 2010, 2011 Nicira, Inc.
+
+Open vSwitch BSD port
+Copyright (c) 2011 Gaetano Catalli
Apache Portable Runtime
Copyright 2008 The Apache Software Foundation.
latter is equivalent to the one of the former, but the difference in
name makes the intended use obvious.
+The default "fail-mode" for Open vSwitch bridges is "standalone",
+meaning that, when the OpenFlow controllers cannot be contacted, Open
+vSwitch acts as a regular MAC-learning switch. This works well in
+virtualization environments where there is normally just one uplink
+(either a single physical interface or a bond). In a more general
+environment, it can create loops. So, if you are porting to a
+general-purpose switch platform, you should consider changing the
+default "fail-mode" to "secure", which does not behave this way. See
+documentation for the "fail-mode" column in the Bridge table in
+ovs-vswitchd.conf.db(5) for more information.
+
lib/entropy.c assumes that it can obtain high-quality random number
seeds at startup by reading from /dev/urandom. You will need to
modify it if this is not true on your platform.
What other documentation is available?
--------------------------------------
-To install Open vSwitch on a regular Linux machine, read INSTALL.Linux.
+To install Open vSwitch on a regular Linux machine, read INSTALL.
+
+For answers to common questions, read FAQ.
To use Open vSwitch as a drop-in replacement for the Linux bridge,
read INSTALL.bridge.
To build RPMs for installing Open vSwitch on a Red Hat Enterprise
Linux host, read INSTALL.RHEL.
-To use Open vSwitch with KVM on Linux, read INSTALL.Linux, then
+To use Open vSwitch with KVM on Linux, read INSTALL, then
INSTALL.KVM.
To use Open vSwitch with Libvirt, read INSTALL.Libvirt.
--enable-coverage=yes
Build with gcov code coverage support.
-
- If you enable coverage and you have the "lcov" and "genhtml"
- programs in PATH, then you may run "make check-lcov" to produce a
- directory "tests/coverage.html" in the build directory with an
- analysis of the test suite's coverage. lcov is available at
- http://ltp.sourceforge.net/coverage/lcov.php
How to Submit Patches for Open vSwitch
======================================
-Send changes to Open vSwitch as patches to discuss@openvswitch.org.
+Send changes to Open vSwitch as patches to dev@openvswitch.org.
One patch per email, please. More details are included below.
If you are using Git, then "git format-patch" takes care of most of
# -*- autoconf -*-
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_warn_if_lro],
[OVS_DEFINE([HAVE_SKB_WARN_LRO])])
OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [consume_skb])
+ OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_frag_page])
OVS_GREP_IFELSE([$KSRC/include/linux/string.h], [kmemdup], [],
[OVS_GREP_IFELSE([$KSRC/include/linux/slab.h], [kmemdup])])
[Define to 1 if net/if_packet.h is available.])
fi])
+dnl Checks for net/if_dl.h.
+AC_DEFUN([OVS_CHECK_IF_DL],
+ [AC_CHECK_HEADER([net/if_dl.h],
+ [HAVE_IF_DL=yes],
+ [HAVE_IF_DL=no])
+ AM_CONDITIONAL([HAVE_IF_DL], [test "$HAVE_IF_DL" = yes])
+ if test "$HAVE_IF_DL" = yes; then
+ AC_DEFINE([HAVE_IF_DL], [1],
+ [Define to 1 if net/if_dl.h is available.])
+ fi])
+
dnl Checks for buggy strtok_r.
dnl
dnl Some versions of glibc 2.7 has a bug in strtok_r when compiling
dnl
dnl Checks whether make has the GNU make $(if condition,then,else) extension.
dnl Runs 'if-true' if so, 'if-false' otherwise.
-AC_DEFUN([OVS_MAKE_HAS_IF],
+AC_DEFUN([OVS_CHECK_MAKE_IF],
[AC_CACHE_CHECK(
[whether ${MAKE-make} has GNU make \$(if) extension],
[ovs_cv_gnu_make_if],
ovs_cv_gnu_make_if=yes
else
ovs_cv_gnu_make_if=no
- fi])
- AS_IF([test $ovs_cv_gnu_make_if = yes], [$1], [$2])])
+ fi])])
+
+dnl OVS_CHECK_GNU_MAKE
+dnl
+dnl Checks whether make is GNU make (because Linux kernel Makefiles
+dnl only work with GNU make).
+AC_DEFUN([OVS_CHECK_GNU_MAKE],
+ [AC_CACHE_CHECK(
+ [whether ${MAKE-make} is GNU make],
+ [ovs_cv_gnu_make],
+ [rm -f conftest.out
+ AS_ECHO(["$as_me:$LINENO: invoking ${MAKE-make} --version:"]) >&AS_MESSAGE_LOG_FD 2>&1
+ ${MAKE-make} --version >conftest.out 2>&1
+ cat conftest.out >&AS_MESSAGE_LOG_FD 2>&1
+ result=`cat conftest.out`
+ rm -f conftest.mk conftest.out
+
+ case $result in # (
+ GNU*) ovs_cv_gnu_make=yes ;; # (
+ *) ovs_cv_gnu_make=no ;;
+ esac])
+ AM_CONDITIONAL([GNU_MAKE], [test $ovs_cv_gnu_make = yes])])
dnl OVS_CHECK_SPARSE_TARGET
dnl
dnl OVS_ENABLE_SPARSE
AC_DEFUN([OVS_ENABLE_SPARSE],
[AC_REQUIRE([OVS_CHECK_SPARSE_TARGET])
- OVS_MAKE_HAS_IF(
- [AC_CONFIG_COMMANDS_PRE(
- [: ${SPARSE=sparse}
- AC_SUBST([SPARSE])
- CC='$(if $(C),REAL_CC="'"$CC"'" CHECK="$(SPARSE) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS)" cgcc $(CGCCFLAGS),'"$CC"')'])])])
+ AC_REQUIRE([OVS_CHECK_MAKE_IF])
+ : ${SPARSE=sparse}
+ AC_SUBST([SPARSE])
+ AC_CONFIG_COMMANDS_PRE(
+ [if test $ovs_cv_gnu_make_if = yes; then
+ CC='$(if $(C),REAL_CC="'"$CC"'" CHECK="$(SPARSE) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS)" cgcc $(CGCCFLAGS),'"$CC"')'
+ fi])])
"NX1.0+": ("OF1.0", "OF1.1", "OF1.2"),
"NX1.0": ("OF1.0",),
"NX1.1": ("OF1.1",),
+ "NX1.1+": ("OF1.1",),
"NX1.2": ("OF1.2",)}
if targets not in target_map:
fatal("%s: unknown error domain" % targets)
}"""
print """
-const struct ofperr_domain %s = {
+static const struct ofperr_domain %s = {
"%s",
%d,
%s_decode,
--- /dev/null
+#! /usr/bin/python
+
+import sys
+import os.path
+import re
+
+line = ""
+
+OFP10_VERSION = 0x01
+OFP11_VERSION = 0x02
+OFP12_VERSION = 0x03
+OFP13_VERSION = 0x04
+
+NX_VENDOR_ID = 0x00002320
+
+OFPT_VENDOR = 4
+OFPT10_STATS_REQUEST = 16
+OFPT10_STATS_REPLY = 17
+OFPT11_STATS_REQUEST = 18
+OFPT11_STATS_REPLY = 19
+OFPST_VENDOR = 0xffff
+
+version_map = {"1.0": (OFP10_VERSION, OFP10_VERSION),
+ "1.1": (OFP11_VERSION, OFP11_VERSION),
+ "1.2": (OFP12_VERSION, OFP12_VERSION),
+ "1.3": (OFP13_VERSION, OFP13_VERSION),
+ "1.0+": (OFP10_VERSION, OFP13_VERSION),
+ "1.1+": (OFP11_VERSION, OFP13_VERSION),
+ "1.2+": (OFP12_VERSION, OFP13_VERSION),
+ "1.3+": (OFP13_VERSION, OFP13_VERSION),
+ "1.0-1.1": (OFP10_VERSION, OFP11_VERSION),
+ "1.0-1.2": (OFP10_VERSION, OFP12_VERSION)}
+
+def get_line():
+ global line
+ global line_number
+ line = input_file.readline()
+ line_number += 1
+ if line == "":
+ fatal("unexpected end of input")
+
+n_errors = 0
+def error(msg):
+ global n_errors
+ sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
+ n_errors += 1
+
+def fatal(msg):
+ error(msg)
+ sys.exit(1)
+
+def usage():
+ argv0 = os.path.basename(sys.argv[0])
+ print '''\
+%(argv0)s, for extracting OpenFlow message types from header files
+usage: %(argv0)s INPUT OUTPUT
+ where INPUT is the name of the input header file
+ and OUTPUT is the output file name.
+Despite OUTPUT, the output is written to stdout, and the OUTPUT argument
+only controls #line directives in the output.\
+''' % {"argv0": argv0}
+ sys.exit(0)
+
+def make_sizeof(s):
+ m = re.match(r'(.*) up to (.*)', s)
+ if m:
+ struct, member = m.groups()
+ return "offsetof(%s, %s)" % (struct, member)
+ else:
+ return "sizeof(%s)" % s
+
+def extract_ofp_msgs(output_file_name):
+ raw_types = []
+
+ all_hdrs = {}
+ all_raws = {}
+ all_raws_order = []
+
+ while True:
+ get_line()
+ if re.match('enum ofpraw', line):
+ break
+
+ while True:
+ get_line()
+ first_line_number = line_number
+ here = '%s:%d' % (file_name, line_number)
+ if (line.startswith('/*')
+ or line.startswith(' *')
+ or not line
+ or line.isspace()):
+ continue
+ elif re.match('}', line):
+ break
+
+ if not line.lstrip().startswith('/*'):
+ fatal("unexpected syntax between ofpraw types")
+
+ comment = line.lstrip()[2:].strip()
+ while not comment.endswith('*/'):
+ get_line()
+ if line.startswith('/*') or not line or line.isspace():
+ fatal("unexpected syntax within error")
+ comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
+ comment = comment[:-2].rstrip()
+
+ m = re.match(r'([A-Z]+) ([-.+\d]+) \((\d+)\): ([^.]+)\.$', comment)
+ if not m:
+ fatal("unexpected syntax between messages")
+ type_, versions, number, contents = m.groups()
+ number = int(number)
+
+ get_line()
+ m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_,
+ line)
+ if not m:
+ fatal("syntax error expecting OFPRAW_ enum")
+ vinfix, name = m.groups()
+ rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name)
+
+ min_version, max_version = version_map[versions]
+
+ human_name = '%s_%s' % (type_, name)
+ if type_.endswith('ST'):
+ if rawname.endswith('_REQUEST'):
+ human_name = human_name[:-8] + " request"
+ elif rawname.endswith('_REPLY'):
+ human_name = human_name[:-6] + " reply"
+ else:
+ fatal("%s messages are statistics but %s doesn't end "
+ "in _REQUEST or _REPLY" % (type_, rawname))
+
+ these_hdrs = []
+ for version in range(min_version, max_version + 1):
+ if type_ == 'OFPT':
+ if number == OFPT_VENDOR:
+ fatal("OFPT (%d) is used for vendor extensions"
+ % number)
+ elif (version == OFP10_VERSION
+ and (number == OFPT10_STATS_REQUEST
+ or number == OFPT10_STATS_REPLY)):
+ fatal("OFPT 1.0 (%d) is used for stats messages"
+ % number)
+ elif (version != OFP10_VERSION
+ and (number == OFPT11_STATS_REQUEST
+ or number == OFPT11_STATS_REPLY)):
+ fatal("OFPT 1.1+ (%d) is used for stats messages"
+ % number)
+ hdrs = (version, number, 0, 0, 0)
+ elif type_ == 'OFPST' and name.endswith('_REQUEST'):
+ if version == OFP10_VERSION:
+ hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0)
+ else:
+ hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0)
+ elif type_ == 'OFPST' and name.endswith('_REPLY'):
+ if version == OFP10_VERSION:
+ hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0)
+ else:
+ hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0)
+ elif type_ == 'NXT':
+ hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number)
+ elif type_ == 'NXST' and name.endswith('_REQUEST'):
+ if version == OFP10_VERSION:
+ hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR,
+ NX_VENDOR_ID, number)
+ else:
+ hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR,
+ NX_VENDOR_ID, number)
+ elif type_ == 'NXST' and name.endswith('_REPLY'):
+ if version == OFP10_VERSION:
+ hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR,
+ NX_VENDOR_ID, number)
+ else:
+ hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR,
+ NX_VENDOR_ID, number)
+ else:
+ fatal("type '%s' unknown" % type_)
+
+ if hdrs in all_hdrs:
+ error("Duplicate message definition for %s." % str(hdrs))
+ sys.stderr.write("%s: Here is the location "
+ "of the previous definition.\n"
+ % (all_hdrs[hdrs]))
+ all_hdrs[hdrs] = here
+ these_hdrs.append(hdrs)
+
+ extra_multiple = '0'
+ if contents == 'void':
+ min_body = '0'
+ else:
+ min_body_elem = []
+ for c in [s.strip() for s in contents.split(",")]:
+ if c.endswith('[]'):
+ if extra_multiple == '0':
+ extra_multiple = make_sizeof(c[:-2])
+ else:
+ error("Cannot have multiple [] elements")
+ else:
+ min_body_elem.append(c)
+
+ if min_body_elem:
+ min_body = " + ".join([make_sizeof(s)
+ for s in min_body_elem])
+ else:
+ if extra_multiple == '0':
+ error("Must specify contents (use 'void' if empty)")
+ min_body = 0
+
+ if rawname in all_raws:
+ fatal("%s: Duplicate name" % rawname)
+
+ all_raws[rawname] = {"hdrs": these_hdrs,
+ "min_version": min_version,
+ "max_version": max_version,
+ "min_body": min_body,
+ "extra_multiple": extra_multiple,
+ "type": type_,
+ "human_name": human_name,
+ "line": first_line_number}
+ all_raws_order.append(rawname)
+
+ continue
+
+ while True:
+ get_line()
+ if re.match('enum ofptype', line):
+ break
+
+ while True:
+ get_line()
+ if re.match(r'\s*/?\*', line) or line.isspace():
+ continue
+ elif re.match('}', line):
+ break
+
+ if not re.match(r'\s*OFPTYPE_.*/\*', line):
+ fatal("unexpected syntax between OFPTYPE_ definitions")
+
+ syntax = line.strip()
+ while not syntax.endswith('*/'):
+ get_line()
+ if not line.strip().startswith('*'):
+ fatal("unexpected syntax within OFPTYPE_ definition")
+ syntax += ' %s' % line.strip().lstrip('* \t')
+ syntax = syntax.strip()
+
+ m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax)
+ if not m:
+ fatal("syntax error in OFPTYPE_ definition")
+
+ ofptype, raws_ = m.groups()
+ raws = [s.rstrip('.') for s in raws_.split()]
+ for raw in raws:
+ if not re.match('OFPRAW_[A-Z0-9_]+$', raw):
+ fatal("%s: invalid OFPRAW_* name syntax" % raw)
+ if raw not in all_raws:
+ fatal("%s: not a declared OFPRAW_* name" % raw)
+ if "ofptype" in all_raws[raw]:
+ fatal("%s: already part of %s"
+ % (raw, all_raws[raw]["ofptype"]))
+ all_raws[raw]["ofptype"] = ofptype
+
+ input_file.close()
+
+ if n_errors:
+ sys.exit(1)
+
+ output = []
+ output.append("/* Generated automatically; do not modify! "
+ "-*- buffer-read-only: t -*- */")
+ output.append("")
+
+ for raw in all_raws_order:
+ r = all_raws[raw]
+ output.append("static struct raw_instance %s_instances[] = {"
+ % raw.lower())
+ for hdrs in r['hdrs']:
+ output.append(" { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 },"
+ % (hdrs + (raw,)))
+
+ output.append("};")
+
+ output.append("")
+
+ output.append("static struct raw_info raw_infos[] = {")
+ for raw in all_raws_order:
+ r = all_raws[raw]
+ if "ofptype" not in r:
+ error("%s: no defined OFPTYPE_" % raw)
+ continue
+ output.append(" {")
+ output.append(" %s_instances," % raw.lower())
+ output.append(" %d, %d," % (r["min_version"], r["max_version"]))
+ output.append("#line %s \"%s\"" % (r["line"], file_name))
+ output.append(" %s," % r["min_body"])
+ output.append("#line %s \"%s\"" % (r["line"], file_name))
+ output.append(" %s," % r["extra_multiple"])
+ output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name))
+ output.append(" %s," % r["ofptype"])
+ output.append(" \"%s\"," % r["human_name"])
+ output.append(" },")
+
+ if r['type'].endswith("ST"):
+ for hdrs in r['hdrs']:
+ op_hdrs = list(hdrs)
+ if hdrs[0] == OFP10_VERSION:
+ if hdrs[1] == OFPT10_STATS_REQUEST:
+ op_hdrs[1] = OFPT10_STATS_REPLY
+ elif hdrs[1] == OFPT10_STATS_REPLY:
+ op_hdrs[1] = OFPT10_STATS_REQUEST
+ else:
+ assert False
+ else:
+ if hdrs[1] == OFPT11_STATS_REQUEST:
+ op_hdrs[1] = OFPT11_STATS_REPLY
+ elif hdrs[1] == OFPT11_STATS_REPLY:
+ op_hdrs[1] = OFPT11_STATS_REQUEST
+ else:
+ assert False
+ if tuple(op_hdrs) not in all_hdrs:
+ if r["human_name"].endswith("request"):
+ fatal("%s has no corresponding reply"
+ % r["human_name"])
+ else:
+ fatal("%s has no corresponding request"
+ % r["human_name"])
+ output.append("};")
+
+ if n_errors:
+ sys.exit(1)
+
+ return output
+
+
+if __name__ == '__main__':
+ if '--help' in sys.argv:
+ usage()
+ elif len(sys.argv) != 3:
+ sys.stderr.write("exactly one non-option arguments required; "
+ "use --help for help\n")
+ sys.exit(1)
+ else:
+ global file_name
+ global input_file
+ global line_number
+ file_name = sys.argv[1]
+ input_file = open(file_name)
+ line_number = 0
+
+ for line in extract_ofp_msgs(sys.argv[2]):
+ print line
+
-# Copyright (c) 2008, 2011 Nicira Networks.
+# Copyright (c) 2008, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2008 Nicira Networks.
+# Copyright (c) 2008 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# limitations under the License.
AC_PREREQ(2.64)
-AC_INIT(openvswitch, 1.6.90, ovs-bugs@openvswitch.org)
+AC_INIT(openvswitch, 1.8.90, ovs-bugs@openvswitch.org)
AC_CONFIG_SRCDIR([datapath/datapath.c])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([build-aux])
AC_SEARCH_LIBS([pow], [m])
AC_SEARCH_LIBS([clock_gettime], [rt])
AC_SEARCH_LIBS([timer_create], [rt])
+AC_SEARCH_LIBS([pcap_open_live], [pcap])
OVS_CHECK_COVERAGE
OVS_CHECK_NDEBUG
OVS_CHECK_OVSDBMONITOR
OVS_CHECK_DOT
OVS_CHECK_IF_PACKET
+OVS_CHECK_IF_DL
OVS_CHECK_STRTOK_R
AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec],
[], [], [[#include <sys/stat.h>]])
OVS_CHECK_PKIDIR
OVS_CHECK_RUNDIR
+OVS_CHECK_DBDIR
OVS_CHECK_BACKTRACE
OVS_CHECK_MALLOC_HOOKS
OVS_CHECK_VALGRIND
OVS_CHECK_XENSERVER_VERSION
OVS_CHECK_GROFF
OVS_CHECK_BRCOMPAT
+OVS_CHECK_GNU_MAKE
OVS_ENABLE_OPTION([-Wall])
OVS_ENABLE_OPTION([-Wno-sign-compare])
Naively, to add VLAN support, it makes sense to add a new "vlan" flow
key attribute to contain the VLAN tag, then continue to decode the
encapsulated headers beyond the VLAN tag using the existing field
-definitions. With this change, an TCP packet in VLAN 10 would have a
+definitions. With this change, a TCP packet in VLAN 10 would have a
flow key much like this:
eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...)
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
module_exit(brc_cleanup);
MODULE_DESCRIPTION("Open vSwitch bridge compatibility");
-MODULE_AUTHOR("Nicira Networks");
+MODULE_AUTHOR("Nicira, Inc.");
MODULE_LICENSE("GPL");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
#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>
#include "vport-internal_dev.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) || \
- LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
-#error Kernels before 2.6.18 or after 3.3 are not supported by this version of Open vSwitch.
+ LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+#error Kernels before 2.6.18 or after 3.5 are not supported by this version of Open vSwitch.
#endif
#define REHASH_FLOW_INTERVAL (10 * 60 * HZ)
struct sk_buff *skb,
const struct dp_upcall_info *upcall_info)
{
+ unsigned short gso_type = skb_shinfo(skb)->gso_type;
struct dp_upcall_info later_info;
struct sw_flow_key later_key;
struct sk_buff *segs, *nskb;
int err;
segs = skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM);
- if (IS_ERR(skb))
- return PTR_ERR(skb);
+ if (IS_ERR(segs))
+ return PTR_ERR(segs);
/* Queue all of the segments. */
skb = segs;
if (err)
break;
- if (skb == segs && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) {
+ if (skb == segs && gso_type & SKB_GSO_UDP) {
/* The initial flow key extracted by ovs_flow_extract()
* in this case is for a first fragment, so we need to
* properly mark later fragments.
return validate_actions(actions, key, depth + 1);
}
+static int validate_tp_port(const struct sw_flow_key *flow_key)
+{
+ if (flow_key->eth.type == htons(ETH_P_IP)) {
+ if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst)
+ return 0;
+ } else if (flow_key->eth.type == htons(ETH_P_IPV6)) {
+ if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst)
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static int validate_set(const struct nlattr *a,
const struct sw_flow_key *flow_key)
{
if (flow_key->eth.type != htons(ETH_P_IP))
return -EINVAL;
- if (!flow_key->ipv4.addr.src || !flow_key->ipv4.addr.dst)
+ if (!flow_key->ip.proto)
return -EINVAL;
ipv4_key = nla_data(ovs_key);
if (flow_key->ip.proto != IPPROTO_TCP)
return -EINVAL;
- if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst)
- return -EINVAL;
-
- break;
+ return validate_tp_port(flow_key);
case OVS_KEY_ATTR_UDP:
if (flow_key->ip.proto != IPPROTO_UDP)
return -EINVAL;
- if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst)
- return -EINVAL;
- break;
+ return validate_tp_port(flow_key);
default:
return -EINVAL;
dp->ifobj.kset = NULL;
kobject_init(&dp->ifobj, &dp_ktype);
+ ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
+
/* Allocate table. */
err = -ENOMEM;
rcu_assign_pointer(dp->table, ovs_flow_tbl_alloc(TBL_MIN_BUCKETS));
err = -ENOMEM;
goto err_destroy_table;
}
- ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
dp->ports = kmalloc(DP_VPORT_HASH_BUCKETS * sizeof(struct hlist_head),
GFP_KERNEL);
err_destroy_table:
ovs_flow_tbl_destroy(genl_dereference(dp->table));
err_free_dp:
+ release_net(ovs_dp_get_net(dp));
kfree(dp);
err_unlock_rtnl:
rtnl_unlock();
reply = ovs_vport_cmd_build_info(vport, info->snd_pid, info->snd_seq,
OVS_VPORT_CMD_NEW);
if (IS_ERR(reply)) {
- err = PTR_ERR(reply);
netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
- ovs_dp_vport_multicast_group.id, err);
- return 0;
+ ovs_dp_vport_multicast_group.id, PTR_ERR(reply));
+ goto exit_unlock;
}
genl_notify(reply, genl_info_net(info), info->snd_pid,
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
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);
}
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
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 \
#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, ...) \
--- /dev/null
+#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
#define consume_skb kfree_skb
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
+#ifndef HAVE_SKB_FRAG_PAGE
static inline struct page *skb_frag_page(const skb_frag_t *frag)
{
return frag->page;
#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);
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)
(_work)->func = (_func); \
} while (0)
-#endif /* kernel version < 2.6.23 */
+extern void flush_scheduled_work(void);
#endif
#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;
BUG_ON(!list_empty(&workq));
kthread_stop(workq_thread);
}
-#endif
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
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)))
#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);
*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;
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;
else
tos = mutable->tos;
- tos = INET_ECN_encapsulate(tos, inner_tos);
-
/* Route lookup */
rt = find_route(vport, mutable, tos, &cache);
if (unlikely(!rt))
if (unlikely(!cache))
unattached_dst = &rt_dst(rt);
+ tos = INET_ECN_encapsulate(tos, inner_tos);
+
/* Reset SKB */
nf_reset(skb);
secpath_reset(skb);
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;
}
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
* Distributed under the terms of the GNU GPL version 2.
*
* Significant portions of this file may be copied from parts of the Linux
/*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
#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"
#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);
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
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;
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;
}
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
/*
- * Copyright (c) 2007-2012 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
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 \
debian/openvswitch-controller.install \
debian/openvswitch-controller.manpages \
debian/openvswitch-controller.postinst \
+ debian/openvswitch-controller.postrm \
debian/openvswitch-datapath-module-_KVERS_.postinst.modules.in \
debian/openvswitch-datapath-dkms.postinst \
debian/openvswitch-datapath-dkms.prerm \
debian/openvswitch-switch.postinst \
debian/openvswitch-switch.postrm \
debian/openvswitch-switch.template \
+ debian/openvswitch-switch.links \
debian/openvswitch-test.dirs \
debian/openvswitch-test.install \
debian/openvswitch-test.manpages \
debian/python-openvswitch.install \
debian/rules \
debian/rules.modules \
+ debian/ifupdown.sh \
debian/source/format
check-debian-changelog-version:
-openvswitch (1.6.90-1) unstable; urgency=low
+openvswitch (1.8.90-1) unstable; urgency=low
+ [ Open vSwitch team ]
+ * New upstream version
+ - Nothing yet! Try NEWS...
+
+ -- Open vSwitch team <dev@openvswitch.org> Mon, 07 May 2012 14:14:52 +0900
+
+openvswitch (1.8.0-1) unstable; urgency=low
+ [ Open vSwitch team ]
+ * New upstream version
+ - New FAQ. Please send updates and additions!
+ - Authors of controllers, please read the new section titled "Action
+ Reproduction" in DESIGN, which describes an Open vSwitch change in
+ behavior in corner cases that may affect some controllers.
+ - ovs-l3ping:
+ - A new test utility that can create L3 tunnel between two Open
+ vSwitches and detect connectivity issues.
+ - ovs-ofctl:
+ - New --sort and --rsort options for "dump-flows" command.
+ - "mod-port" command can now control all OpenFlow config flags.
+ - OpenFlow:
+ - Allow general bitwise masking for IPv4 and IPv6 addresses in
+ IPv4, IPv6, and ARP packets. (Previously, only CIDR masks
+ were allowed.)
+ - Allow support for arbitrary Ethernet masks. (Previously, only
+ the multicast bit in the destination address could be individually
+ masked.)
+ - New field OXM_OF_METADATA, to align with OpenFlow 1.1.
+ - The OFPST_QUEUE request now reports an error if a specified port or
+ queue does not exist, or for requests for a specific queue on all
+ ports, if the specified queue does not exist on any port. (Previous
+ versions generally reported an empty set of results.)
+ - New "flow monitor" feature to allow controllers to be notified of
+ flow table changes as they happen.
+ - Additional protocols are not mirrored and dropped when forward-bpdu is
+ false. For a full list, see the ovs-vswitchd.conf.db man page.
+ - Open vSwitch now sends RARP packets in situations where it previously
+ sent a custom protocol, making it consistent with behavior of QEMU and
+ VMware.
+ - All Open vSwitch programs and log files now show timestamps in UTC,
+ instead the local timezone, by default.
+
+ -- Open vSwitch team <dev@openvswitch.org> Mon, 16 Jul 2012 16:44:52 +0900
+
+openvswitch (1.7.0-1) unstable; urgency=low
[ Open vSwitch team ]
* New upstream version
- - Nothing yet! Try NEWS...
+ - kernel modules are renamed. openvswitch_mod.ko is now
+ openvswitch.ko and brcompat_mod.ko is now brcompat.ko.
+ - Increased the number of NXM registers to 8.
+ - Added ability to configure dscp setting for manager and controller
+ connections. By default, these connections have a DSCP value of
+ Internetwork Control (0xc0).
+ - Added the granular link health statistics, 'cfm_health', to an
+ interface.
+ - OpenFlow:
+ - Added support to mask nd_target for ICMPv6 neighbor discovery flows.
+ - Added support for OpenFlow 1.3 port description (OFPMP_PORT_DESC)
+ multipart messages.
+ - ovs-ofctl:
+ - Added the "dump-ports-desc" command to retrieve port
+ information using the new port description multipart messages.
+ - ovs-test:
+ - Added support for spawning ovs-test server from the client.
+ - Now ovs-test is able to automatically create test bridges and ports.
+ - "ovs-dpctl dump-flows" now prints observed TCP flags in TCP flows.
+ - Tripled flow setup performance.
+ - The "coverage/log" command previously available through ovs-appctl
+ has been replaced by "coverage/show". The new command replies with
+ coverage counter values, instead of logging them.
+
+ -- Open vSwitch team <dev@openvswitch.org> Mon, 30 Jul 2012 17:23:57 +0900
+
+openvswitch (1.6.1-1) unstable; urgency=low
+ [ Open vSwitch team ]
+ * New upstream version
+ - Allow OFPP_CONTROLLER as the in_port for packet-out messages.
- -- Open vSwitch team <dev@openvswitch.org> Fri, 24 Feb 2012 11:12:52 +0900
+ -- Open vSwitch team <dev@openvswitch.org> Mon, 25 Jun 2012 13:52:17 +0900
openvswitch (1.6.0-1) unstable; urgency=low
[ Open vSwitch team ]
- ovsdb-tool now uses the typical database and schema installation
directories as defaults.
- -- Open vSwitch team <dev@openvswitch.org> Wed, 15 Feb 2012 11:12:48 +0900
+ -- Open vSwitch team <dev@openvswitch.org> Fri, 01 June 2012 13:06:00 +0900
+
+openvswitch (1.4.0+git20120426-1) unstable; urgency=low
+
+ * New upstream release fixing the following bugs:
+ o Broken log rotation.
+ o Use-after-free error when ports disappear.
+ o Minor memory leaks.
+ o Testsuite failures on big-endian architectures.
+
+ -- Ben Pfaff <pfaffben@debian.org> Thu, 26 Apr 2012 13:46:55 -0700
+
+openvswitch (1.4.0+git20120321-1) unstable; urgency=low
+
+ * New upstream version including:
+ o Features:
+ - ovs-vsctl: Allow "fake bridges" to be created for VLAN 0.
+ - vswitchd: Make the MAC entry aging time configurable.
+ - mac-learning: Increase MAC learning timeout to 300 seconds.
+ o Bug fixes:
+ - netdev-linux: Fix use-after-free when netdev_dump_queues() deletes
+ queues.
+ - netlink-socket: Increase Netlink socket receive buffer size.
+ - ofproto: Fix code that keeps track of MTU.
+ - ovs-monitor-ipsec: Detect correctly IPSEC configuration changes
+ - bond: Incorrectly reported an error in appctl.
+ - socket-util: Unlink Unix domain sockets that bind but fail to connect.
+ - bridge: Remove unwanted ports at time of ofproto creation.
+ - dpif-linux: Make dpif_linux_port_query_by_name() query only one
+ datapath.
+ - ofproto-dpif: Cleanup STP on ports when disabled on their bridge.
+ - configure: Try to extract kernel source directory from build Makefile.
+ - vswitchd: Always configure a default queue for QoS.
+ - ofproto-dpif: Don't output to in_port even if in_port is OFPP_LOCAL.
+ - sflow_agent: Use snprintf() in place of sprintf().
+ o Packaging:
+ - Move PKI directory to FHS-compliant location. Closes: #661090.
+ Thanks to Andreas Beckmann <debian@abeckmann.de> for reporting
+ this bug.
+ - Use a different way to avoid failing install without kernel module.
+ - Avoid unit test failure when doing "unofficial" builds.
+ - Bump standards-version to 3.9.3.
+ - Remove some useless files from the dkms package.
+ - Clean .pyc files in "clean" target.
+ - Remove po-debconf build dependency.
+ - Build-depend on python-all to pull in all Python versions.
+ - Add missing ${python:Depends} to openvswitch-test package.
+ - Improve long descriptions so as to better describe the packages.
+ - Bump debhelper compat level to 8 and make build-depends consistent.
+ - Fix exit status of openvswitch-switch init script "status" command.
+ - Use provided kernel source dir instead of host kernel version.
+ - Do not run "make" if "configure" fails during DKMS build.
+ - Look in /lib/modules instead of /usr/src for DKMS kernel sources.
+ - Fix dependencies for openvswitch-datapath-dkms package.
+ - Don't install Python modules for obsolete Python versions.
+ - Add dependency on ${misc:Depends} to openvswitch-test
+ o Documentation improvements:
+ - ovsdb-doc: Use minus sign in negative numbers in nroff output.
+ - ovsdb-doc: Convert '-' preceding a number as a minus sign, not a
+ hyphen.
+ - ovsdb-doc: Put NAME section into generated manpage.
+ - Fix typo in manpage.
+ - vswitchd: Document behavior of 802.1p priorities with VLAN splinters.
+ - netdev: Fix typo in error message.
+ - INSTALL.Linux: minor typo
+
+ * Many thanks to Thomas Goirand <zigo@debian.org> for contributing many
+ of the packaging fixes listed above.
+
+ * This version fixes most of the lintian errors described in bug
+ #663051, but a few remain, so this upload does not close that bug. I
+ believe that this upload should be suitable for downgrading that bug's
+ severity.
+
+ -- Ben Pfaff <pfaffben@debian.org> Wed, 21 Mar 2012 10:00:28 -0700
+
+openvswitch (1.4.0-2+nmu1) unstable; urgency=low
+
+ * Non maintainer upload.
+ * Removes all patches in debian/patches, because they are patching stuff
+ inconditionally in debian/*, and this should be applied by default.
+ * Uses the correct ${kernel_source_dir} in debian/dkms.conf.in, so that
+ the kernel module builds as expected (Closes: #659685).
+
+ -- Thomas Goirand <zigo@debian.org> Thu, 08 Mar 2012 08:46:24 +0000
openvswitch (1.4.0-2) unstable; urgency=low
Package: openvswitch-switch
Architecture: linux-any
Suggests: openvswitch-datapath-module
-Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, openvswitch-common (= ${binary:Version}), module-init-tools, procps, uuid-runtime
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, openvswitch-common (= ${binary:Version}), module-init-tools, procps, uuid-runtime, netbase
Description: Open vSwitch switch implementations
Open vSwitch is a production quality, multilayer, software-based, Ethernet
virtual switch. It is designed to enable massive network automation through
Upstream Copyright Holders:
- Copyright (c) 2007, 2008, 2009, 2010, 2011 Nicira Networks.
+ Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
Copyright (c) 2010 Jean Tourrilhes - HP-Labs.
Copyright (c) 2008,2009,2010 Citrix Systems, Inc.
and authors listed above.
+ Copyright (c) 2011 Gaetano Catalli
License:
GNU Lesser General Public License version 2.1.
utilities/bugtool/ovs-bugtool
- utilities/bugtool/ovs-bugtool-cfm-show
- utilities/bugtool/ovs-bugtool-lacp-show
- utilities/bugtool/ovs-bugtool-tc-class-show
- utilities/bugtool/ovs-bugtool-daemons-ver
+ utilities/bugtool/ovs-bugtool-*
utilities/bugtool/plugins/kernel-info/openvswitch.xml
utilities/bugtool/plugins/network-status/openvswitch.xml
utilities/bugtool/plugins/system-configuration.xml
BUILT_MODULE_LOCATION[1]=datapath/linux/
DEST_MODULE_LOCATION[0]=/kernel/drivers/net/openvswitch/
DEST_MODULE_LOCATION[1]=/kernel/drivers/net/openvswitch/
+AUTOINSTALL=yes
--- /dev/null
+#! /bin/sh
+
+# Copyright (c) 2012 Nicira, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Have a look at /usr/share/doc/openvswitch-switch/README.Debian
+# for more information about configuring the /etc/network/interfaces.
+
+if [ -z "${IF_OVS_TYPE}" ]; then
+ exit 0
+fi
+
+ovs_vsctl() {
+ ovs-vsctl --timeout=5 "$@"
+}
+
+if (ovs_vsctl --version) > /dev/null 2>&1; then :; else
+ exit 0
+fi
+
+if [ "${MODE}" = "start" ]; then
+ eval OVS_EXTRA=\"${IF_OVS_EXTRA}\"
+
+ case "${IF_OVS_TYPE}" in
+ OVSBridge)
+ ovs_vsctl -- --may-exist add-br "${IFACE}" ${IF_OVS_OPTIONS}\
+ ${OVS_EXTRA+-- $OVS_EXTRA}
+
+ if [ ! -z "${IF_OVS_PORTS}" ]; then
+ ifup --allow="${IFACE}" ${IF_OVS_PORTS}
+ fi
+ ;;
+ OVSPort)
+ ovs_vsctl -- --may-exist add-port "${IF_OVS_BRIDGE}"\
+ "${IFACE}" ${IF_OVS_OPTIONS} \
+ ${OVS_EXTRA+-- $OVS_EXTRA}
+
+ ifconfig "${IFACE}" up
+ ;;
+ OVSIntPort)
+ ovs_vsctl -- --may-exist add-port "${IF_OVS_BRIDGE}"\
+ "${IFACE}" ${IF_OVS_OPTIONS} -- set Interface "${IFACE}"\
+ type=internal ${OVS_EXTRA+-- $OVS_EXTRA}
+
+ ifconfig "${IFACE}" up
+ ;;
+ OVSBond)
+ ovs_vsctl -- --fake-iface add-bond "${IF_OVS_BRIDGE}"\
+ "${IFACE}" ${IF_OVS_BONDS} ${IF_OVS_OPTIONS} \
+ ${OVS_EXTRA+-- $OVS_EXTRA}
+
+ ifconfig "${IFACE}" up
+ ;;
+ *)
+ exit 0
+ ;;
+ esac
+elif [ "${MODE}" = "stop" ]; then
+ case "${IF_OVS_TYPE}" in
+ OVSBridge)
+ if [ ! -z "${IF_OVS_PORTS}" ]; then
+ ifdown --allow="${IFACE}" ${IF_OVS_PORTS}
+ fi
+
+ ovs_vsctl -- --if-exists del-br "${IFACE}"
+ ;;
+ OVSPort|OVSIntPort|OVSBond)
+ ovs_vsctl -- --if-exists del-port "${IF_OVS_BRIDGE}" "${IFACE}"
+ ;;
+ *)
+ exit 0
+ ;;
+ esac
+fi
+
+exit 0
usr/bin/ovsdb-client
usr/sbin/ovs-bugtool
usr/share/openvswitch/bugtool-plugins
-usr/share/openvswitch/scripts/ovs-bugtool-cfm-show
-usr/share/openvswitch/scripts/ovs-bugtool-lacp-show
-usr/share/openvswitch/scripts/ovs-bugtool-tc-class-show
-usr/share/openvswitch/scripts/ovs-bugtool-daemons-ver
+usr/share/openvswitch/scripts/ovs-bugtool-*
#!/bin/sh
#
-# Copyright (c) 2011 Nicira Networks Inc.
+# Copyright (c) 2011 Nicira, Inc.
# Copyright (c) 2007, 2009 Javier Fernandez-Sanguino <jfs@debian.org>
#
# This is free software; you may redistribute it and/or modify
--- /dev/null
+#!/bin/sh
+# postrm script for openvswitch-controller
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ purge)
+ if cd /etc/openvswitch-controller; then
+ rm -f cacert.pem cert.pem privkey.pem req.pem
+ rm -f tmp-privkey.pem tmp-cert.pem tmp-req.pem
+ fi
+ ;;
+
+ remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
Upstream Authors:
- Nicira Networks
+ Nicira, Inc.
Copyright:
- Copyright (C) 2008 Nicira Networks
+ Copyright (C) 2008 Nicira, Inc.
License:
README.Debian for openvswitch-switch
---------------------------------
-* The switch must be configured before it can be used. Edit
- /etc/default/openvswitch-switch, then start the switch manually with
- "/etc/init.d/openvswitch-switch start".
-
* To use the Linux kernel-based switch implementation, you will need
to build and install the Open vSwitch kernel module. To do so, install
the openvswitch-datapath-source package, then follow the instructions
* This package does not yet support the userspace datapath-based
switch implementation.
- -- Ben Pfaff <blp@nicira.com>, Mon, 30 Aug 2010 09:51:19 -0700
+ -- Ben Pfaff <blp@nicira.com>, Fri, 6 Jul 2012 15:12:38 -0700
+
+Debian network scripts integration
+----------------------------------
+This package lets a user to optionally configure Open vSwitch bridges
+and ports from /etc/network/interfaces. Please refer to the interfaces(5)
+manpage for more details regarding /etc/network/interfaces.
+
+The stanzas that configure the OVS bridges should begin with "allow-ovs"
+followed by name of the bridge. Here is an example.
+allow-ovs br0
+
+The stanzas that configure the OVS ports should begin with
+"allow-${bridge-name}" followed by name of the port. Here is an example.
+allow-br0 eth0
+
+The following OVS specific "command" options are supported:
+
+ - ovs_type: This can either be OVSBridge, OVSPort, OVSIntPort or OVSBond
+ depending on whether you configure a bridge, port, an internal port or
+ a bond. This is a required option.
+
+ - ovs_ports: This option specifies all the ports that belong to a bridge.
+
+ - ovs_bridge: This options specifies a bridge to which a port belongs.
+ This is a required option for a port.
+
+ - ovs_bonds: This option specifies the list of physical interfaces to be
+ bonded together.
+
+ - ovs_options: This option lets you add extra arguments to a ovs-vsctl
+ command. See examples.
+
+ - ovs_extra: This option lets you run additional ovs-vsctl commands,
+ separated by "--" (double dash). Variables can be part of the "ovs_extra"
+ option. You can provide all the standard environmental variables
+ described in the interfaces(5) man page. You can also pass shell
+ commands.
+
+More implementation specific details can be seen in the examples.
+
+Examples:
+--------
+ex 1: A standalone bridge.
+
+allow-ovs br0
+iface br0 inet static
+ address 192.168.1.1
+ netmask 255.255.255.0
+ ovs_type OVSBridge
+
+ex 2: A bridge with one port.
+
+allow-ovs br0
+iface br0 inet dhcp
+ ovs_type OVSBridge
+ ovs_ports eth0
+
+allow-br0 eth0
+iface eth0 inet manual
+ ovs_bridge br0
+ ovs_type OVSPort
+
+ex 3: A bridge with multiple physical ports.
+
+allow-ovs br0
+iface br0 inet dhcp
+ ovs_type OVSBridge
+ ovs_ports eth0 eth1
+
+allow-br0 eth0
+iface eth0 inet manual
+ ovs_bridge br0
+ ovs_type OVSPort
+
+allow-br0 eth1
+iface eth1 inet manual
+ ovs_bridge br0
+ ovs_type OVSPort
+
+ex 4: A bridge with an OVS internal port.
+
+allow-ovs br1
+iface br1 inet static
+ address 192.168.1.1
+ netmask 255.255.255.0
+ ovs_type OVSBridge
+ ovs_ports vlan100
+
+allow-br1 vlan100
+iface vlan100 inet manual
+ ovs_bridge br1
+ ovs_type OVSIntPort
+ ovs_options tag=100
+ ovs_extra set interface ${IFACE} external-ids:iface-id=$(hostname -s)
+
+ex 5: Bonding.
+
+allow-ovs br2
+iface br2 inet static
+ address 192.170.1.1
+ netmask 255.255.255.0
+ ovs_type OVSBridge
+ ovs_ports bond0
+
+allow-br2 bond0
+iface bond0 inet manual
+ ovs_bridge br2
+ ovs_type OVSBond
+ ovs_bonds eth2 eth3
+ ovs_options bond_mode=balance-tcp lacp=active
+
+ex 6: Create and destroy bridges.
+
+ifup --allow=ovs $list_of_bridges
+ifdown --allow=ovs $list_of_bridges
+
+-- Gurucharan Shetty <gshetty@nicira.com>, Fri, 04 May 2012 12:58:19 -0700
#! /bin/sh
#
-# Copyright (C) 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
if test X"$FORCE_COREFILES" != X; then
set "$@" --force-corefiles="$FORCE_COREFILES"
fi
+ set "$@" $OVS_CTL_OPTS
"$@" || exit $?
-
- ovs_ctl --protocol=gre enable-protocol
}
stop () {
usr/bin/ovsdb-tool
usr/sbin/ovs-vswitchd
usr/sbin/ovsdb-server
+usr/share/openvswitch/scripts/ovs-check-dead-ifs
usr/share/openvswitch/scripts/ovs-ctl
usr/share/openvswitch/scripts/ovs-lib
usr/share/openvswitch/scripts/ovs-save
usr/share/openvswitch/vswitch.ovsschema
+debian/ifupdown.sh usr/share/openvswitch/scripts
--- /dev/null
+usr/share/openvswitch/scripts/ifupdown.sh etc/network/if-pre-up.d/openvswitch
+usr/share/openvswitch/scripts/ifupdown.sh etc/network/if-post-down.d/openvswitch
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
}
rm -f /etc/default/openvswitch-switch
rm -f /var/log/openvswitch/ovs-vswitchd.log* || true
rm -f /var/log/openvswitch/ovsdb-server.log* || true
+ rm -f /etc/openvswitch/system-id.conf
;;
remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
# 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=
usr/share/openvswitch/python/ovstest usr/lib/python2.6/dist-packages/
usr/bin/ovs-test
+usr/bin/ovs-l3ping
_debian/utilities/ovs-test.8
+_debian/utilities/ovs-l3ping.8
#!/usr/bin/python
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
import ovs.daemon
import ovs.db.idl
import ovs.unixctl
+import ovs.unixctl.server
import ovs.vlog
vlog = ovs.vlog.Vlog("ovs-monitor-ipsec")
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)
$(MAKE) -C _debian DESTDIR=$(CURDIR)/debian/tmp install
cp debian/openvswitch-switch.template debian/openvswitch-switch/usr/share/openvswitch/switch/default.template
dh_install -s
+ dh_link -s
# Must not depend on anything. This is to be called by
# binary-arch/binary-indep
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2011 Nicira, Inc.
*
* This file is offered under your choice of two licenses: Apache 2.0 or GNU
* GPL 2.0 or later. The permission statements for each of these licenses is
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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 = \
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "openflow/openflow.h"
#include "openvswitch/types.h"
-/* The following vendor extensions, proposed by Nicira Networks, are not yet
+/* The following vendor extensions, proposed by Nicira, are not yet
* standardized, so they are not included in openflow.h. Some of them may be
* suitable for standardization; others we never expect to standardize. */
struct nicira_header {
struct ofp_header header;
ovs_be32 vendor; /* NX_VENDOR_ID. */
- ovs_be32 subtype; /* One of NXT_* below. */
+ ovs_be32 subtype; /* See the NXT numbers in ofp-msgs.h. */
};
OFP_ASSERT(sizeof(struct nicira_header) == 16);
-/* Values for the 'subtype' member of struct nicira_header. */
-enum nicira_type {
- /* No longer used. */
- NXT_STATUS_REQUEST__OBSOLETE = 0,
- NXT_STATUS_REPLY__OBSOLETE = 1,
- NXT_ACT_SET_CONFIG__OBSOLETE = 2,
- NXT_ACT_GET_CONFIG__OBSOLETE = 3,
- NXT_COMMAND_REQUEST__OBSOLETE = 4,
- NXT_COMMAND_REPLY__OBSOLETE = 5,
- NXT_FLOW_END_CONFIG__OBSOLETE = 6,
- NXT_FLOW_END__OBSOLETE = 7,
- NXT_MGMT__OBSOLETE = 8,
- NXT_TUN_ID_FROM_COOKIE__OBSOLETE = 9,
-
- /* Controller role support. The request body is struct nx_role_request.
- * The reply echos the request. */
- NXT_ROLE_REQUEST = 10,
- NXT_ROLE_REPLY = 11,
-
- /* Flexible flow specification (aka NXM = Nicira Extended Match). */
- NXT_SET_FLOW_FORMAT = 12, /* Set flow format. */
- NXT_FLOW_MOD = 13, /* Analogous to OFPT_FLOW_MOD. */
- NXT_FLOW_REMOVED = 14, /* Analogous to OFPT_FLOW_REMOVED. */
-
- /* Use the upper 8 bits of the 'command' member in struct ofp_flow_mod to
- * designate the table to which a flow is to be added? See the big comment
- * on struct nx_flow_mod_table_id for more information. */
- NXT_FLOW_MOD_TABLE_ID = 15,
-
- /* Alternative PACKET_IN message formats. */
- NXT_SET_PACKET_IN_FORMAT = 16, /* Set Packet In format. */
- NXT_PACKET_IN = 17, /* Nicira Packet In. */
-
- /* Are the idle_age and hard_age members in struct nx_flow_stats supported?
- * If so, the switch does not reply to this message (which consists only of
- * a "struct nicira_header"). If not, the switch sends an error reply. */
- NXT_FLOW_AGE = 18,
-
- NXT_SET_ASYNC_CONFIG = 19, /* struct nx_async_config. */
- NXT_SET_CONTROLLER_ID = 20, /* struct nx_controller_id. */
-};
-
-/* Header for Nicira vendor stats request and reply messages. */
-struct nicira_stats_msg {
- struct ofp_vendor_stats_msg vsm; /* Vendor NX_VENDOR_ID. */
+/* Header for Nicira vendor stats request and reply messages in OpenFlow
+ * 1.0. */
+struct nicira10_stats_msg {
+ struct ofp10_vendor_stats_msg vsm; /* Vendor NX_VENDOR_ID. */
ovs_be32 subtype; /* One of NXST_* below. */
uint8_t pad[4]; /* Align to 64-bits. */
};
-OFP_ASSERT(sizeof(struct nicira_stats_msg) == 24);
+OFP_ASSERT(sizeof(struct nicira10_stats_msg) == 24);
-/* Values for the 'subtype' member of struct nicira_stats_msg. */
-enum nicira_stats_type {
- /* Flexible flow specification (aka NXM = Nicira Extended Match). */
- NXST_FLOW, /* Analogous to OFPST_FLOW. */
- NXST_AGGREGATE /* Analogous to OFPST_AGGREGATE. */
+/* Header for Nicira vendor stats request and reply messages in OpenFlow
+ * 1.1. */
+struct nicira11_stats_msg {
+ struct ofp11_vendor_stats_msg vsm; /* Vendor NX_VENDOR_ID. */
+ ovs_be32 subtype; /* One of NXST_* below. */
};
+OFP_ASSERT(sizeof(struct nicira11_stats_msg) == 24);
/* Fields to use when hashing flows. */
enum nx_hash_fields {
* instead of having the switch decide which table is most appropriate as
* required by OpenFlow 1.0. By default, the extension is disabled.
*
- * When this feature is enabled, Open vSwitch treats struct ofp_flow_mod's
+ * When this feature is enabled, Open vSwitch treats struct ofp10_flow_mod's
* 16-bit 'command' member as two separate fields. The upper 8 bits are used
* as the table ID, the lower 8 bits specify the command as usual. A table ID
* of 0xff is treated like a wildcarded table ID.
* table match, then none is modified or deleted.
*/
struct nx_flow_mod_table_id {
- struct ofp_header header;
- ovs_be32 vendor; /* NX_VENDOR_ID. */
- ovs_be32 subtype; /* NXT_FLOW_MOD_TABLE_ID. */
uint8_t set; /* Nonzero to enable, zero to disable. */
uint8_t pad[7];
};
-OFP_ASSERT(sizeof(struct nx_flow_mod_table_id) == 24);
+OFP_ASSERT(sizeof(struct nx_flow_mod_table_id) == 8);
enum nx_packet_in_format {
NXPIF_OPENFLOW10 = 0, /* Standard OpenFlow 1.0 compatible. */
/* NXT_SET_PACKET_IN_FORMAT request. */
struct nx_set_packet_in_format {
- struct nicira_header nxh;
ovs_be32 format; /* One of NXPIF_*. */
};
-OFP_ASSERT(sizeof(struct nx_set_packet_in_format) == 20);
+OFP_ASSERT(sizeof(struct nx_set_packet_in_format) == 4);
/* NXT_PACKET_IN (analogous to OFPT_PACKET_IN).
*
- * The NXT_PACKET_IN format is intended to model the OpenFlow-1.2 PACKET_IN
- * with some minor tweaks. Most notably NXT_PACKET_IN includes the cookie of
- * the rule which triggered the NXT_PACKET_IN message, and the match fields are
- * in NXM format.
+ * NXT_PACKET_IN is similar to the OpenFlow 1.2 OFPT_PACKET_IN. The
+ * differences are:
+ *
+ * - NXT_PACKET_IN includes the cookie of the rule that triggered the
+ * message. (OpenFlow 1.3 OFPT_PACKET_IN also includes the cookie.)
+ *
+ * - The metadata fields use NXM (instead of OXM) field numbers.
+ *
+ * Open vSwitch 1.9.0 and later omits metadata fields that are zero (as allowed
+ * by OpenFlow 1.2). Earlier versions included all implemented metadata
+ * fields.
+ *
+ * Open vSwitch does not include non-metadata in the nx_match, because by
+ * definition that information can be found in the packet itself. The format
+ * and the standards allow this, however, so controllers should be prepared to
+ * tolerate future changes.
*
- * The match fields in the NXT_PACKET_IN are intended to contain flow
- * processing metadata collected at the time the NXT_PACKET_IN message was
- * triggered. It is minimally required to contain the NXM_OF_IN_PORT of the
- * packet, but may include other NXM headers such as flow registers. The match
- * fields are allowed to contain non-metadata (e.g. NXM_OF_ETH_SRC etc).
- * However, this information can typically be found in the packet directly, so
- * it may be redundant.
+ * The NXM format is convenient for reporting metadata values, but it is
+ * important not to interpret the format as matching against a flow, because it
+ * does not. Nothing is being matched; arbitrary metadata masks would not be
+ * meaningful.
*
* Whereas in most cases a controller can expect to only get back NXM fields
* that it set up itself (e.g. flow dumps will ordinarily report only NXM
* fields from flows that the controller added), NXT_PACKET_IN messages might
* contain fields that the controller does not understand, because the switch
* might support fields (new registers, new protocols, etc.) that the
- * controller does not. Â The controller must prepared to tolerate these.
+ * controller does not. The controller must prepared to tolerate these.
*
* The 'cookie' and 'table_id' fields have no meaning when 'reason' is
* OFPR_NO_MATCH. In this case they should be set to 0. */
struct nx_packet_in {
- struct nicira_header nxh;
ovs_be32 buffer_id; /* ID assigned by datapath. */
ovs_be16 total_len; /* Full length of frame. */
uint8_t reason; /* Reason packet is sent (one of OFPR_*). */
* The padding bytes preceding the Ethernet frame ensure that the IP
* header (if any) following the Ethernet header is 32-bit aligned. */
- /* uint8_t nxm_fields[...]; */ /* Match. */
+ /* uint8_t nxm_fields[...]; */ /* NXM headers. */
/* uint8_t pad[2]; */ /* Align to 64 bit + 16 bit. */
/* uint8_t data[0]; */ /* Ethernet frame. */
};
-OFP_ASSERT(sizeof(struct nx_packet_in) == 40);
+OFP_ASSERT(sizeof(struct nx_packet_in) == 24);
/* Configures the "role" of the sending controller. The default role is:
*
* messages, but they do receive OFPT_PORT_STATUS messages.
*/
struct nx_role_request {
- struct nicira_header nxh;
ovs_be32 role; /* One of NX_ROLE_*. */
};
-OFP_ASSERT(sizeof(struct nx_role_request) == 20);
+OFP_ASSERT(sizeof(struct nx_role_request) == 4);
enum nx_role {
NX_ROLE_OTHER, /* Default role, full access. */
* with value 1<<2 == 4 in port_status_mask[1] determines whether the
* controller will receive OFPT_PORT_STATUS messages with reason OFPPR_MODIFY
* (value 2) when the controller has a "slave" role.
+ *
+ * As a side effect, for service controllers, this message changes the
+ * miss_send_len from default of zero to OFP_DEFAULT_MISS_SEND_LEN (128).
*/
struct nx_async_config {
- struct nicira_header nxh;
ovs_be32 packet_in_mask[2]; /* Bitmasks of OFPR_* values. */
ovs_be32 port_status_mask[2]; /* Bitmasks of OFPRR_* values. */
ovs_be32 flow_removed_mask[2]; /* Bitmasks of OFPPR_* values. */
};
-OFP_ASSERT(sizeof(struct nx_async_config) == 40);
+OFP_ASSERT(sizeof(struct nx_async_config) == 24);
\f
/* Nicira vendor flow actions. */
NXAST_NOTE, /* struct nx_action_note */
NXAST_SET_TUNNEL64, /* struct nx_action_set_tunnel64 */
NXAST_MULTIPATH, /* struct nx_action_multipath */
- NXAST_AUTOPATH, /* struct nx_action_autopath */
+ NXAST_AUTOPATH__DEPRECATED, /* struct nx_action_autopath */
NXAST_BUNDLE, /* struct nx_action_bundle */
NXAST_BUNDLE_LOAD, /* struct nx_action_bundle */
NXAST_RESUBMIT_TABLE, /* struct nx_action_resubmit */
NXAST_DEC_TTL, /* struct nx_action_header */
NXAST_FIN_TIMEOUT, /* struct nx_action_fin_timeout */
NXAST_CONTROLLER, /* struct nx_action_controller */
+ NXAST_DEC_TTL_CNT_IDS, /* struct nx_action_cnt_ids */
};
/* Header for Nicira-defined actions. */
* Sets the encapsulating tunnel ID to a 64-bit value. */
struct nx_action_set_tunnel64 {
ovs_be16 type; /* OFPAT_VENDOR. */
- ovs_be16 len; /* Length is 16. */
+ ovs_be16 len; /* Length is 24. */
ovs_be32 vendor; /* NX_VENDOR_ID. */
ovs_be16 subtype; /* NXAST_SET_TUNNEL64. */
uint8_t pad[6];
*/
struct nx_action_reg_move {
ovs_be16 type; /* OFPAT_VENDOR. */
- ovs_be16 len; /* Length is 16. */
+ ovs_be16 len; /* Length is 24. */
ovs_be32 vendor; /* NX_VENDOR_ID. */
ovs_be16 subtype; /* NXAST_REG_MOVE. */
ovs_be16 n_bits; /* Number of bits. */
*/
struct nx_action_reg_load {
ovs_be16 type; /* OFPAT_VENDOR. */
- ovs_be16 len; /* Length is 16. */
+ ovs_be16 len; /* Length is 24. */
ovs_be32 vendor; /* NX_VENDOR_ID. */
ovs_be16 subtype; /* NXAST_REG_LOAD. */
ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */
*/
struct nx_action_autopath {
ovs_be16 type; /* OFPAT_VENDOR. */
- ovs_be16 len; /* Length is 20. */
+ ovs_be16 len; /* Length is 24. */
ovs_be32 vendor; /* NX_VENDOR_ID. */
ovs_be16 subtype; /* NXAST_AUTOPATH. */
* Uses the 'fields' and 'basis' parameters. */
NX_BD_ALG_HRW /* Highest Random Weight. */
};
+
+\f
+/* Action structure for NXAST_DEC_TTL_CNT_IDS.
+ *
+ * If the packet is not IPv4 or IPv6, does nothing. For IPv4 or IPv6, if the
+ * TTL or hop limit is at least 2, decrements it by 1. Otherwise, if TTL or
+ * hop limit is 0 or 1, sends a packet-in to the controllers with each of the
+ * 'n_controllers' controller IDs specified in 'cnt_ids'.
+ *
+ * (This differs from NXAST_DEC_TTL in that for NXAST_DEC_TTL the packet-in is
+ * sent only to controllers with id 0.)
+ */
+struct nx_action_cnt_ids {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length including slaves. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_DEC_TTL_CNT_IDS. */
+
+ ovs_be16 n_controllers; /* Number of controllers. */
+ uint8_t zeros[4]; /* Must be zero. */
+
+ /* Followed by 1 or more controller ids.
+ *
+ * uint16_t cnt_ids[]; // Controller ids.
+ * uint8_t pad[]; // Must be 0 to 8-byte align cnt_ids[].
+ */
+};
+OFP_ASSERT(sizeof(struct nx_action_cnt_ids) == 16);
+
\f
/* Action structure for NXAST_OUTPUT_REG.
*
\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
* 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.
*
*
* 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.
*
*
* 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)
*
* 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)
*
* 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)
*
* 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)
enum nx_flow_format {
NXFF_OPENFLOW10 = 0, /* Standard OpenFlow 1.0 compatible. */
- NXFF_NXM = 2 /* Nicira extended match. */
+ NXFF_NXM = 2, /* Nicira extended match. */
+ NXFF_OPENFLOW12 = 3 /* OpenFlow 1.2 format. */
};
/* NXT_SET_FLOW_FORMAT request. */
struct nx_set_flow_format {
- struct nicira_header nxh;
ovs_be32 format; /* One of NXFF_*. */
};
-OFP_ASSERT(sizeof(struct nx_set_flow_format) == 20);
+OFP_ASSERT(sizeof(struct nx_set_flow_format) == 4);
/* NXT_FLOW_MOD (analogous to OFPT_FLOW_MOD).
*
* It is possible to limit flow deletions and modifications to certain
- * cookies by using the NXM_NX_COOKIE and NXM_NX_COOKIE_W matches. For
- * these commands, the "cookie" field is always ignored. Flow additions
- * make use of the "cookie" field and ignore any NXM_NX_COOKIE*
- * definitions.
+ * cookies by using the NXM_NX_COOKIE(_W) matches. The "cookie" field
+ * is used only to add or modify flow cookies.
*/
struct nx_flow_mod {
- struct nicira_header nxh;
ovs_be64 cookie; /* Opaque controller-issued identifier. */
ovs_be16 command; /* One of OFPFC_*. */
ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */
* multiple of 8).
*/
};
-OFP_ASSERT(sizeof(struct nx_flow_mod) == 48);
+OFP_ASSERT(sizeof(struct nx_flow_mod) == 32);
/* NXT_FLOW_REMOVED (analogous to OFPT_FLOW_REMOVED). */
struct nx_flow_removed {
- struct nicira_header nxh;
ovs_be64 cookie; /* Opaque controller-issued identifier. */
ovs_be16 priority; /* Priority level of flow entry. */
uint8_t reason; /* One of OFPRR_*. */
* - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of
* all-zero bytes. */
};
-OFP_ASSERT(sizeof(struct nx_flow_removed) == 56);
+OFP_ASSERT(sizeof(struct nx_flow_removed) == 40);
/* Nicira vendor stats request of type NXST_FLOW (analogous to OFPST_FLOW
* request).
* NXM_NX_COOKIE and NXM_NX_COOKIE_W matches.
*/
struct nx_flow_stats_request {
- struct nicira_stats_msg nsm;
ovs_be16 out_port; /* Require matching entries to include this
as an output port. A value of OFPP_NONE
indicates no restriction. */
* message.
*/
};
-OFP_ASSERT(sizeof(struct nx_flow_stats_request) == 32);
+OFP_ASSERT(sizeof(struct nx_flow_stats_request) == 8);
/* Body for Nicira vendor stats reply of type NXST_FLOW (analogous to
* OFPST_FLOW reply).
ovs_be32 duration_sec; /* Time flow has been alive in seconds. */
ovs_be32 duration_nsec; /* Time flow has been alive in nanoseconds
beyond duration_sec. */
- ovs_be16 priority; /* Priority of the entry. Only meaningful
- when this is not an exact-match entry. */
+ ovs_be16 priority; /* Priority of the entry. */
ovs_be16 idle_timeout; /* Number of seconds idle before expiration. */
ovs_be16 hard_timeout; /* Number of seconds before expiration. */
ovs_be16 match_len; /* Length of nx_match. */
OFP_ASSERT(sizeof(struct nx_flow_stats) == 48);
/* Nicira vendor stats request of type NXST_AGGREGATE (analogous to
- * OFPST_AGGREGATE request). */
+ * OFPST_AGGREGATE request).
+ *
+ * The reply format is identical to the reply format for OFPST_AGGREGATE,
+ * except for the header. */
struct nx_aggregate_stats_request {
- struct nicira_stats_msg nsm;
ovs_be16 out_port; /* Require matching entries to include this
as an output port. A value of OFPP_NONE
indicates no restriction. */
* message.
*/
};
-OFP_ASSERT(sizeof(struct nx_aggregate_stats_request) == 32);
-
-/* Body for nicira_stats_msg reply of type NXST_AGGREGATE (analogous to
- * OFPST_AGGREGATE reply). */
-struct nx_aggregate_stats_reply {
- struct nicira_stats_msg nsm;
- ovs_be64 packet_count; /* Number of packets, UINT64_MAX if unknown. */
- ovs_be64 byte_count; /* Number of bytes, UINT64_MAX if unknown. */
- ovs_be32 flow_count; /* Number of flows. */
- uint8_t pad[4]; /* Align to 64 bits. */
-};
-OFP_ASSERT(sizeof(struct nx_aggregate_stats_reply) == 48);
+OFP_ASSERT(sizeof(struct nx_aggregate_stats_request) == 8);
\f
/* NXT_SET_CONTROLLER_ID.
*
* The NXAST_CONTROLLER action is the only current user of controller
* connection IDs. */
struct nx_controller_id {
- struct nicira_header nxh;
uint8_t zero[6]; /* Must be zero. */
ovs_be16 controller_id; /* New controller connection ID. */
};
-OFP_ASSERT(sizeof(struct nx_controller_id) == 24);
+OFP_ASSERT(sizeof(struct nx_controller_id) == 8);
/* Action structure for NXAST_CONTROLLER.
*
uint8_t zero; /* Must be zero. */
};
OFP_ASSERT(sizeof(struct nx_action_controller) == 16);
+\f
+/* Flow Table Monitoring
+ * =====================
+ *
+ * NXST_FLOW_MONITOR allows a controller to keep track of changes to OpenFlow
+ * flow table(s) or subsets of them, with the following workflow:
+ *
+ * 1. The controller sends an NXST_FLOW_MONITOR request to begin monitoring
+ * flows. The 'id' in the request must be unique among all monitors that
+ * the controller has started and not yet canceled on this OpenFlow
+ * connection.
+ *
+ * 2. The switch responds with an NXST_FLOW_MONITOR reply. If the request's
+ * 'flags' included NXFMF_INITIAL, the reply includes all the flows that
+ * matched the request at the time of the request (with event NXFME_ADDED).
+ * If 'flags' did not include NXFMF_INITIAL, the reply is empty.
+ *
+ * The reply uses the xid of the request (as do all replies to OpenFlow
+ * requests).
+ *
+ * 3. Whenever a change to a flow table entry matches some outstanding monitor
+ * request's criteria and flags, the switch sends a notification to the
+ * controller as an additional NXST_FLOW_MONITOR reply with xid 0.
+ *
+ * When multiple outstanding monitors match a single change, only a single
+ * notification is sent. This merged notification includes the information
+ * requested in any of the individual monitors. That is, if any of the
+ * matching monitors requests actions (NXFMF_ACTIONS), the notification
+ * includes actions, and if any of the monitors request full changes for the
+ * controller's own changes (NXFMF_OWN), the controller's own changes will
+ * be included in full.
+ *
+ * 4. The controller may cancel a monitor with NXT_FLOW_MONITOR_CANCEL. No
+ * further notifications will be sent on the basis of the canceled monitor
+ * afterward.
+ *
+ *
+ * Buffer Management
+ * =================
+ *
+ * OpenFlow messages for flow monitor notifications can overflow the buffer
+ * space available to the switch, either temporarily (e.g. due to network
+ * conditions slowing OpenFlow traffic) or more permanently (e.g. the sustained
+ * rate of flow table change exceeds the network bandwidth between switch and
+ * controller).
+ *
+ * When Open vSwitch's notification buffer space reaches a limiting threshold,
+ * OVS reacts as follows:
+ *
+ * 1. OVS sends an NXT_FLOW_MONITOR_PAUSED message to the controller, following
+ * all the already queued notifications. After it receives this message,
+ * the controller knows that its view of the flow table, as represented by
+ * flow monitor notifications, is incomplete.
+ *
+ * 2. As long as the notification buffer is not empty:
+ *
+ * - NXMFE_ADD and NXFME_MODIFIED notifications will not be sent.
+ *
+ * - NXFME_DELETED notifications will still be sent, but only for flows
+ * that existed before OVS sent NXT_FLOW_MONITOR_PAUSED.
+ *
+ * - NXFME_ABBREV notifications will not be sent. They are treated as
+ * the expanded version (and therefore only the NXFME_DELETED
+ * components, if any, are sent).
+ *
+ * 3. When the notification buffer empties, OVS sends NXFME_ADD notifications
+ * for flows added since the buffer reached its limit and NXFME_MODIFIED
+ * notifications for flows that existed before the limit was reached and
+ * changed after the limit was reached.
+ *
+ * 4. OVS sends an NXT_FLOW_MONITOR_RESUMED message to the controller. After
+ * it receives this message, the controller knows that its view of the flow
+ * table, as represented by flow monitor notifications, is again complete.
+ *
+ * This allows the maximum buffer space requirement for notifications to be
+ * bounded by the limit plus the maximum number of supported flows.
+ *
+ *
+ * "Flow Removed" messages
+ * =======================
+ *
+ * The flow monitor mechanism is independent of OFPT_FLOW_REMOVED and
+ * NXT_FLOW_REMOVED. Flow monitor updates for deletion are sent if
+ * NXFMF_DELETE is set on a monitor, regardless of whether the
+ * OFPFF_SEND_FLOW_REM flag was set when the flow was added. */
+
+/* NXST_FLOW_MONITOR request.
+ *
+ * The NXST_FLOW_MONITOR request's body consists of an array of zero or more
+ * instances of this structure. The request arranges to monitor the flows
+ * that match the specified criteria, which are interpreted in the same way as
+ * for NXST_FLOW.
+ *
+ * 'id' identifies a particular monitor for the purpose of allowing it to be
+ * canceled later with NXT_FLOW_MONITOR_CANCEL. 'id' must be unique among
+ * existing monitors that have not already been canceled.
+ *
+ * The reply includes the initial flow matches for monitors that have the
+ * NXFMF_INITIAL flag set. No single flow will be included in the reply more
+ * than once, even if more than one requested monitor matches that flow. The
+ * reply will be empty if none of the monitors has NXFMF_INITIAL set or if none
+ * of the monitors initially matches any flows.
+ *
+ * For NXFMF_ADD, an event will be reported if 'out_port' matches against the
+ * actions of the flow being added or, for a flow that is replacing an existing
+ * flow, if 'out_port' matches against the actions of the flow being replaced.
+ * For NXFMF_DELETE, 'out_port' matches against the actions of a flow being
+ * deleted. For NXFMF_MODIFY, an event will be reported if 'out_port' matches
+ * either the old or the new actions. */
+struct nx_flow_monitor_request {
+ ovs_be32 id; /* Controller-assigned ID for this monitor. */
+ ovs_be16 flags; /* NXFMF_*. */
+ ovs_be16 out_port; /* Required output port, if not OFPP_NONE. */
+ ovs_be16 match_len; /* Length of nx_match. */
+ uint8_t table_id; /* One table's ID or 0xff for all tables. */
+ uint8_t zeros[5]; /* Align to 64 bits (must be zero). */
+ /* Followed by:
+ * - Exactly match_len (possibly 0) bytes containing the nx_match, then
+ * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of
+ * all-zero bytes. */
+};
+OFP_ASSERT(sizeof(struct nx_flow_monitor_request) == 16);
+
+/* 'flags' bits in struct nx_flow_monitor_request. */
+enum nx_flow_monitor_flags {
+ /* When to send updates. */
+ NXFMF_INITIAL = 1 << 0, /* Initially matching flows. */
+ NXFMF_ADD = 1 << 1, /* New matching flows as they are added. */
+ NXFMF_DELETE = 1 << 2, /* Old matching flows as they are removed. */
+ NXFMF_MODIFY = 1 << 3, /* Matching flows as they are changed. */
+
+ /* What to include in updates. */
+ NXFMF_ACTIONS = 1 << 4, /* If set, actions are included. */
+ NXFMF_OWN = 1 << 5, /* If set, include own changes in full. */
+};
+
+/* NXST_FLOW_MONITOR reply header.
+ *
+ * The body of an NXST_FLOW_MONITOR reply is an array of variable-length
+ * structures, each of which begins with this header. The 'length' member may
+ * be used to traverse the array, and the 'event' member may be used to
+ * determine the particular structure.
+ *
+ * Every instance is a multiple of 8 bytes long. */
+struct nx_flow_update_header {
+ ovs_be16 length; /* Length of this entry. */
+ ovs_be16 event; /* One of NXFME_*. */
+ /* ...other data depending on 'event'... */
+};
+OFP_ASSERT(sizeof(struct nx_flow_update_header) == 4);
+
+/* 'event' values in struct nx_flow_update_header. */
+enum nx_flow_update_event {
+ /* struct nx_flow_update_full. */
+ NXFME_ADDED = 0, /* Flow was added. */
+ NXFME_DELETED = 1, /* Flow was deleted. */
+ NXFME_MODIFIED = 2, /* Flow (generally its actions) was changed. */
+
+ /* struct nx_flow_update_abbrev. */
+ NXFME_ABBREV = 3, /* Abbreviated reply. */
+};
+
+/* NXST_FLOW_MONITOR reply for NXFME_ADDED, NXFME_DELETED, and
+ * NXFME_MODIFIED. */
+struct nx_flow_update_full {
+ ovs_be16 length; /* Length is 24. */
+ ovs_be16 event; /* One of NXFME_*. */
+ ovs_be16 reason; /* OFPRR_* for NXFME_DELETED, else zero. */
+ ovs_be16 priority; /* Priority of the entry. */
+ ovs_be16 idle_timeout; /* Number of seconds idle before expiration. */
+ ovs_be16 hard_timeout; /* Number of seconds before expiration. */
+ ovs_be16 match_len; /* Length of nx_match. */
+ uint8_t table_id; /* ID of flow's table. */
+ uint8_t pad; /* Reserved, currently zeroed. */
+ ovs_be64 cookie; /* Opaque controller-issued identifier. */
+ /* Followed by:
+ * - Exactly match_len (possibly 0) bytes containing the nx_match, then
+ * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of
+ * all-zero bytes, then
+ * - Actions to fill out the remainder 'length' bytes (always a multiple
+ * of 8). If NXFMF_ACTIONS was not specified, or 'event' is
+ * NXFME_DELETED, no actions are included.
+ */
+};
+OFP_ASSERT(sizeof(struct nx_flow_update_full) == 24);
+
+/* NXST_FLOW_MONITOR reply for NXFME_ABBREV.
+ *
+ * When the controller does not specify NXFMF_OWN in a monitor request, any
+ * flow tables changes due to the controller's own requests (on the same
+ * OpenFlow channel) will be abbreviated, when possible, to this form, which
+ * simply specifies the 'xid' of the OpenFlow request (e.g. an OFPT_FLOW_MOD or
+ * NXT_FLOW_MOD) that caused the change.
+ *
+ * Some changes cannot be abbreviated and will be sent in full:
+ *
+ * - Changes that only partially succeed. This can happen if, for example,
+ * a flow_mod with type OFPFC_MODIFY affects multiple flows, but only some
+ * of those modifications succeed (e.g. due to hardware limitations).
+ *
+ * This cannot occur with the current implementation of the Open vSwitch
+ * software datapath. It could happen with other datapath implementations.
+ *
+ * - Changes that race with conflicting changes made by other controllers or
+ * other flow_mods (not separated by barriers) by the same controller.
+ *
+ * This cannot occur with the current Open vSwitch implementation
+ * (regardless of datapath) because Open vSwitch internally serializes
+ * potentially conflicting changes.
+ *
+ * A flow_mod that does not change the flow table will not trigger any
+ * notification, even an abbreviated one. For example, a "modify" or "delete"
+ * flow_mod that does not match any flows will not trigger a notification.
+ * Whether an "add" or "modify" that specifies all the same parameters that a
+ * flow already has triggers a notification is unspecified and subject to
+ * change in future versions of Open vSwitch.
+ *
+ * OVS will always send the notifications for a given flow table change before
+ * the reply to a OFPT_BARRIER_REQUEST request that precedes the flow table
+ * change. Thus, if the controller does not receive an abbreviated
+ * notification for a flow_mod before the next OFPT_BARRIER_REPLY, it will
+ * never receive one. */
+struct nx_flow_update_abbrev {
+ ovs_be16 length; /* Length is 8. */
+ ovs_be16 event; /* NXFME_ABBREV. */
+ ovs_be32 xid; /* Controller-specified xid from flow_mod. */
+};
+OFP_ASSERT(sizeof(struct nx_flow_update_abbrev) == 8);
+
+/* NXT_FLOW_MONITOR_CANCEL.
+ *
+ * Used by a controller to cancel an outstanding monitor. */
+struct nx_flow_monitor_cancel {
+ ovs_be32 id; /* 'id' from nx_flow_monitor_request. */
+};
+OFP_ASSERT(sizeof(struct nx_flow_monitor_cancel) == 4);
#endif /* openflow/nicira-ext.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
OFPP_NONE = 0xffff /* Not associated with a physical port. */
};
-/* OpenFlow 1.0 specific message types, in addition to the common message
- * types. */
-enum ofp10_type {
- /* Controller command messages. */
- OFPT10_PACKET_OUT = 13, /* Controller/switch message */
- OFPT10_FLOW_MOD, /* Controller/switch message */
- OFPT10_PORT_MOD, /* Controller/switch message */
-
- /* Statistics messages. */
- OFPT10_STATS_REQUEST, /* Controller/switch message */
- OFPT10_STATS_REPLY, /* Controller/switch message */
-
- /* Barrier messages. */
- OFPT10_BARRIER_REQUEST, /* Controller/switch message */
- OFPT10_BARRIER_REPLY, /* Controller/switch message */
-
- /* Queue Configuration messages. */
- OFPT10_QUEUE_GET_CONFIG_REQUEST, /* Controller/switch message */
- OFPT10_QUEUE_GET_CONFIG_REPLY /* Controller/switch message */
-};
-
-/* OFPT_HELLO. This message has an empty body, but implementations must
- * ignore any data included in the body, to allow for future extensions. */
-struct ofp_hello {
- struct ofp_header header;
-};
-
#define OFP_DEFAULT_MISS_SEND_LEN 128
enum ofp_config_flags {
/* Switch configuration. */
struct ofp_switch_config {
- struct ofp_header header;
ovs_be16 flags; /* OFPC_* flags. */
ovs_be16 miss_send_len; /* Max bytes of new flow that datapath should
send to the controller. */
};
-OFP_ASSERT(sizeof(struct ofp_switch_config) == 12);
+OFP_ASSERT(sizeof(struct ofp_switch_config) == 4);
/* OpenFlow 1.0 specific capabilities supported by the datapath (struct
* ofp_switch_features, member capabilities). */
/* Modify behavior of the physical port */
struct ofp10_port_mod {
- struct ofp_header header;
ovs_be16 port_no;
uint8_t hw_addr[OFP_ETH_ALEN]; /* The hardware address is not
configurable. This is used to
bits to prevent any action taking place. */
uint8_t pad[4]; /* Pad to 64-bits. */
};
-OFP_ASSERT(sizeof(struct ofp10_port_mod) == 32);
+OFP_ASSERT(sizeof(struct ofp10_port_mod) == 24);
/* Query for port queue configuration. */
struct ofp10_queue_get_config_request {
- struct ofp_header header;
ovs_be16 port; /* Port to be queried. Should refer
to a valid physical port (i.e. < OFPP_MAX) */
uint8_t pad[2];
/* 32-bit alignment. */
};
-OFP_ASSERT(sizeof(struct ofp10_queue_get_config_request) == 12);
+OFP_ASSERT(sizeof(struct ofp10_queue_get_config_request) == 4);
/* Queue configuration for a given port. */
struct ofp10_queue_get_config_reply {
- struct ofp_header header;
ovs_be16 port;
uint8_t pad[6];
/* struct ofp10_packet_queue queues[0]; List of configured queues. */
};
-OFP_ASSERT(sizeof(struct ofp10_queue_get_config_reply) == 16);
+OFP_ASSERT(sizeof(struct ofp10_queue_get_config_reply) == 8);
/* Packet received on port (datapath -> controller). */
struct ofp_packet_in {
- struct ofp_header header;
ovs_be32 buffer_id; /* ID assigned by datapath. */
ovs_be16 total_len; /* Full length of frame. */
ovs_be16 in_port; /* Port on which frame was received. */
offsetof(struct ofp_packet_in, data) ==
sizeof(struct ofp_packet_in) - 2. */
};
-OFP_ASSERT(sizeof(struct ofp_packet_in) == 20);
+OFP_ASSERT(sizeof(struct ofp_packet_in) == 12);
enum ofp10_action_type {
OFPAT10_OUTPUT, /* Output to switch port. */
* When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max
* number of bytes to send. A 'max_len' of zero means no bytes of the
* packet should be sent. */
-struct ofp_action_output {
+struct ofp10_action_output {
ovs_be16 type; /* OFPAT10_OUTPUT. */
ovs_be16 len; /* Length is 8. */
ovs_be16 port; /* Output port. */
ovs_be16 max_len; /* Max length to send to controller. */
};
-OFP_ASSERT(sizeof(struct ofp_action_output) == 8);
-
-/* The VLAN id is 12 bits, so we can use the entire 16 bits to indicate
- * special conditions. All ones is used to match that no VLAN id was
- * set. */
-#define OFP_VLAN_NONE 0xffff
-
-/* Action structure for OFPAT10_SET_VLAN_VID. */
-struct ofp_action_vlan_vid {
- ovs_be16 type; /* OFPAT10_SET_VLAN_VID. */
- ovs_be16 len; /* Length is 8. */
- ovs_be16 vlan_vid; /* VLAN id. */
- uint8_t pad[2];
-};
-OFP_ASSERT(sizeof(struct ofp_action_vlan_vid) == 8);
-
-/* Action structure for OFPAT10_SET_VLAN_PCP. */
-struct ofp_action_vlan_pcp {
- ovs_be16 type; /* OFPAT10_SET_VLAN_PCP. */
- ovs_be16 len; /* Length is 8. */
- uint8_t vlan_pcp; /* VLAN priority. */
- uint8_t pad[3];
-};
-OFP_ASSERT(sizeof(struct ofp_action_vlan_pcp) == 8);
-
-/* Action structure for OFPAT10_SET_DL_SRC/DST. */
-struct ofp_action_dl_addr {
- ovs_be16 type; /* OFPAT10_SET_DL_SRC/DST. */
- ovs_be16 len; /* Length is 16. */
- uint8_t dl_addr[OFP_ETH_ALEN]; /* Ethernet address. */
- uint8_t pad[6];
-};
-OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16);
-
-/* Action structure for OFPAT10_SET_NW_SRC/DST. */
-struct ofp_action_nw_addr {
- ovs_be16 type; /* OFPAT10_SET_TW_SRC/DST. */
- ovs_be16 len; /* Length is 8. */
- ovs_be32 nw_addr; /* IP address. */
-};
-OFP_ASSERT(sizeof(struct ofp_action_nw_addr) == 8);
-
-/* Action structure for OFPAT10_SET_NW_TOS. */
-struct ofp_action_nw_tos {
- ovs_be16 type; /* OFPAT10_SET_TW_TOS. */
- ovs_be16 len; /* Length is 8. */
- uint8_t nw_tos; /* IP TOS (DSCP field, 6 bits). */
- uint8_t pad[3];
-};
-OFP_ASSERT(sizeof(struct ofp_action_nw_tos) == 8);
-
-/* Action structure for OFPAT10_SET_TP_SRC/DST. */
-struct ofp_action_tp_port {
- ovs_be16 type; /* OFPAT10_SET_TP_SRC/DST. */
- ovs_be16 len; /* Length is 8. */
- ovs_be16 tp_port; /* TCP/UDP port. */
- uint8_t pad[2];
-};
-OFP_ASSERT(sizeof(struct ofp_action_tp_port) == 8);
+OFP_ASSERT(sizeof(struct ofp10_action_output) == 8);
/* Action header for OFPAT10_VENDOR. The rest of the body is vendor-defined. */
struct ofp_action_vendor_header {
ovs_be16 type;
struct ofp_action_header header;
struct ofp_action_vendor_header vendor;
- struct ofp_action_output output;
+ struct ofp10_action_output output10;
struct ofp_action_vlan_vid vlan_vid;
struct ofp_action_vlan_pcp vlan_pcp;
struct ofp_action_nw_addr nw_addr;
/* Send packet (controller -> datapath). */
struct ofp_packet_out {
- struct ofp_header header;
ovs_be32 buffer_id; /* ID assigned by datapath or UINT32_MAX. */
ovs_be16 in_port; /* Packet's input port (OFPP_NONE if none). */
ovs_be16 actions_len; /* Size of action array in bytes. */
* of the message length.
*/
};
-OFP_ASSERT(sizeof(struct ofp_packet_out) == 16);
-
-enum ofp_flow_mod_command {
- OFPFC_ADD, /* New flow. */
- OFPFC_MODIFY, /* Modify all matching flows. */
- OFPFC_MODIFY_STRICT, /* Modify entry strictly matching wildcards */
- OFPFC_DELETE, /* Delete all matching flows. */
- OFPFC_DELETE_STRICT /* Strictly match wildcards and priority. */
-};
+OFP_ASSERT(sizeof(struct ofp_packet_out) == 8);
/* Flow wildcards. */
enum ofp_flow_wildcards {
- OFPFW_IN_PORT = 1 << 0, /* Switch input port. */
- OFPFW_DL_VLAN = 1 << 1, /* VLAN vid. */
- OFPFW_DL_SRC = 1 << 2, /* Ethernet source address. */
- OFPFW_DL_DST = 1 << 3, /* Ethernet destination address. */
- OFPFW_DL_TYPE = 1 << 4, /* Ethernet frame type. */
- OFPFW_NW_PROTO = 1 << 5, /* IP protocol. */
- OFPFW_TP_SRC = 1 << 6, /* TCP/UDP source port. */
- OFPFW_TP_DST = 1 << 7, /* TCP/UDP destination port. */
+ OFPFW10_IN_PORT = 1 << 0, /* Switch input port. */
+ OFPFW10_DL_VLAN = 1 << 1, /* VLAN vid. */
+ OFPFW10_DL_SRC = 1 << 2, /* Ethernet source address. */
+ OFPFW10_DL_DST = 1 << 3, /* Ethernet destination address. */
+ OFPFW10_DL_TYPE = 1 << 4, /* Ethernet frame type. */
+ OFPFW10_NW_PROTO = 1 << 5, /* IP protocol. */
+ OFPFW10_TP_SRC = 1 << 6, /* TCP/UDP source port. */
+ OFPFW10_TP_DST = 1 << 7, /* TCP/UDP destination port. */
/* IP source address wildcard bit count. 0 is exact match, 1 ignores the
* LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard
* the entire field. This is the *opposite* of the usual convention where
* e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded. */
- OFPFW_NW_SRC_SHIFT = 8,
- OFPFW_NW_SRC_BITS = 6,
- OFPFW_NW_SRC_MASK = ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT,
- OFPFW_NW_SRC_ALL = 32 << OFPFW_NW_SRC_SHIFT,
+ OFPFW10_NW_SRC_SHIFT = 8,
+ OFPFW10_NW_SRC_BITS = 6,
+ OFPFW10_NW_SRC_MASK = (((1 << OFPFW10_NW_SRC_BITS) - 1)
+ << OFPFW10_NW_SRC_SHIFT),
+ OFPFW10_NW_SRC_ALL = 32 << OFPFW10_NW_SRC_SHIFT,
/* IP destination address wildcard bit count. Same format as source. */
- OFPFW_NW_DST_SHIFT = 14,
- OFPFW_NW_DST_BITS = 6,
- OFPFW_NW_DST_MASK = ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT,
- OFPFW_NW_DST_ALL = 32 << OFPFW_NW_DST_SHIFT,
+ OFPFW10_NW_DST_SHIFT = 14,
+ OFPFW10_NW_DST_BITS = 6,
+ OFPFW10_NW_DST_MASK = (((1 << OFPFW10_NW_DST_BITS) - 1)
+ << OFPFW10_NW_DST_SHIFT),
+ OFPFW10_NW_DST_ALL = 32 << OFPFW10_NW_DST_SHIFT,
- OFPFW_DL_VLAN_PCP = 1 << 20, /* VLAN priority. */
- OFPFW_NW_TOS = 1 << 21, /* IP ToS (DSCP field, 6 bits). */
+ OFPFW10_DL_VLAN_PCP = 1 << 20, /* VLAN priority. */
+ OFPFW10_NW_TOS = 1 << 21, /* IP ToS (DSCP field, 6 bits). */
/* Wildcard all fields. */
- OFPFW_ALL = ((1 << 22) - 1)
+ OFPFW10_ALL = ((1 << 22) - 1)
};
/* The wildcards for ICMP type and code fields use the transport source
* and destination port fields, respectively. */
-#define OFPFW_ICMP_TYPE OFPFW_TP_SRC
-#define OFPFW_ICMP_CODE OFPFW_TP_DST
+#define OFPFW10_ICMP_TYPE OFPFW10_TP_SRC
+#define OFPFW10_ICMP_CODE OFPFW10_TP_DST
/* Values below this cutoff are 802.3 packets and the two bytes
* following MAC addresses are used as a frame length. Otherwise, the
#define OFP_DL_TYPE_NOT_ETH_TYPE 0x05ff
/* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate
- * special conditions. All ones indicates that no VLAN id was set.
+ * special conditions. All ones indicates that 802.1Q header is not present.
*/
-#define OFP_VLAN_NONE 0xffff
+#define OFP10_VLAN_NONE 0xffff
/* Fields to match against flows */
-struct ofp_match {
+struct ofp10_match {
ovs_be32 wildcards; /* Wildcard fields. */
ovs_be16 in_port; /* Input switch port. */
uint8_t dl_src[OFP_ETH_ALEN]; /* Ethernet source address. */
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. */
/* By default, choose a priority in the middle. */
#define OFP_DEFAULT_PRIORITY 0x8000
-enum ofp_flow_mod_flags {
- OFPFF_SEND_FLOW_REM = 1 << 0, /* Send flow removed message when flow
- * expires or is deleted. */
- OFPFF_CHECK_OVERLAP = 1 << 1, /* Check for overlapping entries first. */
- OFPFF_EMERG = 1 << 2 /* Ramark this is for emergency. */
+enum ofp10_flow_mod_flags {
+ OFPFF10_EMERG = 1 << 2 /* Ramark this is for emergency. */
};
/* Flow setup and teardown (controller -> datapath). */
-struct ofp_flow_mod {
- struct ofp_header header;
- struct ofp_match match; /* Fields to match */
+struct ofp10_flow_mod {
+ struct ofp10_match match; /* Fields to match */
ovs_be64 cookie; /* Opaque controller-issued identifier. */
/* Flow actions. */
from the length field in the
header. */
};
-OFP_ASSERT(sizeof(struct ofp_flow_mod) == 72);
+OFP_ASSERT(sizeof(struct ofp10_flow_mod) == 64);
/* Flow removed (datapath -> controller). */
struct ofp_flow_removed {
- struct ofp_header header;
- struct ofp_match match; /* Description of fields. */
+ struct ofp10_match match; /* Description of fields. */
ovs_be64 cookie; /* Opaque controller-issued identifier. */
ovs_be16 priority; /* Priority level of flow entry. */
ovs_be64 packet_count;
ovs_be64 byte_count;
};
-OFP_ASSERT(sizeof(struct ofp_flow_removed) == 88);
+OFP_ASSERT(sizeof(struct ofp_flow_removed) == 80);
/* OFPT_ERROR: Error message (datapath -> controller). */
struct ofp_error_msg {
- struct ofp_header header;
-
ovs_be16 type;
ovs_be16 code;
uint8_t data[0]; /* Variable-length data. Interpreted based
on the type and code. */
};
-OFP_ASSERT(sizeof(struct ofp_error_msg) == 12);
-
-enum ofp_stats_types {
- /* Description of this OpenFlow switch.
- * The request is struct ofp_stats_msg.
- * The reply is struct ofp_desc_stats. */
- OFPST_DESC,
-
- /* Individual flow statistics.
- * The request is struct ofp_flow_stats_request.
- * The reply body is an array of struct ofp_flow_stats. */
- OFPST_FLOW,
-
- /* Aggregate flow statistics.
- * The request is struct ofp_flow_stats_request.
- * The reply is struct ofp_aggregate_stats_reply. */
- OFPST_AGGREGATE,
-
- /* Flow table statistics.
- * The request is struct ofp_stats_msg.
- * The reply body is an array of struct ofp_table_stats. */
- OFPST_TABLE,
-
- /* Physical port statistics.
- * The request is struct ofp_port_stats_request.
- * The reply body is an array of struct ofp_port_stats. */
- OFPST_PORT,
-
- /* Queue statistics for a port.
- * The request body is struct ofp_queue_stats_request.
- * The reply body is an array of struct ofp_queue_stats. */
- OFPST_QUEUE,
-
- /* Vendor extension.
- * The request and reply begin with "struct ofp_vendor_stats". */
- OFPST_VENDOR = 0xffff
-};
+OFP_ASSERT(sizeof(struct ofp_error_msg) == 4);
/* Statistics request or reply message. */
-struct ofp_stats_msg {
+struct ofp10_stats_msg {
struct ofp_header header;
ovs_be16 type; /* One of the OFPST_* constants. */
ovs_be16 flags; /* Requests: always 0.
* Replies: 0 or OFPSF_REPLY_MORE. */
};
-OFP_ASSERT(sizeof(struct ofp_stats_msg) == 12);
+OFP_ASSERT(sizeof(struct ofp10_stats_msg) == 12);
enum ofp_stats_reply_flags {
OFPSF_REPLY_MORE = 1 << 0 /* More replies to follow. */
};
-#define DESC_STR_LEN 256
-#define SERIAL_NUM_LEN 32
-/* Reply to OFPST_DESC request. Each entry is a NULL-terminated ASCII
- * string. */
-struct ofp_desc_stats {
- struct ofp_stats_msg osm;
- char mfr_desc[DESC_STR_LEN]; /* Manufacturer description. */
- char hw_desc[DESC_STR_LEN]; /* Hardware description. */
- char sw_desc[DESC_STR_LEN]; /* Software description. */
- char serial_num[SERIAL_NUM_LEN]; /* Serial number. */
- char dp_desc[DESC_STR_LEN]; /* Human readable description of
- the datapath. */
-};
-OFP_ASSERT(sizeof(struct ofp_desc_stats) == 1068);
-
/* Stats request of type OFPST_AGGREGATE or OFPST_FLOW. */
-struct ofp_flow_stats_request {
- struct ofp_stats_msg osm;
- struct ofp_match match; /* Fields to match. */
+struct ofp10_flow_stats_request {
+ struct ofp10_match match; /* Fields to match. */
uint8_t table_id; /* ID of table to read (from ofp_table_stats)
or 0xff for all tables. */
uint8_t pad; /* Align to 32 bits. */
as an output port. A value of OFPP_NONE
indicates no restriction. */
};
-OFP_ASSERT(sizeof(struct ofp_flow_stats_request) == 56);
+OFP_ASSERT(sizeof(struct ofp10_flow_stats_request) == 44);
/* Body of reply to OFPST_FLOW request. */
-struct ofp_flow_stats {
+struct ofp10_flow_stats {
ovs_be16 length; /* Length of this entry. */
uint8_t table_id; /* ID of table flow came from. */
uint8_t pad;
- struct ofp_match match; /* Description of fields. */
+ struct ofp10_match match; /* Description of fields. */
ovs_be32 duration_sec; /* Time flow has been alive in seconds. */
ovs_be32 duration_nsec; /* Time flow has been alive in nanoseconds
beyond duration_sec. */
ovs_32aligned_be64 byte_count; /* Number of bytes in flow. */
struct ofp_action_header actions[0]; /* Actions. */
};
-OFP_ASSERT(sizeof(struct ofp_flow_stats) == 88);
-
-/* Reply to OFPST_AGGREGATE request. */
-struct ofp_aggregate_stats_reply {
- struct ofp_stats_msg osm;
- ovs_32aligned_be64 packet_count; /* Number of packets in flows. */
- ovs_32aligned_be64 byte_count; /* Number of bytes in flows. */
- ovs_be32 flow_count; /* Number of flows. */
- uint8_t pad[4]; /* Align to 64 bits. */
-};
-OFP_ASSERT(sizeof(struct ofp_aggregate_stats_reply) == 36);
+OFP_ASSERT(sizeof(struct ofp10_flow_stats) == 88);
/* Body of reply to OFPST_TABLE request. */
-struct ofp_table_stats {
+struct ofp10_table_stats {
uint8_t table_id; /* Identifier of table. Lower numbered tables
are consulted first. */
uint8_t pad[3]; /* Align to 32-bits. */
char name[OFP_MAX_TABLE_NAME_LEN];
- ovs_be32 wildcards; /* Bitmap of OFPFW_* wildcards that are
+ ovs_be32 wildcards; /* Bitmap of OFPFW10_* wildcards that are
supported by the table. */
ovs_be32 max_entries; /* Max number of entries supported. */
ovs_be32 active_count; /* Number of active entries. */
ovs_32aligned_be64 lookup_count; /* # of packets looked up in table. */
ovs_32aligned_be64 matched_count; /* Number of packets that hit table. */
};
-OFP_ASSERT(sizeof(struct ofp_table_stats) == 64);
+OFP_ASSERT(sizeof(struct ofp10_table_stats) == 64);
/* Stats request of type OFPST_PORT. */
-struct ofp_port_stats_request {
- struct ofp_stats_msg osm;
+struct ofp10_port_stats_request {
ovs_be16 port_no; /* OFPST_PORT message may request statistics
for a single port (specified with port_no)
or for all ports (port_no == OFPP_NONE). */
uint8_t pad[6];
};
-OFP_ASSERT(sizeof(struct ofp_port_stats_request) == 20);
+OFP_ASSERT(sizeof(struct ofp10_port_stats_request) == 8);
/* Body of reply to OFPST_PORT request. If a counter is unsupported, set
* the field to all ones. */
-struct ofp_port_stats {
+struct ofp10_port_stats {
ovs_be16 port_no;
uint8_t pad[6]; /* Align to 64-bits. */
ovs_32aligned_be64 rx_packets; /* Number of received packets. */
ovs_32aligned_be64 rx_crc_err; /* Number of CRC errors. */
ovs_32aligned_be64 collisions; /* Number of collisions. */
};
-OFP_ASSERT(sizeof(struct ofp_port_stats) == 104);
+OFP_ASSERT(sizeof(struct ofp10_port_stats) == 104);
/* All ones is used to indicate all queues in a port (for stats retrieval). */
#define OFPQ_ALL 0xffffffff
/* Body for stats request of type OFPST_QUEUE. */
-struct ofp_queue_stats_request {
- struct ofp_stats_msg osm;
+struct ofp10_queue_stats_request {
ovs_be16 port_no; /* All ports if OFPP_ALL. */
uint8_t pad[2]; /* Align to 32-bits. */
ovs_be32 queue_id; /* All queues if OFPQ_ALL. */
};
-OFP_ASSERT(sizeof(struct ofp_queue_stats_request) == 20);
+OFP_ASSERT(sizeof(struct ofp10_queue_stats_request) == 8);
/* Body for stats reply of type OFPST_QUEUE consists of an array of this
* structure type. */
-struct ofp_queue_stats {
+struct ofp10_queue_stats {
ovs_be16 port_no;
uint8_t pad[2]; /* Align to 32-bits. */
ovs_be32 queue_id; /* Queue id. */
ovs_32aligned_be64 tx_packets; /* Number of transmitted packets. */
ovs_32aligned_be64 tx_errors; /* # of packets dropped due to overrun. */
};
-OFP_ASSERT(sizeof(struct ofp_queue_stats) == 32);
+OFP_ASSERT(sizeof(struct ofp10_queue_stats) == 32);
/* Vendor extension stats message. */
-struct ofp_vendor_stats_msg {
- struct ofp_stats_msg osm; /* Type OFPST_VENDOR. */
+struct ofp10_vendor_stats_msg {
+ struct ofp10_stats_msg osm; /* Type OFPST_VENDOR. */
ovs_be32 vendor; /* Vendor ID:
* - MSB 0: low-order bytes are IEEE OUI.
* - MSB != 0: defined by OpenFlow
* consortium. */
/* Followed by vendor-defined arbitrary additional data. */
};
-OFP_ASSERT(sizeof(struct ofp_vendor_stats_msg) == 16);
+OFP_ASSERT(sizeof(struct ofp10_vendor_stats_msg) == 16);
/* Vendor extension. */
struct ofp_vendor_header {
*/
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#define OFPP11_MAX 0xffffff00
#define OFPP11_OFFSET (OFPP11_MAX - OFPP_MAX)
-/* OpenFlow 1.1 specific message types, in addition to the common message
- * types. */
-enum ofp11_type {
- /* Controller command messages. */
- OFPT11_PACKET_OUT = 13, /* Controller/switch message */
- OFPT11_FLOW_MOD, /* Controller/switch message */
- OFPT11_GROUP_MOD, /* Controller/switch message */
- OFPT11_PORT_MOD, /* Controller/switch message */
- OFPT11_TABLE_MOD, /* Controller/switch message */
-
- /* Statistics messages. */
- OFPT11_STATS_REQUEST, /* Controller/switch message */
- OFPT11_STATS_REPLY, /* Controller/switch message */
-
- /* Barrier messages. */
- OFPT11_BARRIER_REQUEST, /* Controller/switch message */
- OFPT11_BARRIER_REPLY, /* Controller/switch message */
-
- /* Queue Configuration messages. */
- OFPT11_QUEUE_GET_CONFIG_REQUEST, /* Controller/switch message */
- OFPT11_QUEUE_GET_CONFIG_REPLY, /* Controller/switch message */
-};
-
/* OpenFlow 1.1 port config flags are just the common flags. */
#define OFPPC11_ALL \
(OFPPC_PORT_DOWN | OFPPC_NO_RECV | OFPPC_NO_FWD | OFPPC_NO_PACKET_IN)
/* Modify behavior of the physical port */
struct ofp11_port_mod {
- struct ofp_header header;
ovs_be32 port_no;
uint8_t pad[4];
uint8_t hw_addr[OFP_ETH_ALEN]; /* The hardware address is not
to prevent any action taking place. */
uint8_t pad3[4]; /* Pad to 64 bits. */
};
-OFP_ASSERT(sizeof(struct ofp11_port_mod) == 40);
+OFP_ASSERT(sizeof(struct ofp11_port_mod) == 32);
/* Group setup and teardown (controller -> datapath). */
struct ofp11_group_mod {
- struct ofp_header header;
ovs_be16 command; /* One of OFPGC_*. */
uint8_t type; /* One of OFPGT_*. */
uint8_t pad; /* Pad to 64 bits. */
/* struct ofp11_bucket buckets[0]; The bucket length is inferred from the
length field in the header. */
};
-OFP_ASSERT(sizeof(struct ofp11_group_mod) == 16);
+OFP_ASSERT(sizeof(struct ofp11_group_mod) == 8);
/* Query for port queue configuration. */
struct ofp11_queue_get_config_request {
- struct ofp_header header;
ovs_be32 port;
/* Port to be queried. Should refer
to a valid physical port (i.e. < OFPP_MAX) */
uint8_t pad[4];
};
-OFP_ASSERT(sizeof(struct ofp11_queue_get_config_request) == 16);
+OFP_ASSERT(sizeof(struct ofp11_queue_get_config_request) == 8);
/* Group commands */
enum ofp11_group_mod_command {
/* 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
OFPIT11_EXPERIMENTER = 0xFFFF /* Experimenter instruction */
};
+#define OFP11_INSTRUCTION_ALIGN 8
+
+/* Generic ofp_instruction structure. */
+struct ofp11_instruction {
+ ovs_be16 type; /* Instruction type */
+ ovs_be16 len; /* Length of this struct in bytes. */
+ uint8_t pad[4]; /* Align to 64-bits */
+};
+OFP_ASSERT(sizeof(struct ofp11_instruction) == 8);
+
/* Instruction structure for OFPIT_GOTO_TABLE */
struct ofp11_instruction_goto_table {
ovs_be16 type; /* OFPIT_GOTO_TABLE */
};
OFP_ASSERT(sizeof(struct ofp11_instruction_actions) == 8);
+/* Instruction structure for experimental instructions */
+struct ofp11_instruction_experimenter {
+ ovs_be16 type; /* OFPIT11_EXPERIMENTER */
+ ovs_be16 len; /* Length of this struct in bytes */
+ ovs_be32 experimenter; /* Experimenter ID which takes the same form
+ as in struct ofp_vendor_header. */
+ /* Experimenter-defined arbitrary additional data. */
+};
+OFP_ASSERT(sizeof(struct ofp11_instruction_experimenter) == 8);
+
/* Action structure for OFPAT_OUTPUT, which sends packets out 'port'.
* When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max
* number of bytes to send. A 'max_len' of zero means no bytes of the
/* Configure/Modify behavior of a flow table */
struct ofp11_table_mod {
- struct ofp_header header;
uint8_t table_id; /* ID of the table, 0xFF indicates all tables */
uint8_t pad[3]; /* Pad to 32 bits */
ovs_be32 config; /* Bitmap of OFPTC_* flags */
};
-OFP_ASSERT(sizeof(struct ofp11_table_mod) == 16);
+OFP_ASSERT(sizeof(struct ofp11_table_mod) == 8);
/* Flags to indicate behavior of the flow table for unmatched packets.
These flags are used in ofp_table_stats messages to describe the current
/* Flow setup and teardown (controller -> datapath). */
struct ofp11_flow_mod {
- struct ofp_header header;
ovs_be64 cookie; /* Opaque controller-issued identifier. */
ovs_be64 cookie_mask; /* Mask used to restrict the cookie bits
that must match when the command is
indicates no restriction. */
ovs_be32 out_group; /* For OFPFC_DELETE* commands, require
matching entries to include this as an
- output group. A value of OFPG_ANY
+ output group. A value of OFPG11_ANY
indicates no restriction. */
ovs_be16 flags; /* One of OFPFF_*. */
uint8_t pad[2];
- /* Open Flow version specific match */
- /* struct ofp_instruction instructions[0]; Instruction set */
+ /* Followed by an ofp11_match structure. */
+ /* Followed by an instruction set. */
};
-OFP_ASSERT(sizeof(struct ofp11_flow_mod) == 48);
+OFP_ASSERT(sizeof(struct ofp11_flow_mod) == 40);
/* Group types. Values in the range [128, 255] are reserved for experimental
* use. */
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
/* Queue configuration for a given port. */
struct ofp11_queue_get_config_reply {
- struct ofp_header header;
ovs_be32 port;
uint8_t pad[4];
/* struct ofp_packet_queue queues[0]; List of configured queues. */
};
-OFP_ASSERT(sizeof(struct ofp11_queue_get_config_reply) == 16);
+OFP_ASSERT(sizeof(struct ofp11_queue_get_config_reply) == 8);
struct ofp11_stats_msg {
struct ofp_header header;
ovs_be16 type; /* One of the OFPST_* constants. */
ovs_be16 flags; /* OFPSF_REQ_* flags (none yet defined). */
uint8_t pad[4];
- /* uint8_t body[0]; Body of the request. */
+ /* Followed by the body of the request. */
};
OFP_ASSERT(sizeof(struct ofp11_stats_msg) == 16);
+/* Vendor extension stats message. */
+struct ofp11_vendor_stats_msg {
+ struct ofp11_stats_msg osm; /* Type OFPST_VENDOR. */
+ ovs_be32 vendor; /* Vendor ID:
+ * - MSB 0: low-order bytes are IEEE OUI.
+ * - MSB != 0: defined by OpenFlow
+ * consortium. */
+ /* Followed by vendor-defined arbitrary additional data. */
+};
+OFP_ASSERT(sizeof(struct ofp11_vendor_stats_msg) == 20);
+
/* Stats request of type OFPST_FLOW. */
struct ofp11_flow_stats_request {
- struct ofp11_stats_msg osm;
uint8_t table_id; /* ID of table to read (from ofp_table_stats),
0xff for all tables. */
uint8_t pad[3]; /* Align to 64 bits. */
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
ovs_be64 cookie_mask; /* Mask used to restrict the cookie bits that
must match. A value of 0 indicates
no restriction. */
- struct ofp11_match match; /* Fields to match. */
+ /* Followed by an ofp11_match structure. */
};
-OFP_ASSERT(sizeof(struct ofp11_flow_stats_request) == 136);
+OFP_ASSERT(sizeof(struct ofp11_flow_stats_request) == 32);
/* Body of reply to OFPST_FLOW request. */
struct ofp11_flow_stats {
/* Body of reply to OFPST_TABLE request. */
struct ofp11_table_stats {
- struct ofp11_stats_msg osm;
uint8_t table_id; /* Identifier of table. Lower numbered tables
are consulted first. */
uint8_t pad[7]; /* Align to 64-bits. */
ovs_be64 lookup_count; /* Number of packets looked up in table. */
ovs_be64 matched_count; /* Number of packets that hit table. */
};
-OFP_ASSERT(sizeof(struct ofp11_table_stats) == 104);
+OFP_ASSERT(sizeof(struct ofp11_table_stats) == 88);
/* Body for ofp_stats_request of type OFPST_PORT. */
struct ofp11_port_stats_request {
- struct ofp11_stats_msg osm;
ovs_be32 port_no; /* OFPST_PORT message must request statistics
* either for a single port (specified in
* port_no) or for all ports (if port_no ==
* OFPP_ANY). */
uint8_t pad[4];
};
-OFP_ASSERT(sizeof(struct ofp11_port_stats_request) == 24);
+OFP_ASSERT(sizeof(struct ofp11_port_stats_request) == 8);
/* Body of reply to OFPST_PORT request. If a counter is unsupported, set
* the field to all ones. */
struct ofp11_port_stats {
- struct ofp11_stats_msg osm;
ovs_be32 port_no;
uint8_t pad[4]; /* Align to 64-bits. */
ovs_be64 rx_packets; /* Number of received packets. */
ovs_be64 rx_crc_err; /* Number of CRC errors. */
ovs_be64 collisions; /* Number of collisions. */
};
-OFP_ASSERT(sizeof(struct ofp11_port_stats) == 120);
+OFP_ASSERT(sizeof(struct ofp11_port_stats) == 104);
struct ofp11_queue_stats_request {
- struct ofp11_stats_msg osm;
ovs_be32 port_no; /* All ports if OFPP_ANY. */
ovs_be32 queue_id; /* All queues if OFPQ_ALL. */
};
-OFP_ASSERT(sizeof(struct ofp11_queue_stats_request) == 24);
+OFP_ASSERT(sizeof(struct ofp11_queue_stats_request) == 8);
struct ofp11_queue_stats {
- struct ofp11_stats_msg osm;
ovs_be32 port_no;
ovs_be32 queue_id; /* Queue id. */
ovs_be64 tx_bytes; /* Number of transmitted bytes. */
ovs_be64 tx_packets; /* Number of transmitted packets. */
ovs_be64 tx_errors; /* # of packets dropped due to overrun. */
};
-OFP_ASSERT(sizeof(struct ofp11_queue_stats) == 48);
+OFP_ASSERT(sizeof(struct ofp11_queue_stats) == 32);
struct ofp11_group_stats_request {
- struct ofp11_stats_msg osm;
ovs_be32 group_id; /* All groups if OFPG_ALL. */
uint8_t pad[4]; /* Align to 64 bits. */
};
-OFP_ASSERT(sizeof(struct ofp11_group_stats_request) == 24);
+OFP_ASSERT(sizeof(struct ofp11_group_stats_request) == 8);
/* Body of reply to OFPST11_GROUP request */
struct ofp11_group_stats {
/* Used in group stats replies. */
struct ofp11_bucket_counter {
- struct ofp11_stats_msg osm;
ovs_be64 packet_count; /* Number of packets processed by bucket. */
ovs_be64 byte_count; /* Number of bytes processed by bucket. */
};
-OFP_ASSERT(sizeof(struct ofp11_bucket_counter) == 32);
+OFP_ASSERT(sizeof(struct ofp11_bucket_counter) == 16);
/* Body of reply to OFPST11_GROUP_DESC request. */
struct ofp11_group_desc_stats {
/* Send packet (controller -> datapath). */
struct ofp11_packet_out {
- struct ofp_header header;
ovs_be32 buffer_id; /* ID assigned by datapath (-1 if none). */
ovs_be32 in_port; /* Packet's input port or OFPP_CONTROLLER. */
ovs_be16 actions_len; /* Size of action array in bytes. */
from the length field in the header.
(Only meaningful if buffer_id == -1.) */
};
-OFP_ASSERT(sizeof(struct ofp11_packet_out) == 24);
+OFP_ASSERT(sizeof(struct ofp11_packet_out) == 16);
/* Packet received on port (datapath -> controller). */
struct ofp11_packet_in {
- struct ofp_header header;
ovs_be32 buffer_id; /* ID assigned by datapath. */
ovs_be32 in_port; /* Port on which frame was received. */
ovs_be32 in_phy_port; /* Physical Port on which frame was received. */
offsetof(struct ofp_packet_in, data) ==
sizeof(struct ofp_packet_in) - 2. */
};
-OFP_ASSERT(sizeof(struct ofp11_packet_in) == 24);
+OFP_ASSERT(sizeof(struct ofp11_packet_in) == 16);
/* Flow removed (datapath -> controller). */
struct ofp11_flow_removed {
- struct ofp_header header;
ovs_be64 cookie; /* Opaque controller-issued identifier. */
ovs_be16 priority; /* Priority level of flow entry. */
uint8_t pad2[2]; /* Align to 64-bits. */
ovs_be64 packet_count;
ovs_be64 byte_count;
- struct ofp11_match match; /* Description of fields. */
+ /* Followed by an ofp11_match structure. */
};
-OFP_ASSERT(sizeof(struct ofp11_flow_removed) == 136);
+OFP_ASSERT(sizeof(struct ofp11_flow_removed) == 40);
#endif /* openflow/openflow-1.1.h */
*/
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
* Copyright (c) 2012 Horms Solutions Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
#include "openflow/openflow-1.1.h"
-/* OpenFlow 1.2 specific message types, in addition to the common message
- * types. */
-enum ofp12_type {
- /* Controller role change request messages. */
- OFPT12_ROLE_REQUEST = 24, /* Controller/switch message */
- OFPT12_ROLE_REPLY, /* Controller/switch message */
-};
-
/*
* OXM Class IDs.
* The high order bit differentiate reserved classes from member classes.
*/
#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)
to outermost */
OFPAT12_COPY_TTL_IN, /* Copy TTL "inwards" -- from outermost to
next-to-outermost */
- OFPAT12_SET_MPLS_TTL, /* MPLS TTL */
+ OFPAT12_SET_MPLS_TTL = 15, /* MPLS TTL */
OFPAT12_DEC_MPLS_TTL, /* Decrement MPLS TTL */
OFPAT12_PUSH_VLAN, /* Push a new VLAN tag */
OFPAT12_POP_VLAN, /* Pop the outer VLAN tag */
};
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. */
/* Body of reply to OFPST_TABLE request. */
struct ofp12_table_stats {
- struct ofp11_stats_msg osm;
uint8_t table_id; /* Identifier of table. Lower numbered tables
are consulted first. */
uint8_t pad[7]; /* Align to 64-bits. */
ovs_be64 lookup_count; /* Number of packets looked up in table. */
ovs_be64 matched_count; /* Number of packets that hit table. */
};
-OFP_ASSERT(sizeof(struct ofp12_table_stats) == 144);
+OFP_ASSERT(sizeof(struct ofp12_table_stats) == 128);
/* Body of reply to OFPST12_GROUP_FEATURES request. Group features. */
struct ofp12_group_features_stats {
- struct ofp11_stats_msg osm;
ovs_be32 types; /* Bitmap of OFPGT_* values supported. */
ovs_be32 capabilities; /* Bitmap of OFPGFC12_* capability supported. */
ovs_be32 max_groups[4]; /* Maximum number of groups for each type. */
ovs_be32 actions[4]; /* Bitmaps of OFPAT_* that are supported. */
};
-OFP_ASSERT(sizeof(struct ofp12_group_features_stats) == 56);
+OFP_ASSERT(sizeof(struct ofp12_group_features_stats) == 40);
/* Group configuration flags */
enum ofp12_group_capabilities {
/* Role request and reply message. */
struct ofp12_role_request {
- struct ofp_header header; /* Type OFPT12_ROLE_REQUEST/OFPT12_ROLE_REPLY. */
ovs_be32 role; /* One of OFPCR12_ROLE_*. */
uint8_t pad[4]; /* Align to 64 bits. */
ovs_be64 generation_id; /* Master Election Generation Id */
};
-OFP_ASSERT(sizeof(struct ofp12_role_request) == 24);
+OFP_ASSERT(sizeof(struct ofp12_role_request) == 16);
/* Controller roles. */
enum ofp12_controller_role {
/* Packet received on port (datapath -> controller). */
struct ofp12_packet_in {
- struct ofp_header header;
ovs_be32 buffer_id; /* ID assigned by datapath. */
ovs_be16 total_len; /* Full length of frame. */
uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */
/* uint8_t pad[2]; Align to 64 bit + 16 bit */
/* uint8_t data[0]; Ethernet frame */
};
-OFP_ASSERT(sizeof(struct ofp12_packet_in) == 16);
+OFP_ASSERT(sizeof(struct ofp12_packet_in) == 8);
/* Flow removed (datapath -> controller). */
struct ofp12_flow_removed {
- struct ofp_header header;
ovs_be64 cookie; /* Opaque controller-issued identifier. */
ovs_be16 priority; /* Priority level of flow entry. */
ovs_be64 byte_count;
/* struct ofp12_match match; Description of fields. Variable size. */
};
-OFP_ASSERT(sizeof(struct ofp12_flow_removed) == 48);
+OFP_ASSERT(sizeof(struct ofp12_flow_removed) == 40);
#endif /* openflow/openflow-1.2.h */
-/* Copyright (c) 2008, 2011 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2011, 2012 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
*/
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* The most significant bit being set in the version field indicates an
* experimental OpenFlow version.
*/
-#define OFP10_VERSION 0x01
-#define OFP11_VERSION 0x02
-#define OFP12_VERSION 0x03
+enum ofp_version {
+ OFP10_VERSION = 0x01,
+ OFP11_VERSION = 0x02,
+ OFP12_VERSION = 0x03,
+};
#define OFP_MAX_TABLE_NAME_LEN 32
#define OFP_MAX_PORT_NAME_LEN 16
#define OFP_ETH_ALEN 6 /* Bytes in an Ethernet address. */
-/* Common OpenFlow message types. */
-enum ofp_type {
- /* Immutable messages. */
- OFPT_HELLO, /* Symmetric message */
- OFPT_ERROR, /* Symmetric message */
- OFPT_ECHO_REQUEST, /* Symmetric message */
- OFPT_ECHO_REPLY, /* Symmetric message */
- OFPT_VENDOR, /* Symmetric message */
-
- /* Switch configuration messages. */
- OFPT_FEATURES_REQUEST, /* Controller/switch message */
- OFPT_FEATURES_REPLY, /* Controller/switch message */
- OFPT_GET_CONFIG_REQUEST, /* Controller/switch message */
- OFPT_GET_CONFIG_REPLY, /* Controller/switch message */
- OFPT_SET_CONFIG, /* Controller/switch message */
-
- /* Asynchronous messages. */
- OFPT_PACKET_IN, /* Async message */
- OFPT_FLOW_REMOVED, /* Async message */
- OFPT_PORT_STATUS, /* Async message */
-};
-
/* Header on all OpenFlow packets. */
struct ofp_header {
uint8_t version; /* An OpenFlow version number, e.g. OFP10_VERSION. */
/* Switch features. */
struct ofp_switch_features {
- struct ofp_header header;
ovs_be64 datapath_id; /* Datapath unique ID. The lower 48-bits are for
a MAC address, while the upper 16-bits are
implementer-defined. */
/* Followed by an array of struct ofp10_phy_port or struct ofp11_port
* structures. The number is inferred from header.length. */
};
-OFP_ASSERT(sizeof(struct ofp_switch_features) == 32);
+OFP_ASSERT(sizeof(struct ofp_switch_features) == 24);
/* Common capabilities supported by the datapath (struct ofp_switch_features,
* member capabilities). */
OFPR_N_REASONS
};
+enum ofp_flow_mod_command {
+ OFPFC_ADD, /* New flow. */
+ OFPFC_MODIFY, /* Modify all matching flows. */
+ OFPFC_MODIFY_STRICT, /* Modify entry strictly matching wildcards */
+ OFPFC_DELETE, /* Delete all matching flows. */
+ OFPFC_DELETE_STRICT /* Strictly match wildcards and priority. */
+};
+
+enum ofp_flow_mod_flags {
+ OFPFF_SEND_FLOW_REM = 1 << 0, /* Send flow removed message when flow
+ * expires or is deleted. */
+ OFPFF_CHECK_OVERLAP = 1 << 1, /* Check for overlapping entries first. */
+};
+
+/* Action structure for OFPAT10_SET_VLAN_VID and OFPAT11_SET_VLAN_VID. */
+struct ofp_action_vlan_vid {
+ ovs_be16 type; /* Type. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be16 vlan_vid; /* VLAN id. */
+ uint8_t pad[2];
+};
+OFP_ASSERT(sizeof(struct ofp_action_vlan_vid) == 8);
+
+/* Action structure for OFPAT10_SET_VLAN_PCP and OFPAT11_SET_VLAN_PCP. */
+struct ofp_action_vlan_pcp {
+ ovs_be16 type; /* Type. */
+ ovs_be16 len; /* Length is 8. */
+ uint8_t vlan_pcp; /* VLAN priority. */
+ uint8_t pad[3];
+};
+OFP_ASSERT(sizeof(struct ofp_action_vlan_pcp) == 8);
+
+/* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */
+struct ofp_action_dl_addr {
+ ovs_be16 type; /* Type. */
+ ovs_be16 len; /* Length is 16. */
+ uint8_t dl_addr[OFP_ETH_ALEN]; /* Ethernet address. */
+ uint8_t pad[6];
+};
+OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16);
+
+/* Action structure for OFPAT10_SET_NW_SRC/DST and OFPAT11_SET_NW_SRC/DST. */
+struct ofp_action_nw_addr {
+ ovs_be16 type; /* Type. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 nw_addr; /* IP address. */
+};
+OFP_ASSERT(sizeof(struct ofp_action_nw_addr) == 8);
+
+/* Action structure for OFPAT10_SET_NW_TOS and OFPAT11_SET_NW_TOS. */
+struct ofp_action_nw_tos {
+ ovs_be16 type; /* Type.. */
+ ovs_be16 len; /* Length is 8. */
+ uint8_t nw_tos; /* DSCP in high 6 bits, rest ignored. */
+ uint8_t pad[3];
+};
+OFP_ASSERT(sizeof(struct ofp_action_nw_tos) == 8);
+
+/* Action structure for OFPAT10_SET_TP_SRC/DST and OFPAT11_SET_TP_SRC/DST. */
+struct ofp_action_tp_port {
+ ovs_be16 type; /* Type. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be16 tp_port; /* TCP/UDP port. */
+ uint8_t pad[2];
+};
+OFP_ASSERT(sizeof(struct ofp_action_tp_port) == 8);
+
/* Why was this flow removed? */
enum ofp_flow_removed_reason {
OFPRR_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */
OFPRR_HARD_TIMEOUT, /* Time exceeded hard_timeout. */
OFPRR_DELETE, /* Evicted by a DELETE flow mod. */
- OFPRR_GROUP_DELETE /* Group was removed. */
+ OFPRR_GROUP_DELETE, /* Group was removed. */
+ OFPRR_EVICTION, /* Switch eviction to free resources. */
};
/* What changed about the physical port */
/* A physical port has changed in the datapath */
struct ofp_port_status {
- struct ofp_header header;
uint8_t reason; /* One of OFPPR_*. */
uint8_t pad[7]; /* Align to 64-bits. */
/* Followed by struct ofp10_phy_port or struct ofp11_port. */
};
-OFP_ASSERT(sizeof(struct ofp_port_status) == 16);
+OFP_ASSERT(sizeof(struct ofp_port_status) == 8);
+
+#define DESC_STR_LEN 256
+#define SERIAL_NUM_LEN 32
+/* Body of reply to OFPST_DESC request. Each entry is a NULL-terminated ASCII
+ * string. */
+struct ofp_desc_stats {
+ char mfr_desc[DESC_STR_LEN]; /* Manufacturer description. */
+ char hw_desc[DESC_STR_LEN]; /* Hardware description. */
+ char sw_desc[DESC_STR_LEN]; /* Software description. */
+ char serial_num[SERIAL_NUM_LEN]; /* Serial number. */
+ char dp_desc[DESC_STR_LEN]; /* Human readable description of
+ the datapath. */
+};
+OFP_ASSERT(sizeof(struct ofp_desc_stats) == 1056);
+
+/* Reply to OFPST_AGGREGATE request. */
+struct ofp_aggregate_stats_reply {
+ ovs_32aligned_be64 packet_count; /* Number of packets in flows. */
+ ovs_32aligned_be64 byte_count; /* Number of bytes in flows. */
+ ovs_be32 flow_count; /* Number of flows. */
+ uint8_t pad[4]; /* Align to 64 bits. */
+};
+OFP_ASSERT(sizeof(struct ofp_aggregate_stats_reply) == 24);
/* The match type indicates the match structure (set of fields that compose the
* match) in use. The match type is placed in the type field at the beginning
OFPMT_OXM = 1, /* OpenFlow Extensible Match */
};
+/* Group numbering. Groups can use any number up to OFPG_MAX. */
+enum ofp_group {
+ /* Last usable group number. */
+ OFPG_MAX = 0xffffff00,
+
+ /* Fake groups. */
+ OFPG_ALL = 0xfffffffc, /* All groups, for group delete commands. */
+ OFPG_ANY = 0xffffffff /* Wildcard, for flow stats requests. */
+};
+
#endif /* openflow/openflow-common.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
*
* This file is offered under your choice of two licenses: Apache 2.0 or GNU
* GPL 2.0 or later. The permission statements for each of these licenses is
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* This file is offered under your choice of two licenses: Apache 2.0 or GNU
* GPL 2.0 or later. The permission statements for each of these licenses is
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* This file is offered under your choice of two licenses: Apache 2.0 or GNU
* GPL 2.0 or later. The permission statements for each of these licenses is
/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
int msg_flags;
};
+struct cmsghdr {
+ size_t cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+ unsigned char cmsg_data[];
+};
+
+#define __CMSG_ALIGNTO sizeof(size_t)
+#define CMSG_ALIGN(LEN) \
+ (((LEN) + __CMSG_ALIGNTO - 1) / __CMSG_ALIGNTO * __CMSG_ALIGNTO)
+#define CMSG_DATA(CMSG) ((CMSG)->cmsg_data)
+#define CMSG_LEN(LEN) (sizeof(struct cmsghdr) + (LEN))
+#define CMSG_SPACE(LEN) CMSG_ALIGN(CMSG_LEN(LEN))
+#define CMSG_FIRSTHDR(MSG) \
+ ((MSG)->msg_controllen ? (struct cmsghdr *) (MSG)->msg_control : NULL)
+#define CMSG_NXTHDR(MSG, CMSG) __cmsg_nxthdr(MSG, CMSG)
+
+static inline struct cmsghdr *
+__cmsg_nxthdr(struct msghdr *msg, struct cmsghdr *cmsg)
+{
+ size_t ofs = (char *) cmsg - (char *) msg->msg_control;
+ size_t next_ofs = ofs + CMSG_ALIGN(cmsg->cmsg_len);
+ return (next_ofs < msg->msg_controllen
+ ? (void *) ((char *) msg->msg_control + next_ofs)
+ : NULL);
+}
+
+enum {
+ SCM_RIGHTS = 1
+};
+
enum {
SOCK_DGRAM,
SOCK_RAW,
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/dirs.c
/coverage-counters.c
/ofp-errors.inc
+/ofp-msgs.inc
/vswitch-idl.c
/vswitch-idl.h
/vswitch-idl.ovsidl
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
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 \
lib/nx-match.h \
lib/odp-util.c \
lib/odp-util.h \
+ lib/ofp-actions.c \
+ lib/ofp-actions.h \
lib/ofp-errors.c \
lib/ofp-errors.h \
+ lib/ofp-msgs.c \
+ lib/ofp-msgs.h \
lib/ofp-parse.c \
lib/ofp-parse.h \
lib/ofp-print.c \
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 \
lib/timer.h \
lib/timeval.c \
lib/timeval.h \
+ lib/token-bucket.c \
+ lib/token-bucket.h \
lib/tunalloc.c \
lib/tunalloc.h \
lib/type-props.h \
lib/vlog.c \
lib/vlog.h \
lib/vswitch-idl.c \
- lib/vswitch-idl.h
+ lib/vswitch-idl.h \
+ lib/worker.c \
+ lib/worker.h
nodist_lib_libopenvswitch_a_SOURCES = \
lib/dirs.c
lib/route-table.h
endif
+if HAVE_IF_DL
+lib_libopenvswitch_a_SOURCES += \
+ lib/netdev-bsd.c \
+ lib/rtbsd.c \
+ lib/rtbsd.h \
+ lib/route-table-bsd.c
+endif
+
if HAVE_OPENSSL
lib_libopenvswitch_a_SOURCES += lib/stream-ssl.c
nodist_lib_libopenvswitch_a_SOURCES += lib/dhparams.c
lib/daemon.man \
lib/daemon-syn.man \
lib/leak-checker.man \
+ lib/memory-unixctl.man \
lib/ovs.tmac \
lib/ssl-bootstrap.man \
lib/ssl-bootstrap-syn.man \
-e 's,[@]srcdir[@],$(srcdir),g' \
-e 's,[@]LOGDIR[@],"$(LOGDIR)",g' \
-e 's,[@]RUNDIR[@],"$(RUNDIR)",g' \
+ -e 's,[@]DBDIR[@],"$(DBDIR)",g' \
-e 's,[@]bindir[@],"$(bindir)",g' \
-e 's,[@]sysconfdir[@],"$(sysconfdir)",g' \
-e 's,[@]pkgdatadir[@],"$(pkgdatadir)",g') \
$(srcdir)/lib/ofp-errors.inc: \
lib/ofp-errors.h $(srcdir)/build-aux/extract-ofp-errors
- $(PYTHON) $(srcdir)/build-aux/extract-ofp-errors \
+ $(run_python) $(srcdir)/build-aux/extract-ofp-errors \
$(srcdir)/lib/ofp-errors.h > $@.tmp && mv $@.tmp $@
$(srcdir)/lib/ofp-errors.c: $(srcdir)/lib/ofp-errors.inc
EXTRA_DIST += build-aux/extract-ofp-errors lib/ofp-errors.inc
+$(srcdir)/lib/ofp-msgs.inc: \
+ lib/ofp-msgs.h $(srcdir)/build-aux/extract-ofp-msgs
+ $(run_python) $(srcdir)/build-aux/extract-ofp-msgs \
+ $(srcdir)/lib/ofp-msgs.h $@ > $@.tmp && mv $@.tmp $@
+$(srcdir)/lib/ofp-msgs.c: $(srcdir)/lib/ofp-msgs.inc
+EXTRA_DIST += build-aux/extract-ofp-msgs lib/ofp-msgs.inc
+
INSTALL_DATA_LOCAL += lib-install-data-local
lib-install-data-local:
$(MKDIR_P) $(DESTDIR)$(RUNDIR)
$(MKDIR_P) $(DESTDIR)$(PKIDIR)
$(MKDIR_P) $(DESTDIR)$(LOGDIR)
+ $(MKDIR_P) $(DESTDIR)$(DBDIR)
if !USE_LINKER_SECTIONS
# All distributed sources, with names adjust properly for referencing
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "flow.h"
#include "meta-flow.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "openflow/nicira-ext.h"
VLOG_DEFINE_THIS_MODULE(autopath);
-/* Loads 'ofp_port' into the appropriate register in accordance with the
- * autopath action. */
void
-autopath_execute(const struct nx_action_autopath *ap, struct flow *flow,
- uint16_t ofp_port)
-{
- struct mf_subfield dst;
-
- nxm_decode(&dst, ap->dst, ap->ofs_nbits);
- mf_set_subfield_value(&dst, ofp_port, flow);
-}
-
-void
-autopath_parse(struct nx_action_autopath *ap, const char *s_)
+autopath_parse(struct ofpact_autopath *ap, const char *s_)
{
char *s;
- char *id_str, *dst_s, *save_ptr;
- struct mf_subfield dst;
int id_int;
+ char *id_str, *dst, *save_ptr;
+
+ ofpact_init_AUTOPATH(ap);
s = xstrdup(s_);
save_ptr = NULL;
id_str = strtok_r(s, ", ", &save_ptr);
- dst_s = strtok_r(NULL, ", ", &save_ptr);
+ dst = strtok_r(NULL, ", ", &save_ptr);
- if (!dst_s) {
+ if (!dst) {
ovs_fatal(0, "%s: not enough arguments to autopath action", s_);
}
ovs_fatal(0, "%s: autopath id %d is not in valid range "
"1 to %"PRIu32, s_, id_int, UINT32_MAX);
}
+ ap->port = id_int;
- mf_parse_subfield(&dst, dst_s);
- if (dst.n_bits < 16) {
+ mf_parse_subfield(&ap->dst, dst);
+ if (ap->dst.n_bits < 16) {
ovs_fatal(0, "%s: %d-bit destination field has %u possible values, "
"less than required 65536",
- s_, dst.n_bits, 1u << dst.n_bits);
+ s_, ap->dst.n_bits, 1u << ap->dst.n_bits);
}
- ofputil_init_NXAST_AUTOPATH(ap);
- ap->id = htonl(id_int);
- ap->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
- ap->dst = htonl(dst.field->nxm_header);
-
free(s);
}
enum ofperr
-autopath_check(const struct nx_action_autopath *ap, const struct flow *flow)
+autopath_from_openflow(const struct nx_action_autopath *nap,
+ struct ofpact_autopath *autopath)
{
- struct mf_subfield dst;
+ ofpact_init_AUTOPATH(autopath);
+ autopath->dst.field = mf_from_nxm_header(ntohl(nap->dst));
+ autopath->dst.ofs = nxm_decode_ofs(nap->ofs_nbits);
+ autopath->dst.n_bits = nxm_decode_n_bits(nap->ofs_nbits);
+ autopath->port = ntohl(nap->id);
- nxm_decode(&dst, ap->dst, ap->ofs_nbits);
- if (dst.n_bits < 16) {
+ if (autopath->dst.n_bits < 16) {
VLOG_WARN("at least 16 bit destination is required for autopath "
"action.");
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- return mf_check_dst(&dst, flow);
+ return autopath_check(autopath, NULL);
+}
+
+enum ofperr
+autopath_check(const struct ofpact_autopath *autopath, const struct flow *flow)
+{
+ VLOG_WARN_ONCE("The autopath action is deprecated and may be removed in"
+ " February 2013. Please email dev@openvswitch.org with"
+ " concerns.");
+ return mf_check_dst(&autopath->dst, flow);
+}
+
+void
+autopath_to_nxast(const struct ofpact_autopath *autopath,
+ struct ofpbuf *openflow)
+{
+ struct nx_action_autopath *ap;
+
+ ap = ofputil_put_NXAST_AUTOPATH__DEPRECATED(openflow);
+ ap->ofs_nbits = nxm_encode_ofs_nbits(autopath->dst.ofs,
+ autopath->dst.n_bits);
+ ap->dst = htonl(autopath->dst.field->nxm_header);
+ ap->id = htonl(autopath->port);
}
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct flow;
struct nx_action_autopath;
+struct ofpact_autopath;
+struct ofpbuf;
/* NXAST_AUTOPATH helper functions.
*
* See include/openflow/nicira-ext.h for NXAST_AUTOPATH specification. */
-void autopath_execute(const struct nx_action_autopath *, struct flow *,
- uint16_t ofp_port);
-void autopath_parse(struct nx_action_autopath *, const char *);
-enum ofperr autopath_check(const struct nx_action_autopath *,
+void autopath_parse(struct ofpact_autopath *, const char *);
+
+enum ofperr autopath_from_openflow(const struct nx_action_autopath *,
+ struct ofpact_autopath *);
+enum ofperr autopath_check(const struct ofpact_autopath *,
const struct flow *);
+void autopath_to_nxast(const struct ofpact_autopath *,
+ struct ofpbuf *openflow);
#endif /* autopath.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
static inline unsigned long *
bitmap_unit__(const unsigned long *bitmap, size_t offset)
{
- return (unsigned long *) &bitmap[offset / BITMAP_ULONG_BITS];
+ return CONST_CAST(unsigned long *, &bitmap[offset / BITMAP_ULONG_BITS]);
}
static inline unsigned long
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
static tag_type bond_get_active_slave_tag(const struct bond *);
static struct bond_slave *choose_output_slave(const struct bond *,
const struct flow *,
- uint16_t vlan);
+ uint16_t vlan, tag_type *tags);
static void bond_update_fake_slave_stats(struct bond *);
/* Attempts to parse 's' as the name of a bond balancing mode. If successful,
if (bond->balance != s->balance) {
bond->balance = s->balance;
revalidate = true;
+
+ if (bond->balance == BM_STABLE) {
+ VLOG_WARN_ONCE("Stable bond mode is deprecated and may be removed"
+ " in February 2013. Please email"
+ " dev@openvswitch.org with concerns.");
+ }
}
if (bond->basis != s->basis) {
may_send_learning_packets(const struct bond *bond)
{
return bond->lacp_status == LACP_DISABLED
- && bond->balance != BM_AB
+ && bond->balance != BM_STABLE
&& bond->active_slave;
}
* is located. For each MAC that has been learned on a port other than 'bond',
* it should call bond_compose_learning_packet().
*
- * This function will only return true if 'bond' is in SLB mode and LACP is not
- * negotiated. Otherwise sending learning packets isn't necessary.
+ * This function will only return true if 'bond' is in SLB or active-backup
+ * mode and LACP is not negotiated. Otherwise sending learning packets isn't
+ * necessary.
*
* Calling this function resets the state that it checks. */
bool
{
struct bond_slave *slave;
struct ofpbuf *packet;
+ tag_type tags = 0;
struct flow flow;
assert(may_send_learning_packets(bond));
memset(&flow, 0, sizeof flow);
memcpy(flow.dl_src, eth_src, ETH_ADDR_LEN);
- slave = choose_output_slave(bond, &flow, vlan);
+ slave = choose_output_slave(bond, &flow, vlan, &tags);
packet = ofpbuf_new(0);
- compose_benign_packet(packet, "Open vSwitch Bond Failover", 0xf177,
- eth_src);
+ compose_rarp(packet, eth_src);
if (vlan) {
eth_push_vlan(packet, htons(vlan));
}
bond_choose_output_slave(struct bond *bond, const struct flow *flow,
uint16_t vlan, tag_type *tags)
{
- struct bond_slave *slave = choose_output_slave(bond, flow, vlan);
+ struct bond_slave *slave = choose_output_slave(bond, flow, vlan, tags);
if (slave) {
*tags |= bond->balance == BM_STABLE ? bond->stb_tag : slave->tag;
return slave->aux;
static struct bond_slave *
choose_output_slave(const struct bond *bond, const struct flow *flow,
- uint16_t vlan)
+ uint16_t vlan, tag_type *tags)
{
struct bond_entry *e;
}
e->tag = tag_create_random();
}
+ *tags |= e->tag;
return e->slave;
default:
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2011, 2012 Nicira Networks.
+/* Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "meta-flow.h"
#include "nx-match.h"
#include "ofpbuf.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "openflow/nicira-ext.h"
VLOG_DEFINE_THIS_MODULE(bundle);
static uint16_t
-execute_ab(const struct nx_action_bundle *nab,
+execute_ab(const struct ofpact_bundle *bundle,
bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
{
size_t i;
- for (i = 0; i < ntohs(nab->n_slaves); i++) {
- uint16_t slave = bundle_get_slave(nab, i);
-
+ for (i = 0; i < bundle->n_slaves; i++) {
+ uint16_t slave = bundle->slaves[i];
if (slave_enabled(slave, aux)) {
return slave;
}
}
static uint16_t
-execute_hrw(const struct nx_action_bundle *nab, const struct flow *flow,
+execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow,
bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
{
uint32_t flow_hash, best_hash;
int best, i;
- flow_hash = flow_hash_fields(flow, ntohs(nab->fields), ntohs(nab->basis));
+ flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis);
best = -1;
best_hash = 0;
- for (i = 0; i < ntohs(nab->n_slaves); i++) {
- if (slave_enabled(bundle_get_slave(nab, i), aux)) {
+ for (i = 0; i < bundle->n_slaves; i++) {
+ if (slave_enabled(bundle->slaves[i], aux)) {
uint32_t hash = hash_2words(i, flow_hash);
if (best < 0 || hash > best_hash) {
}
}
- return best >= 0 ? bundle_get_slave(nab, best) : OFPP_NONE;
+ return best >= 0 ? bundle->slaves[best] : OFPP_NONE;
}
-/* Executes 'nab' on 'flow'. Uses 'slave_enabled' to determine if the slave
+/* Executes 'bundle' on 'flow'. Uses 'slave_enabled' to determine if the slave
* designated by 'ofp_port' is up. Returns the chosen slave, or OFPP_NONE if
* none of the slaves are acceptable. */
uint16_t
-bundle_execute(const struct nx_action_bundle *nab, const struct flow *flow,
+bundle_execute(const struct ofpact_bundle *bundle, const struct flow *flow,
bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
{
- switch (ntohs(nab->algorithm)) {
- case NX_BD_ALG_HRW: return execute_hrw(nab, flow, slave_enabled, aux);
- case NX_BD_ALG_ACTIVE_BACKUP: return execute_ab(nab, slave_enabled, aux);
- default: NOT_REACHED();
- }
-}
+ switch (bundle->algorithm) {
+ case NX_BD_ALG_HRW:
+ return execute_hrw(bundle, flow, slave_enabled, aux);
-void
-bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow,
- bool (*slave_enabled)(uint16_t ofp_port, void *aux),
- void *aux)
-{
- struct mf_subfield dst;
+ case NX_BD_ALG_ACTIVE_BACKUP:
+ return execute_ab(bundle, slave_enabled, aux);
- nxm_decode(&dst, nab->dst, nab->ofs_nbits);
- mf_set_subfield_value(&dst, bundle_execute(nab, flow, slave_enabled, aux),
- flow);
+ default:
+ NOT_REACHED();
+ }
}
/* Checks that 'nab' specifies a bundle action which is supported by this
* ofputil_check_output_port(). Returns 0 if 'nab' is supported, otherwise an
* OFPERR_* error code. */
enum ofperr
-bundle_check(const struct nx_action_bundle *nab, int max_ports,
- const struct flow *flow)
+bundle_from_openflow(const struct nx_action_bundle *nab,
+ struct ofpbuf *ofpacts)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- uint16_t n_slaves, fields, algorithm, subtype;
+ struct ofpact_bundle *bundle;
+ uint16_t subtype;
uint32_t slave_type;
size_t slaves_size, i;
enum ofperr error;
+ bundle = ofpact_put_BUNDLE(ofpacts);
+
subtype = ntohs(nab->subtype);
- n_slaves = ntohs(nab->n_slaves);
- fields = ntohs(nab->fields);
- algorithm = ntohs(nab->algorithm);
+ bundle->n_slaves = ntohs(nab->n_slaves);
+ bundle->basis = ntohs(nab->basis);
+ bundle->fields = ntohs(nab->fields);
+ bundle->algorithm = ntohs(nab->algorithm);
slave_type = ntohl(nab->slave_type);
slaves_size = ntohs(nab->len) - sizeof *nab;
error = OFPERR_OFPBAC_BAD_ARGUMENT;
- if (!flow_hash_fields_valid(fields)) {
- VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, fields);
- } else if (n_slaves > BUNDLE_MAX_SLAVES) {
+ if (!flow_hash_fields_valid(bundle->fields)) {
+ VLOG_WARN_RL(&rl, "unsupported fields %d", (int) bundle->fields);
+ } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) {
VLOG_WARN_RL(&rl, "too may slaves");
- } else if (algorithm != NX_BD_ALG_HRW
- && algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
- VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16, algorithm);
+ } else if (bundle->algorithm != NX_BD_ALG_HRW
+ && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
+ VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm);
} else if (slave_type != NXM_OF_IN_PORT) {
VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type);
} else {
error = 0;
}
- for (i = 0; i < sizeof(nab->zero); i++) {
- if (nab->zero[i]) {
- VLOG_WARN_RL(&rl, "reserved field is nonzero");
- error = OFPERR_OFPBAC_BAD_ARGUMENT;
- }
+ if (!is_all_zeros(nab->zero, sizeof nab->zero)) {
+ VLOG_WARN_RL(&rl, "reserved field is nonzero");
+ error = OFPERR_OFPBAC_BAD_ARGUMENT;
}
if (subtype == NXAST_BUNDLE && (nab->ofs_nbits || nab->dst)) {
}
if (subtype == NXAST_BUNDLE_LOAD) {
- struct mf_subfield dst;
+ bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst));
+ bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits);
+ bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits);
- nxm_decode(&dst, nab->dst, nab->ofs_nbits);
- if (dst.n_bits < 16) {
+ if (bundle->dst.n_bits < 16) {
VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit "
"destination.");
error = OFPERR_OFPBAC_BAD_ARGUMENT;
- } else if (!error) {
- error = mf_check_dst(&dst, flow);
}
}
- if (slaves_size < n_slaves * sizeof(ovs_be16)) {
+ if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) {
VLOG_WARN_RL(&rl, "Nicira action %"PRIu16" only has %zu bytes "
"allocated for slaves. %zu bytes are required for "
"%"PRIu16" slaves.", subtype, slaves_size,
- n_slaves * sizeof(ovs_be16), n_slaves);
+ bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves);
error = OFPERR_OFPBAC_BAD_LEN;
}
- for (i = 0; i < n_slaves; i++) {
- uint16_t ofp_port = bundle_get_slave(nab, i);
- enum ofperr ofputil_error;
+ for (i = 0; i < bundle->n_slaves; i++) {
+ uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]);
+ ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port);
+ }
+
+ bundle = ofpacts->l2;
+ ofpact_update_len(ofpacts, &bundle->ofpact);
+
+ if (!error) {
+ error = bundle_check(bundle, OFPP_MAX, NULL);
+ }
+ return error;
+}
+
+enum ofperr
+bundle_check(const struct ofpact_bundle *bundle, int max_ports,
+ const struct flow *flow)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ size_t i;
+
+ if (bundle->dst.field) {
+ enum ofperr error = mf_check_dst(&bundle->dst, flow);
+ if (error) {
+ return error;
+ }
+ }
+
+ for (i = 0; i < bundle->n_slaves; i++) {
+ uint16_t ofp_port = bundle->slaves[i];
+ enum ofperr error;
- ofputil_error = ofputil_check_output_port(ofp_port, max_ports);
- if (ofputil_error) {
+ error = ofputil_check_output_port(ofp_port, max_ports);
+ if (error) {
VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port);
- error = ofputil_error;
+ return error;
}
/* Controller slaves are unsupported due to the lack of a max_len
* seem to be a real-world use-case for supporting it. */
if (ofp_port == OFPP_CONTROLLER) {
VLOG_WARN_RL(&rl, "unsupported controller slave");
- error = OFPERR_OFPBAC_BAD_OUT_PORT;
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
}
}
- return error;
+ return 0;
+}
+
+void
+bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow)
+{
+ int slaves_len = ROUND_UP(2 * bundle->n_slaves, OFP_ACTION_ALIGN);
+ struct nx_action_bundle *nab;
+ ovs_be16 *slaves;
+ size_t i;
+
+ nab = (bundle->dst.field
+ ? ofputil_put_NXAST_BUNDLE_LOAD(openflow)
+ : ofputil_put_NXAST_BUNDLE(openflow));
+ nab->len = htons(ntohs(nab->len) + slaves_len);
+ nab->algorithm = htons(bundle->algorithm);
+ nab->fields = htons(bundle->fields);
+ nab->basis = htons(bundle->basis);
+ nab->slave_type = htonl(NXM_OF_IN_PORT);
+ nab->n_slaves = htons(bundle->n_slaves);
+ if (bundle->dst.field) {
+ nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs,
+ bundle->dst.n_bits);
+ nab->dst = htonl(bundle->dst.field->nxm_header);
+ }
+
+ slaves = ofpbuf_put_zeros(openflow, slaves_len);
+ for (i = 0; i < bundle->n_slaves; i++) {
+ slaves[i] = htons(bundle->slaves[i]);
+ }
}
/* Helper for bundle_parse and bundle_parse_load. */
static void
-bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr,
+bundle_parse__(const char *s, char **save_ptr,
const char *fields, const char *basis, const char *algorithm,
- const char *slave_type, const char *dst_s,
- const char *slave_delim)
+ const char *slave_type, const char *dst,
+ const char *slave_delim, struct ofpbuf *ofpacts)
{
- enum ofputil_action_code code;
- struct nx_action_bundle *nab;
- uint16_t n_slaves;
+ struct ofpact_bundle *bundle;
if (!slave_delim) {
ovs_fatal(0, "%s: not enough arguments to bundle action", s);
s, slave_delim);
}
- code = dst_s ? OFPUTIL_NXAST_BUNDLE_LOAD : OFPUTIL_NXAST_BUNDLE;
- b->l2 = ofputil_put_action(code, b);
+ bundle = ofpact_put_BUNDLE(ofpacts);
- n_slaves = 0;
for (;;) {
- ovs_be16 slave_be;
+ uint16_t slave_port;
char *slave;
slave = strtok_r(NULL, ", [", save_ptr);
- if (!slave || n_slaves >= BUNDLE_MAX_SLAVES) {
+ if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
break;
}
- slave_be = htons(atoi(slave));
- ofpbuf_put(b, &slave_be, sizeof slave_be);
+ slave_port = atoi(slave);
+ ofpbuf_put(ofpacts, &slave_port, sizeof slave_port);
- n_slaves++;
+ bundle = ofpacts->l2;
+ bundle->n_slaves++;
}
+ ofpact_update_len(ofpacts, &bundle->ofpact);
- /* Slaves array must be multiple of 8 bytes long. */
- if (b->size % 8) {
- ofpbuf_put_zeros(b, 8 - (b->size % 8));
- }
-
- nab = b->l2;
- nab->len = htons(b->size - ((char *) b->l2 - (char *) b->data));
- nab->n_slaves = htons(n_slaves);
- nab->basis = htons(atoi(basis));
+ bundle->basis = atoi(basis);
if (!strcasecmp(fields, "eth_src")) {
- nab->fields = htons(NX_HASH_FIELDS_ETH_SRC);
+ bundle->fields = NX_HASH_FIELDS_ETH_SRC;
} else if (!strcasecmp(fields, "symmetric_l4")) {
- nab->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4);
+ bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
} else {
ovs_fatal(0, "%s: unknown fields `%s'", s, fields);
}
if (!strcasecmp(algorithm, "active_backup")) {
- nab->algorithm = htons(NX_BD_ALG_ACTIVE_BACKUP);
+ bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
} else if (!strcasecmp(algorithm, "hrw")) {
- nab->algorithm = htons(NX_BD_ALG_HRW);
+ bundle->algorithm = NX_BD_ALG_HRW;
} else {
ovs_fatal(0, "%s: unknown algorithm `%s'", s, algorithm);
}
- if (!strcasecmp(slave_type, "ofport")) {
- nab->slave_type = htonl(NXM_OF_IN_PORT);
- } else {
+ if (strcasecmp(slave_type, "ofport")) {
ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type);
}
- if (dst_s) {
- struct mf_subfield dst;
-
- mf_parse_subfield(&dst, dst_s);
- nab->dst = htonl(dst.field->nxm_header);
- nab->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
+ if (dst) {
+ mf_parse_subfield(&bundle->dst, dst);
}
-
- b->l2 = NULL;
}
/* Converts a bundle action string contained in 's' to an nx_action_bundle and
* stores it in 'b'. Sets 'b''s l2 pointer to NULL. */
void
-bundle_parse(struct ofpbuf *b, const char *s)
+bundle_parse(const char *s, struct ofpbuf *ofpacts)
{
char *fields, *basis, *algorithm, *slave_type, *slave_delim;
char *tokstr, *save_ptr;
slave_type = strtok_r(NULL, ", ", &save_ptr);
slave_delim = strtok_r(NULL, ": ", &save_ptr);
- bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
- slave_delim);
+ bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
+ slave_delim, ofpacts);
free(tokstr);
}
/* Converts a bundle_load action string contained in 's' to an nx_action_bundle
* and stores it in 'b'. Sets 'b''s l2 pointer to NULL. */
void
-bundle_parse_load(struct ofpbuf *b, const char *s)
+bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
{
char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim;
char *tokstr, *save_ptr;
dst = strtok_r(NULL, ", ", &save_ptr);
slave_delim = strtok_r(NULL, ": ", &save_ptr);
- bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, dst,
- slave_delim);
+ bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, dst,
+ slave_delim, ofpacts);
free(tokstr);
}
/* Appends a human-readable representation of 'nab' to 's'. */
void
-bundle_format(const struct nx_action_bundle *nab, struct ds *s)
+bundle_format(const struct ofpact_bundle *bundle, struct ds *s)
{
- const char *action, *fields, *algorithm, *slave_type;
+ const char *action, *fields, *algorithm;
size_t i;
- fields = flow_hash_fields_to_str(ntohs(nab->fields));
+ fields = flow_hash_fields_to_str(bundle->fields);
- switch (ntohs(nab->algorithm)) {
+ switch (bundle->algorithm) {
case NX_BD_ALG_HRW:
algorithm = "hrw";
break;
algorithm = "<unknown>";
}
- switch (ntohl(nab->slave_type)) {
- case NXM_OF_IN_PORT:
- slave_type = "ofport";
- break;
- default:
- slave_type = "<unknown>";
- }
-
- switch (ntohs(nab->subtype)) {
- case NXAST_BUNDLE:
- action = "bundle";
- break;
- case NXAST_BUNDLE_LOAD:
- action = "bundle_load";
- break;
- default:
- NOT_REACHED();
- }
+ action = bundle->dst.field ? "bundle_load" : "bundle";
ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields,
- ntohs(nab->basis), algorithm, slave_type);
-
- if (nab->subtype == htons(NXAST_BUNDLE_LOAD)) {
- struct mf_subfield dst;
+ bundle->basis, algorithm, "ofport");
- nxm_decode(&dst, nab->dst, nab->ofs_nbits);
- mf_format_subfield(&dst, s);
+ if (bundle->dst.field) {
+ mf_format_subfield(&bundle->dst, s);
ds_put_cstr(s, ",");
}
ds_put_cstr(s, "slaves:");
- for (i = 0; i < ntohs(nab->n_slaves); i++) {
+ for (i = 0; i < bundle->n_slaves; i++) {
if (i) {
ds_put_cstr(s, ",");
}
- ds_put_format(s, "%"PRIu16, bundle_get_slave(nab, i));
+ ds_put_format(s, "%"PRIu16, bundle->slaves[i]);
}
ds_put_cstr(s, ")");
-/* Copyright (c) 2011 Nicira Networks.
+/* Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ds;
struct flow;
+struct ofpact_bundle;
struct ofpbuf;
/* NXAST_BUNDLE helper functions.
*
* See include/openflow/nicira-ext.h for NXAST_BUNDLE specification. */
-uint16_t bundle_execute(const struct nx_action_bundle *, const struct flow *,
+uint16_t bundle_execute(const struct ofpact_bundle *, const struct flow *,
bool (*slave_enabled)(uint16_t ofp_port, void *aux),
void *aux);
-void bundle_execute_load(const struct nx_action_bundle *, struct flow *,
- bool (*slave_enabled)(uint16_t ofp_port, void *aux),
- void *aux);
-enum ofperr bundle_check(const struct nx_action_bundle *, int max_ports,
+enum ofperr bundle_from_openflow(const struct nx_action_bundle *,
+ struct ofpbuf *ofpact);
+enum ofperr bundle_check(const struct ofpact_bundle *, int max_ports,
const struct flow *);
-void bundle_parse(struct ofpbuf *, const char *);
-void bundle_parse_load(struct ofpbuf *b, const char *);
-void bundle_format(const struct nx_action_bundle *, struct ds *);
-
-/* Returns the 'i'th slave in 'nab'. */
-static inline uint16_t
-bundle_get_slave(const struct nx_action_bundle *nab, size_t i)
-{
- return ntohs(((ovs_be16 *)(nab + 1))[i]);
-}
+void bundle_to_nxast(const struct ofpact_bundle *, struct ofpbuf *of10);
+void bundle_parse(const char *, struct ofpbuf *ofpacts);
+void bundle_parse_load(const char *, struct ofpbuf *ofpacts);
+void bundle_format(const struct ofpact_bundle *, struct ds *);
#endif /* bundle.h */
/*
- * Copyright (c) 2008, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2008, 2009 Nicira Networks, Inc.
+/* Copyright (c) 2008, 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2008, 2009 Nicira Networks, Inc.
+/* Copyright (c) 2008, 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#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;
uint64_t mpid;
bool extended; /* Extended mode. */
+ bool booted; /* A full fault interval has occured. */
enum cfm_fault_reason fault; /* Connectivity fault status. */
enum cfm_fault_reason recv_fault; /* Bit mask of faults occuring on
receive. */
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
uint32_t seq; /* Most recently received sequence number. */
uint8_t num_health_ccm; /* Number of received ccm frames every
CFM_HEALTH_INTERVAL * 'fault_interval'. */
+ long long int last_rx; /* Last CCM reception time. */
};
}
static void
-ds_put_cfm_fault(struct ds *ds, int old_fault, int new_fault)
+ds_put_cfm_fault(struct ds *ds, int fault)
{
int i;
for (i = 0; i < CFM_FAULT_N_REASONS; i++) {
int reason = 1 << i;
- if ((old_fault | new_fault) & reason) {
- ds_put_format(ds, " %s%s",
- (!(old_fault & reason) ? "+"
- : !(new_fault & reason) ? "-"
- : ""),
- cfm_fault_reason_to_str(reason));
+ if (fault & reason) {
+ ds_put_format(ds, "%s ", cfm_fault_reason_to_str(reason));
}
}
+
+ ds_chomp(ds, ' ');
}
static void
cfm->remote_opup = true;
cfm->fault_override = -1;
cfm->health = -1;
+ cfm->last_tx = 0;
return cfm;
}
HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) {
if (!rmp->recv) {
- VLOG_DBG("%s: no CCM from RMP %"PRIu64" in the last %lldms",
- cfm->name, rmp->mpid, interval);
+ VLOG_INFO("%s: Received no CCM from RMP %"PRIu64" in the last"
+ " %lldms", cfm->name, rmp->mpid,
+ time_msec() - rmp->last_rx);
hmap_remove(&cfm->remote_mps, &rmp->node);
free(rmp);
} else {
cfm->fault |= CFM_FAULT_RECV;
}
- if (old_cfm_fault != cfm->fault) {
+ if (old_cfm_fault != cfm->fault && !VLOG_DROP_INFO(&rl)) {
struct ds ds = DS_EMPTY_INITIALIZER;
- ds_put_cfm_fault(&ds, old_cfm_fault, cfm->fault);
- VLOG_INFO_RL(&rl, "%s: CFM fault status changed:%s", cfm->name,
- ds_cstr_ro(&ds));
+ ds_put_cstr(&ds, "from [");
+ ds_put_cfm_fault(&ds, old_cfm_fault);
+ ds_put_cstr(&ds, "] to [");
+ ds_put_cfm_fault(&ds, cfm->fault);
+ ds_put_char(&ds, ']');
+ VLOG_INFO("%s: CFM faults changed %s.", cfm->name, ds_cstr(&ds));
ds_destroy(&ds);
}
+ cfm->booted = true;
timer_set_duration(&cfm->fault_timer, interval);
+ VLOG_DBG("%s: new fault interval", cfm->name);
}
}
if (cfm->ccm_interval == 0) {
assert(cfm->extended);
ccm->interval_ms_x = htons(cfm->ccm_interval_ms);
+ } else {
+ ccm->interval_ms_x = htons(0);
}
- if (hmap_is_empty(&cfm->remote_mps)) {
+ if (cfm->booted && hmap_is_empty(&cfm->remote_mps)) {
ccm->flags |= CCM_RDI_MASK;
}
+
+ if (cfm->last_tx) {
+ long long int delay = time_msec() - cfm->last_tx;
+ if (delay > (cfm->ccm_interval_ms * 3 / 2)) {
+ VLOG_WARN("%s: long delay of %lldms (expected %dms) sending CCM"
+ " seq %"PRIu32, cfm->name, delay, cfm->ccm_interval_ms,
+ cfm->seq);
+ }
+ }
+ cfm->last_tx = time_msec();
}
void
if (ccm_rdi) {
cfm_fault |= CFM_FAULT_RDI;
VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu64, cfm->name,
- rmp->mpid);
+ ccm_mpid);
}
VLOG_DBG("%s: received CCM (seq %"PRIu32") (mpid %"PRIu64")"
}
if (rmp->seq && ccm_seq != (rmp->seq + 1)) {
- cfm_fault |= CFM_FAULT_SEQUENCE;
VLOG_WARN_RL(&rl, "%s: (mpid %"PRIu64") detected sequence"
" numbers which indicate possible connectivity"
" problems (previous %"PRIu32") (current %"PRIu32
cfm->recv_fault |= cfm_fault;
rmp->seq = ccm_seq;
rmp->opup = !ccm_opdown;
+ rmp->last_rx = time_msec();
}
}
}
/* Gets the operational state of 'cfm'. 'cfm' is considered operationally down
* if it has received a CCM with the operationally down bit set from any of its
- * remote maintenance points. Returns true if 'cfm' is operationally up. False
- * otherwise. */
-bool
+ * remote maintenance points. Returns 1 if 'cfm' is operationally up, 0 if
+ * 'cfm' is operationally down, or -1 if 'cfm' has no operational state
+ * (because it isn't in extended mode). */
+int
cfm_get_opup(const struct cfm *cfm)
{
- return cfm->remote_opup;
+ if (cfm->extended) {
+ return cfm->remote_opup;
+ } else {
+ return -1;
+ }
}
/* Populates 'rmps' with an array of remote maintenance points reachable by
fault = cfm_get_fault(cfm);
if (fault) {
- ds_put_cstr(ds, "\tfault:");
- ds_put_cfm_fault(ds, fault, fault);
+ ds_put_cstr(ds, "\tfault: ");
+ ds_put_cfm_fault(ds, fault);
ds_put_cstr(ds, "\n");
}
-/* Copyright (c) 2010, 2011 Nicira Networks.
+/* Copyright (c) 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
CFM_FAULT_REASON(LOOPBACK, loopback) \
CFM_FAULT_REASON(OVERFLOW, overflow) \
CFM_FAULT_REASON(OVERRIDE, override) \
- CFM_FAULT_REASON(INTERVAL, interval) \
- CFM_FAULT_REASON(SEQUENCE, sequence)
+ CFM_FAULT_REASON(INTERVAL, interval)
enum cfm_fault_bit_index {
#define CFM_FAULT_REASON(NAME, STR) CFM_FAULT_INDEX_##NAME,
void cfm_process_heartbeat(struct cfm *, const struct ofpbuf *packet);
int cfm_get_fault(const struct cfm *);
int cfm_get_health(const struct cfm *);
-bool cfm_get_opup(const struct cfm *);
+int cfm_get_opup(const struct cfm *);
void cfm_get_remote_mpids(const struct cfm *, const uint64_t **rmps,
size_t *n_rmps);
const char *cfm_fault_reason_to_str(int fault);
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
rule->flow.regs[reg_idx] = value & mask;
}
+void
+cls_rule_set_metadata(struct cls_rule *rule, ovs_be64 metadata)
+{
+ cls_rule_set_metadata_masked(rule, metadata, htonll(UINT64_MAX));
+}
+
+void
+cls_rule_set_metadata_masked(struct cls_rule *rule, ovs_be64 metadata,
+ ovs_be64 mask)
+{
+ rule->wc.metadata_mask = mask;
+ rule->flow.metadata = metadata & mask;
+}
+
void
cls_rule_set_tun_id(struct cls_rule *rule, ovs_be64 tun_id)
{
rule->flow.dl_type = dl_type;
}
+/* Modifies 'value_src' so that the Ethernet address must match
+ * 'value_dst' exactly. 'mask_dst' is set to all 1s */
+static void
+cls_rule_set_eth(const uint8_t value_src[ETH_ADDR_LEN],
+ uint8_t value_dst[ETH_ADDR_LEN],
+ uint8_t mask_dst[ETH_ADDR_LEN])
+{
+ memcpy(value_dst, value_src, ETH_ADDR_LEN);
+ memset(mask_dst, 0xff, ETH_ADDR_LEN);
+}
+
+/* Modifies 'value_src' so that the Ethernet address must match
+ * 'value_src' after each byte is ANDed with the appropriate byte in
+ * 'mask_src'. 'mask_dst' is set to 'mask_src' */
+static void
+cls_rule_set_eth_masked(const uint8_t value_src[ETH_ADDR_LEN],
+ const uint8_t mask_src[ETH_ADDR_LEN],
+ uint8_t value_dst[ETH_ADDR_LEN],
+ uint8_t mask_dst[ETH_ADDR_LEN])
+{
+ size_t i;
+
+ for (i = 0; i < ETH_ADDR_LEN; i++) {
+ value_dst[i] = value_src[i] & mask_src[i];
+ mask_dst[i] = mask_src[i];
+ }
+}
+
+/* Modifies 'rule' so that the source Ethernet address
+ * must match 'dl_src' exactly. */
void
cls_rule_set_dl_src(struct cls_rule *rule, const uint8_t dl_src[ETH_ADDR_LEN])
{
- rule->wc.wildcards &= ~FWW_DL_SRC;
- memcpy(rule->flow.dl_src, dl_src, ETH_ADDR_LEN);
+ cls_rule_set_eth(dl_src, rule->flow.dl_src, rule->wc.dl_src_mask);
+}
+
+/* Modifies 'rule' so that the source Ethernet address
+ * must match 'dl_src' after each byte is ANDed with
+ * the appropriate byte in 'mask'. */
+void
+cls_rule_set_dl_src_masked(struct cls_rule *rule,
+ const uint8_t dl_src[ETH_ADDR_LEN],
+ const uint8_t mask[ETH_ADDR_LEN])
+{
+ cls_rule_set_eth_masked(dl_src, mask,
+ rule->flow.dl_src, rule->wc.dl_src_mask);
}
-/* Modifies 'rule' so that the Ethernet address must match 'dl_dst' exactly. */
+/* Modifies 'rule' so that the destination Ethernet address
+ * must match 'dl_dst' exactly. */
void
cls_rule_set_dl_dst(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN])
{
- rule->wc.wildcards &= ~(FWW_DL_DST | FWW_ETH_MCAST);
- memcpy(rule->flow.dl_dst, dl_dst, ETH_ADDR_LEN);
+ cls_rule_set_eth(dl_dst, rule->flow.dl_dst, rule->wc.dl_dst_mask);
}
-/* Modifies 'rule' so that the Ethernet address must match 'dl_dst' after each
- * byte is ANDed with the appropriate byte in 'mask'.
- *
- * This function will assert-fail if 'mask' is invalid. Only 'mask' values
- * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */
+/* Modifies 'rule' so that the destination Ethernet address
+ * must match 'dl_src' after each byte is ANDed with
+ * the appropriate byte in 'mask'. */
void
cls_rule_set_dl_dst_masked(struct cls_rule *rule,
const uint8_t dl_dst[ETH_ADDR_LEN],
const uint8_t mask[ETH_ADDR_LEN])
{
- flow_wildcards_t *wc = &rule->wc.wildcards;
- size_t i;
-
- *wc = flow_wildcards_set_dl_dst_mask(*wc, mask);
- for (i = 0; i < ETH_ADDR_LEN; i++) {
- rule->flow.dl_dst[i] = dl_dst[i] & mask[i];
- }
+ cls_rule_set_eth_masked(dl_dst, mask,
+ rule->flow.dl_dst, rule->wc.dl_dst_mask);
}
void
void
cls_rule_set_dl_vlan(struct cls_rule *rule, ovs_be16 dl_vlan)
{
- flow_set_vlan_vid(&rule->flow, dl_vlan);
- if (dl_vlan == htons(OFP_VLAN_NONE)) {
+ flow_set_dl_vlan(&rule->flow, dl_vlan);
+ if (dl_vlan == htons(OFP10_VLAN_NONE)) {
rule->wc.vlan_tci_mask = htons(UINT16_MAX);
} else {
rule->wc.vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
}
}
+/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI). */
+void
+cls_rule_set_vlan_vid(struct cls_rule *rule, ovs_be16 vid)
+{
+ cls_rule_set_vlan_vid_masked(rule, vid, htons(VLAN_VID_MASK | VLAN_CFI));
+}
+
+
+/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI), with the corresponding 'mask'. */
+void
+cls_rule_set_vlan_vid_masked(struct cls_rule *rule,
+ ovs_be16 vid, ovs_be16 mask)
+{
+ ovs_be16 pcp_mask = htons(VLAN_PCP_MASK);
+ ovs_be16 vid_mask = htons(VLAN_VID_MASK | VLAN_CFI);
+
+ mask &= vid_mask;
+ flow_set_vlan_vid(&rule->flow, vid & mask);
+ rule->wc.vlan_tci_mask = mask | (rule->wc.vlan_tci_mask & pcp_mask);
+}
+
/* Modifies 'rule' so that the VLAN PCP is wildcarded. If the VID is already
* wildcarded, then 'rule' will match a packet regardless of whether it has an
* 802.1Q header or not. */
void
cls_rule_set_arp_sha(struct cls_rule *rule, const uint8_t sha[ETH_ADDR_LEN])
{
- rule->wc.wildcards &= ~FWW_ARP_SHA;
- memcpy(rule->flow.arp_sha, sha, ETH_ADDR_LEN);
+ cls_rule_set_eth(sha, rule->flow.arp_sha, rule->wc.arp_sha_mask);
+}
+
+void
+cls_rule_set_arp_sha_masked(struct cls_rule *rule,
+ const uint8_t arp_sha[ETH_ADDR_LEN],
+ const uint8_t mask[ETH_ADDR_LEN])
+{
+ cls_rule_set_eth_masked(arp_sha, mask,
+ rule->flow.arp_sha, rule->wc.arp_sha_mask);
}
void
cls_rule_set_arp_tha(struct cls_rule *rule, const uint8_t tha[ETH_ADDR_LEN])
{
- rule->wc.wildcards &= ~FWW_ARP_THA;
- memcpy(rule->flow.arp_tha, tha, ETH_ADDR_LEN);
+ cls_rule_set_eth(tha, rule->flow.arp_tha, rule->wc.arp_tha_mask);
+}
+
+void
+cls_rule_set_arp_tha_masked(struct cls_rule *rule,
+ const uint8_t arp_tha[ETH_ADDR_LEN],
+ const uint8_t mask[ETH_ADDR_LEN])
+{
+ cls_rule_set_eth_masked(arp_tha, mask,
+ rule->flow.arp_tha, rule->wc.arp_tha_mask);
}
void
void
cls_rule_set_ipv6_label(struct cls_rule *rule, ovs_be32 ipv6_label)
{
- rule->wc.wildcards &= ~FWW_IPV6_LABEL;
- rule->flow.ipv6_label = ipv6_label;
+ cls_rule_set_ipv6_label_masked(rule, ipv6_label, htonl(UINT32_MAX));
+}
+
+void
+cls_rule_set_ipv6_label_masked(struct cls_rule *rule, ovs_be32 ipv6_label,
+ ovs_be32 mask)
+{
+ rule->flow.ipv6_label = ipv6_label & mask;
+ rule->wc.ipv6_label_mask = mask;
}
void
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)
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
if (rule->priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%d,", rule->priority);
ntohll(f->tun_id), ntohll(wc->tun_id_mask));
break;
}
+ switch (wc->metadata_mask) {
+ case 0:
+ break;
+ case CONSTANT_HTONLL(UINT64_MAX):
+ ds_put_format(s, "metadata=%#"PRIx64",", ntohll(f->metadata));
+ break;
+ default:
+ ds_put_format(s, "metadata=%#"PRIx64"/%#"PRIx64",",
+ ntohll(f->metadata), ntohll(wc->metadata_mask));
+ break;
+ }
if (!(w & FWW_IN_PORT)) {
ds_put_format(s, "in_port=%"PRIu16",", f->in_port);
}
ntohs(f->vlan_tci), ntohs(wc->vlan_tci_mask));
}
}
- if (!(w & FWW_DL_SRC)) {
- ds_put_format(s, "dl_src="ETH_ADDR_FMT",", ETH_ADDR_ARGS(f->dl_src));
- }
- switch (w & (FWW_DL_DST | FWW_ETH_MCAST)) {
- case 0:
- ds_put_format(s, "dl_dst="ETH_ADDR_FMT",", ETH_ADDR_ARGS(f->dl_dst));
- break;
- case FWW_DL_DST:
- ds_put_format(s, "dl_dst="ETH_ADDR_FMT"/01:00:00:00:00:00,",
- ETH_ADDR_ARGS(f->dl_dst));
- break;
- case FWW_ETH_MCAST:
- ds_put_format(s, "dl_dst="ETH_ADDR_FMT"/fe:ff:ff:ff:ff:ff,",
- ETH_ADDR_ARGS(f->dl_dst));
- break;
- case FWW_DL_DST | FWW_ETH_MCAST:
- break;
- }
+ format_eth_masked(s, "dl_src", f->dl_src, wc->dl_src_mask);
+ format_eth_masked(s, "dl_dst", f->dl_dst, wc->dl_dst_mask);
if (!skip_type && !(w & FWW_DL_TYPE)) {
ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type));
}
if (f->dl_type == htons(ETH_TYPE_IPV6)) {
format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->ipv6_src_mask);
format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->ipv6_dst_mask);
- if (!(w & FWW_IPV6_LABEL)) {
- ds_put_format(s, "ipv6_label=0x%05"PRIx32",", ntohl(f->ipv6_label));
+ if (wc->ipv6_label_mask) {
+ if (wc->ipv6_label_mask == htonl(UINT32_MAX)) {
+ ds_put_format(s, "ipv6_label=0x%05"PRIx32",",
+ ntohl(f->ipv6_label));
+ } else {
+ ds_put_format(s, "ipv6_label=0x%05"PRIx32"/0x%05"PRIx32",",
+ ntohl(f->ipv6_label),
+ ntohl(wc->ipv6_label_mask));
+ }
}
} else {
format_ip_netmask(s, "nw_src", f->nw_src, wc->nw_src_mask);
}
}
if (f->dl_type == htons(ETH_TYPE_ARP)) {
- if (!(w & FWW_ARP_SHA)) {
- ds_put_format(s, "arp_sha="ETH_ADDR_FMT",",
- ETH_ADDR_ARGS(f->arp_sha));
- }
- if (!(w & FWW_ARP_THA)) {
- ds_put_format(s, "arp_tha="ETH_ADDR_FMT",",
- ETH_ADDR_ARGS(f->arp_tha));
- }
+ format_eth_masked(s, "arp_sha", f->arp_sha, wc->arp_sha_mask);
+ format_eth_masked(s, "arp_tha", f->arp_tha, wc->arp_tha_mask);
}
if (!(w & FWW_NW_DSCP)) {
ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos & IP_DSCP_MASK);
format_be16_masked(s, "icmp_code", f->tp_dst, wc->tp_dst_mask);
format_ipv6_netmask(s, "nd_target", &f->nd_target,
&wc->nd_target_mask);
- if (!(w & FWW_ARP_SHA)) {
- ds_put_format(s, "nd_sll="ETH_ADDR_FMT",",
- ETH_ADDR_ARGS(f->arp_sha));
- }
- if (!(w & FWW_ARP_THA)) {
- ds_put_format(s, "nd_tll="ETH_ADDR_FMT",",
- ETH_ADDR_ARGS(f->arp_tha));
- }
+ format_eth_masked(s, "nd_sll", f->arp_sha, wc->arp_sha_mask);
+ format_eth_masked(s, "nd_tll", f->arp_tha, wc->arp_tha_mask);
} else {
format_be16_masked(s, "tp_src", f->tp_src, wc->tp_src_mask);
format_be16_masked(s, "tp_dst", f->tp_dst, wc->tp_dst_mask);
return false;
}
+
+/* Returns true if 'rule' exactly matches 'criteria' or if 'rule' is more
+ * specific than 'criteria'. That is, 'rule' matches 'criteria' and this
+ * function returns true if, for every field:
+ *
+ * - 'criteria' and 'rule' specify the same (non-wildcarded) value for the
+ * field, or
+ *
+ * - 'criteria' wildcards the field,
+ *
+ * Conversely, 'rule' does not match 'criteria' and this function returns false
+ * if, for at least one field:
+ *
+ * - 'criteria' and 'rule' specify different values for the field, or
+ *
+ * - 'criteria' specifies a value for the field but 'rule' wildcards it.
+ *
+ * Equivalently, the truth table for whether a field matches is:
+ *
+ * rule
+ *
+ * c wildcard exact
+ * r +---------+---------+
+ * i wild | yes | yes |
+ * t card | | |
+ * e +---------+---------+
+ * r exact | no |if values|
+ * i | |are equal|
+ * a +---------+---------+
+ *
+ * This is the matching rule used by OpenFlow 1.0 non-strict OFPT_FLOW_MOD
+ * commands and by OpenFlow 1.0 aggregate and flow stats.
+ *
+ * Ignores rule->priority and criteria->priority. */
+bool
+cls_rule_is_loose_match(const struct cls_rule *rule,
+ const struct cls_rule *criteria)
+{
+ return (!flow_wildcards_has_extra(&rule->wc, &criteria->wc)
+ && flow_equal_except(&rule->flow, &criteria->flow, &criteria->wc));
+}
\f
/* Iteration. */
return NULL;
}
-/* Initializes 'cursor' for iterating through 'cls' rules that exactly match
- * 'target' or are more specific than 'target'. That is, a given 'rule'
- * matches 'target' if, for every field:
+/* Initializes 'cursor' for iterating through rules in 'cls':
*
- * - 'target' and 'rule' specify the same (non-wildcarded) value for the
- * field, or
- *
- * - 'target' wildcards the field,
- *
- * but not if:
- *
- * - 'target' and 'rule' specify different values for the field, or
- *
- * - 'target' specifies a value for the field but 'rule' wildcards it.
- *
- * Equivalently, the truth table for whether a field matches is:
- *
- * rule
- *
- * wildcard exact
- * +---------+---------+
- * t wild | yes | yes |
- * a card | | |
- * r +---------+---------+
- * g exact | no |if values|
- * e | |are equal|
- * t +---------+---------+
- *
- * This is the matching rule used by OpenFlow 1.0 non-strict OFPT_FLOW_MOD
- * commands and by OpenFlow 1.0 aggregate and flow stats.
+ * - If 'target' is null, the cursor will visit every rule in 'cls'.
*
- * Ignores target->priority.
+ * - If 'target' is nonnull, the cursor will visit each 'rule' in 'cls'
+ * such that cls_rule_is_loose_match(rule, target) returns true.
*
- * 'target' may be NULL to iterate over every rule in 'cls'. */
+ * Ignores target->priority. */
void
cls_cursor_init(struct cls_cursor *cursor, const struct classifier *cls,
const struct cls_rule *target)
const flow_wildcards_t wc = wildcards->wildcards;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
for (i = 0; i < FLOW_N_REGS; i++) {
if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
}
return (!((a->tun_id ^ b->tun_id) & wildcards->tun_id_mask)
+ && !((a->metadata ^ b->metadata) & wildcards->metadata_mask)
&& !((a->nw_src ^ b->nw_src) & wildcards->nw_src_mask)
&& !((a->nw_dst ^ b->nw_dst) & wildcards->nw_dst_mask)
&& (wc & FWW_IN_PORT || a->in_port == b->in_port)
&& (wc & FWW_DL_TYPE || a->dl_type == b->dl_type)
&& !((a->tp_src ^ b->tp_src) & wildcards->tp_src_mask)
&& !((a->tp_dst ^ b->tp_dst) & wildcards->tp_dst_mask)
- && (wc & FWW_DL_SRC || eth_addr_equals(a->dl_src, b->dl_src))
- && (wc & FWW_DL_DST
- || (!((a->dl_dst[0] ^ b->dl_dst[0]) & 0xfe)
- && a->dl_dst[1] == b->dl_dst[1]
- && a->dl_dst[2] == b->dl_dst[2]
- && a->dl_dst[3] == b->dl_dst[3]
- && a->dl_dst[4] == b->dl_dst[4]
- && a->dl_dst[5] == b->dl_dst[5]))
- && (wc & FWW_ETH_MCAST
- || !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
+ && eth_addr_equal_except(a->dl_src, b->dl_src,
+ wildcards->dl_src_mask)
+ && eth_addr_equal_except(a->dl_dst, b->dl_dst,
+ wildcards->dl_dst_mask)
&& (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
&& (wc & FWW_NW_TTL || a->nw_ttl == b->nw_ttl)
&& (wc & FWW_NW_DSCP || !((a->nw_tos ^ b->nw_tos) & IP_DSCP_MASK))
&& (wc & FWW_NW_ECN || !((a->nw_tos ^ b->nw_tos) & IP_ECN_MASK))
&& !((a->nw_frag ^ b->nw_frag) & wildcards->nw_frag_mask)
- && (wc & FWW_ARP_SHA || eth_addr_equals(a->arp_sha, b->arp_sha))
- && (wc & FWW_ARP_THA || eth_addr_equals(a->arp_tha, b->arp_tha))
- && (wc & FWW_IPV6_LABEL || a->ipv6_label == b->ipv6_label)
+ && eth_addr_equal_except(a->arp_sha, b->arp_sha,
+ wildcards->arp_sha_mask)
+ && eth_addr_equal_except(a->arp_tha, b->arp_tha,
+ wildcards->arp_tha_mask)
+ && !((a->ipv6_label ^ b->ipv6_label) & wildcards->ipv6_label_mask)
&& ipv6_equal_except(&a->ipv6_src, &b->ipv6_src,
&wildcards->ipv6_src_mask)
&& ipv6_equal_except(&a->ipv6_dst, &b->ipv6_dst,
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void cls_rule_zero_wildcarded_fields(struct cls_rule *);
+bool cls_rule_is_loose_match(const struct cls_rule *rule,
+ const struct cls_rule *criteria);
+
void cls_rule_set_reg(struct cls_rule *, unsigned int reg_idx, uint32_t value);
void cls_rule_set_reg_masked(struct cls_rule *, unsigned int reg_idx,
uint32_t value, uint32_t mask);
+void cls_rule_set_metadata(struct cls_rule *, ovs_be64 metadata);
+void cls_rule_set_metadata_masked(struct cls_rule *, ovs_be64 metadata,
+ ovs_be64 mask);
void cls_rule_set_tun_id(struct cls_rule *, ovs_be64 tun_id);
void cls_rule_set_tun_id_masked(struct cls_rule *,
ovs_be64 tun_id, ovs_be64 mask);
void cls_rule_set_in_port(struct cls_rule *, uint16_t ofp_port);
void cls_rule_set_dl_type(struct cls_rule *, ovs_be16);
void cls_rule_set_dl_src(struct cls_rule *, const uint8_t[6]);
+void cls_rule_set_dl_src_masked(struct cls_rule *, const uint8_t dl_src[6],
+ const uint8_t mask[6]);
void cls_rule_set_dl_dst(struct cls_rule *, const uint8_t[6]);
void cls_rule_set_dl_dst_masked(struct cls_rule *, const uint8_t dl_dst[6],
const uint8_t mask[6]);
ovs_be16 tci, ovs_be16 mask);
void cls_rule_set_any_vid(struct cls_rule *);
void cls_rule_set_dl_vlan(struct cls_rule *, ovs_be16);
+void cls_rule_set_vlan_vid(struct cls_rule *, ovs_be16);
+void cls_rule_set_vlan_vid_masked(struct cls_rule *,
+ ovs_be16 vid, ovs_be16 mask);
void cls_rule_set_any_pcp(struct cls_rule *);
void cls_rule_set_dl_vlan_pcp(struct cls_rule *, uint8_t);
void cls_rule_set_tp_src(struct cls_rule *, ovs_be16);
void cls_rule_set_icmp_type(struct cls_rule *, uint8_t);
void cls_rule_set_icmp_code(struct cls_rule *, uint8_t);
void cls_rule_set_arp_sha(struct cls_rule *, const uint8_t[6]);
+void cls_rule_set_arp_sha_masked(struct cls_rule *, const uint8_t[6],
+ const uint8_t [6]);
void cls_rule_set_arp_tha(struct cls_rule *, const uint8_t[6]);
+void cls_rule_set_arp_tha_masked(struct cls_rule *, const uint8_t[6],
+ const uint8_t [6]);
void cls_rule_set_ipv6_src(struct cls_rule *, const struct in6_addr *);
void cls_rule_set_ipv6_src_masked(struct cls_rule *, const struct in6_addr *,
const struct in6_addr *);
void cls_rule_set_ipv6_dst_masked(struct cls_rule *, const struct in6_addr *,
const struct in6_addr *);
void cls_rule_set_ipv6_label(struct cls_rule *, ovs_be32);
+void cls_rule_set_ipv6_label_masked(struct cls_rule *, ovs_be32, ovs_be32);
void cls_rule_set_nd_target(struct cls_rule *, const struct in6_addr *);
void cls_rule_set_nd_target_masked(struct cls_rule *, const struct in6_addr *,
const struct in6_addr *);
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#define SENTINEL(N)
#endif
+/* ISO C says that a C implementation may choose any integer type for an enum
+ * that is sufficient to hold all of its values. Common ABIs (such as the
+ * System V ABI used on i386 GNU/Linux) always use a full-sized "int", even
+ * when a smaller type would suffice.
+ *
+ * In GNU C, "enum __attribute__((packed)) name { ... }" defines 'name' as an
+ * enum compatible with a type that is no bigger than necessary. This is the
+ * intended use of OVS_PACKED_ENUM.
+ *
+ * OVS_PACKED_ENUM is intended for use only as a space optimization, since it
+ * only works with GCC. That means that it must not be used in wire protocols
+ * or otherwise exposed outside of a single process. */
+#if __GNUC__ && !__CHECKER__
+#define OVS_PACKED_ENUM __attribute__((__packed__))
+#else
+#define OVS_PACKED_ENUM
+#endif
+
#endif /* compiler.h */
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
VLOG_DEFINE_THIS_MODULE(daemon);
/* --detach: Should we run in the background? */
-static bool detach;
+static bool detach; /* Was --detach specified? */
+static bool detached; /* Have we already detached? */
/* --pidfile: Name of pidfile (null if none). */
static char *pidfile;
daemonize_complete();
}
+/* Calls fork() and on success returns its return value. On failure, logs an
+ * error and exits unsuccessfully.
+ *
+ * Post-fork, but before returning, this function calls a few other functions
+ * that are generally useful if the child isn't planning to exec a new
+ * process. */
+pid_t
+fork_and_clean_up(void)
+{
+ pid_t pid;
+
+ pid = fork();
+ if (pid > 0) {
+ /* Running in parent process. */
+ fatal_signal_fork();
+ } else if (!pid) {
+ /* Running in child process. */
+ time_postfork();
+ lockfile_postfork();
+ } else {
+ VLOG_FATAL("fork failed (%s)", strerror(errno));
+ }
+
+ return pid;
+}
+
+/* Forks, then:
+ *
+ * - In the parent, waits for the child to signal that it has completed its
+ * startup sequence. Then stores -1 in '*fdp' and returns the child's pid.
+ *
+ * - In the child, stores a fd in '*fdp' and returns 0. The caller should
+ * pass the fd to fork_notify_startup() after it finishes its startup
+ * sequence.
+ *
+ * If something goes wrong with the fork, logs a critical error and aborts the
+ * process. */
static pid_t
fork_and_wait_for_startup(int *fdp)
{
xpipe(fds);
- pid = fork();
+ pid = fork_and_clean_up();
if (pid > 0) {
/* Running in parent process. */
size_t bytes_read;
char c;
close(fds[1]);
- fatal_signal_fork();
if (read_fully(fds[0], &c, 1, &bytes_read) != 0) {
int retval;
int status;
} else if (!pid) {
/* Running in child process. */
close(fds[0]);
- time_postfork();
- lockfile_postfork();
*fdp = fds[1];
- } else {
- VLOG_FATAL("fork failed (%s)", strerror(errno));
}
return pid;
monitor_daemon(pid_t daemon_pid)
{
/* XXX Should log daemon's stderr output at startup time. */
- const char *saved_program_name;
time_t last_restart;
char *status_msg;
int crashes;
- saved_program_name = program_name;
- program_name = xasprintf("monitor(%s)", program_name);
+ subprogram_name = "monitor";
status_msg = xstrdup("healthy");
last_restart = TIME_MIN;
crashes = 0;
int status;
proctitle_set("%s: monitoring pid %lu (%s)",
- saved_program_name, (unsigned long int) daemon_pid,
+ program_name, (unsigned long int) daemon_pid,
status_msg);
do {
/* Running in new daemon process. */
proctitle_restore();
- free((char *) program_name);
- program_name = saved_program_name;
+ subprogram_name = "";
}
/* Close standard file descriptors (except any that the client has requested we
}
/* If daemonization is configured, then this function notifies the parent
- * process that the child process has completed startup successfully.
+ * process that the child process has completed startup successfully. It also
+ * call daemonize_post_detach().
*
* Calling this function more than once has no additional effect. */
void
daemonize_complete(void)
{
- fork_notify_startup(daemonize_fd);
- daemonize_fd = -1;
+ if (!detached) {
+ detached = true;
+ fork_notify_startup(daemonize_fd);
+ daemonize_fd = -1;
+ daemonize_post_detach();
+ }
+}
+
+/* If daemonization is configured, then this function does traditional Unix
+ * daemonization behavior: join a new session, chdir to the root (if not
+ * disabled), and close the standard file descriptors.
+ *
+ * It only makes sense to call this function as part of an implementation of a
+ * special daemon subprocess. A normal daemon should just call
+ * daemonize_complete(). */
+void
+daemonize_post_detach(void)
+{
if (detach) {
setsid();
if (chdir_) {
ignore(chdir("/"));
}
close_standard_fds();
- detach = false;
}
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
pid_t read_pidfile(const char *name);
pid_t read_pidfile_if_exists(const char *name);
+pid_t fork_and_clean_up(void);
+void daemonize_post_detach(void);
+
#endif /* daemon.h */
/*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008 Nicira Networks.
+ * Copyright (c) 2008 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#line 2 "@srcdir@/lib/dirs.c.in"
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <config.h>
#include "dirs.h"
#include <stdlib.h>
+#include "util.h"
struct directory {
const char *value; /* Actual value; NULL if not yet determined. */
return get_dir(&d);
}
+const char *
+ovs_dbdir(void)
+{
+ static const char *dbdir;
+ if (!dbdir) {
+ dbdir = getenv("OVS_DBDIR");
+ if (!dbdir || !dbdir[0]) {
+ char *sysconfdir = getenv("OVS_SYSCONFDIR");
+
+ dbdir = (sysconfdir
+ ? xasprintf("%s/openvswitch", sysconfdir)
+ : @DBDIR@);
+ }
+ }
+ return dbdir;
+}
+
const char *
ovs_bindir(void)
{
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
const char *ovs_pkgdatadir(void); /* /usr/local/share/openvswitch */
const char *ovs_rundir(void); /* /usr/local/var/run/openvswitch */
const char *ovs_logdir(void); /* /usr/local/var/log/openvswitch */
+const char *ovs_dbdir(void); /* /usr/local/etc/openvswitch */
const char *ovs_bindir(void); /* /usr/local/bin */
#endif /* dirs.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "random.h"
#include "shash.h"
#include "sset.h"
+#include "timeval.h"
#include "unaligned.h"
#include "util.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(dpif_linux);
enum { MAX_PORTS = USHRT_MAX };
-enum { N_UPCALL_SOCKS = 16 };
-BUILD_ASSERT_DECL(IS_POW2(N_UPCALL_SOCKS));
-BUILD_ASSERT_DECL(N_UPCALL_SOCKS <= 32); /* We use a 32-bit word as a mask. */
+enum { N_CHANNELS = 17 };
+BUILD_ASSERT_DECL(IS_POW2(N_CHANNELS - 1));
+BUILD_ASSERT_DECL(N_CHANNELS > 1);
+BUILD_ASSERT_DECL(N_CHANNELS <= 32); /* We use a 32-bit word as a mask. */
/* This ethtool flag was introduced in Linux 2.6.24, so it might be
* missing if we have old headers. */
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. */
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
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);
}
}
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);
}
netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false);
}
- /* Loop until we find a port that isn't used. */
+ /* Unless a specific port was requested, loop until we find a port
+ * that isn't used. */
do {
uint32_t upcall_pid;
- request.port_no = ++dpif->alloc_port_no;
+ request.port_no = *port_nop != UINT16_MAX ? *port_nop
+ : ++dpif->alloc_port_no;
upcall_pid = dpif_linux_port_get_pid(dpif_, request.port_no);
request.upcall_pid = &upcall_pid;
error = dpif_linux_vport_transact(&request, &reply, &buf);
/* Older datapath has lower limit. */
max_ports = dpif->alloc_port_no;
dpif->alloc_port_no = 0;
+ } else if (error == EBUSY && *port_nop != UINT16_MAX) {
+ VLOG_INFO("%s: requested port %"PRIu16" is in use",
+ dpif_name(dpif_), *port_nop);
}
ofpbuf_delete(buf);
- } while ((i++ < max_ports)
+ } while ((*port_nop == UINT16_MAX) && (i++ < max_ports)
&& (error == EBUSY || error == EFBIG));
return error;
if (dpif->epoll_fd < 0) {
return 0;
} else {
- int idx = port_no & (N_UPCALL_SOCKS - 1);
- return nl_sock_pid(dpif->upcall_socks[idx]);
+ int idx;
+
+ idx = (port_no != UINT16_MAX
+ ? 1 + (port_no & (N_CHANNELS - 2))
+ : 0);
+ return nl_sock_pid(dpif->channels[idx].sock);
}
}
return error;
}
- dpif_port->name = (char *) vport.name;
- dpif_port->type = (char *) netdev_vport_get_netdev_type(&vport);
+ dpif_port->name = CONST_CAST(char *, vport.name);
+ dpif_port->type = CONST_CAST(char *, netdev_vport_get_netdev_type(&vport));
dpif_port->port_no = vport.port_no;
return 0;
}
dpif_linux_flow_get_stats(&reply, stats);
}
if (actionsp) {
- buf->data = (void *) reply.actions;
+ buf->data = CONST_CAST(struct nlattr *, reply.actions);
buf->size = reply.actions_len;
*actionsp = buf;
} else {
switch (op->type) {
case DPIF_OP_FLOW_PUT:
put = &op->u.flow_put;
- if (!op->error && put->stats) {
- struct dpif_linux_flow reply;
-
- op->error = dpif_linux_flow_from_ofpbuf(&reply, txn->reply);
+ if (put->stats) {
if (!op->error) {
- dpif_linux_flow_get_stats(&reply, put->stats);
+ struct dpif_linux_flow reply;
+
+ op->error = dpif_linux_flow_from_ofpbuf(&reply,
+ txn->reply);
+ if (!op->error) {
+ dpif_linux_flow_get_stats(&reply, put->stats);
+ }
+ }
+
+ if (op->error) {
+ memset(put->stats, 0, sizeof *put->stats);
}
}
break;
case DPIF_OP_FLOW_DEL:
del = &op->u.flow_del;
- if (!op->error && del->stats) {
- struct dpif_linux_flow reply;
-
- op->error = dpif_linux_flow_from_ofpbuf(&reply, txn->reply);
+ if (del->stats) {
if (!op->error) {
- dpif_linux_flow_get_stats(&reply, del->stats);
+ struct dpif_linux_flow reply;
+
+ op->error = dpif_linux_flow_from_ofpbuf(&reply,
+ txn->reply);
+ if (!op->error) {
+ dpif_linux_flow_get_stats(&reply, del->stats);
+ }
+ }
+
+ if (op->error) {
+ memset(del->stats, 0, sizeof *del->stats);
}
}
break;
}
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_);
memset(upcall, 0, sizeof *upcall);
upcall->type = type;
upcall->packet = buf;
- upcall->packet->data = (void *) nl_attr_get(a[OVS_PACKET_ATTR_PACKET]);
+ upcall->packet->data = CONST_CAST(struct nlattr *,
+ nl_attr_get(a[OVS_PACKET_ATTR_PACKET]));
upcall->packet->size = nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]);
- upcall->key = (void *) nl_attr_get(a[OVS_PACKET_ATTR_KEY]);
+ upcall->key = CONST_CAST(struct nlattr *,
+ nl_attr_get(a[OVS_PACKET_ATTR_KEY]));
upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]);
upcall->userdata = (a[OVS_PACKET_ATTR_USERDATA]
? nl_attr_get_u64(a[OVS_PACKET_ATTR_USERDATA])
}
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);
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);
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;
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) {
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);
}
}
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);
+}
/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
enum { QUEUE_MASK = MAX_QUEUE_LEN - 1 };
BUILD_ASSERT_DECL(IS_POW2(MAX_QUEUE_LEN));
+struct dp_netdev_upcall {
+ struct dpif_upcall upcall; /* Queued upcall information. */
+ struct ofpbuf buf; /* ofpbuf instance for upcall.packet. */
+};
+
struct dp_netdev_queue {
- struct dpif_upcall *upcalls[MAX_QUEUE_LEN];
+ struct dp_netdev_upcall upcalls[MAX_QUEUE_LEN];
unsigned int head, tail;
};
struct shash_node *node;
SHASH_FOR_EACH(node, &dp_netdevs) {
- sset_add(all_dps, node->name);
+ sset_add(all_dps, node->name);
}
- return 0;
+ return 0;
}
static struct dpif *
struct dp_netdev_queue *q = &dp->queues[i];
while (q->tail != q->head) {
- struct dpif_upcall *upcall = q->upcalls[q->tail++ & QUEUE_MASK];
-
- ofpbuf_delete(upcall->packet);
- free(upcall);
+ struct dp_netdev_upcall *u = &q->upcalls[q->tail++ & QUEUE_MASK];
+ ofpbuf_uninit(&u->buf);
}
}
}
struct dp_netdev *dp = get_dp_netdev(dpif);
int port_no;
- port_no = choose_port(dpif, netdev);
+ if (*port_nop != UINT16_MAX) {
+ if (*port_nop >= MAX_PORTS) {
+ return EFBIG;
+ } else if (dp->ports[*port_nop]) {
+ return EBUSY;
+ }
+ port_no = *port_nop;
+ } else {
+ port_no = choose_port(dpif, netdev);
+ }
if (port_no >= 0) {
*port_nop = port_no;
return do_add_port(dp, netdev_get_name(netdev),
}
static int
-add_flow(struct dpif *dpif, const struct flow *key,
- const struct nlattr *actions, size_t actions_len)
+dp_netdev_flow_add(struct dp_netdev *dp, const struct flow *key,
+ const struct nlattr *actions, size_t actions_len)
{
- struct dp_netdev *dp = get_dp_netdev(dpif);
struct dp_netdev_flow *flow;
int error;
if (put->stats) {
memset(put->stats, 0, sizeof *put->stats);
}
- return add_flow(dpif, &key, put->actions, put->actions_len);
+ return dp_netdev_flow_add(dp, &key, put->actions,
+ put->actions_len);
} else {
return EFBIG;
}
{
struct dp_netdev_queue *q = find_nonempty_queue(dpif);
if (q) {
- struct dpif_upcall *u = q->upcalls[q->tail++ & QUEUE_MASK];
- *upcall = *u;
- free(u);
+ struct dp_netdev_upcall *u = &q->upcalls[q->tail++ & QUEUE_MASK];
+
+ *upcall = u->upcall;
+ upcall->packet = buf;
ofpbuf_uninit(buf);
- *buf = *u->packet;
+ *buf = u->buf;
return 0;
} else {
if (packet->size < ETH_HEADER_LEN) {
return;
}
- flow_extract(packet, 0, 0, port->port_no, &key);
+ flow_extract(packet, 0, 0, odp_port_to_ofp_port(port->port_no), &key);
flow = dp_netdev_lookup_flow(dp, &key);
if (flow) {
dp_netdev_flow_used(flow, &key, packet);
int queue_no, const struct flow *flow, uint64_t arg)
{
struct dp_netdev_queue *q = &dp->queues[queue_no];
+ struct dp_netdev_upcall *u;
struct dpif_upcall *upcall;
struct ofpbuf *buf;
size_t key_len;
return ENOBUFS;
}
- buf = ofpbuf_new(ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size);
+ u = &q->upcalls[q->head++ & QUEUE_MASK];
+
+ buf = &u->buf;
+ ofpbuf_init(buf, ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size);
odp_flow_key_from_flow(buf, flow);
key_len = buf->size;
ofpbuf_pull(buf, key_len);
ofpbuf_reserve(buf, 2);
ofpbuf_put(buf, packet->data, packet->size);
- upcall = xzalloc(sizeof *upcall);
+ upcall = &u->upcall;
upcall->type = queue_no;
upcall->packet = buf;
upcall->key = buf->base;
upcall->key_len = key_len;
upcall->userdata = arg;
- q->upcalls[q->head++ & QUEUE_MASK] = upcall;
-
return 0;
}
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Retrieves statistics for 'dpif' into 'stats'. */
int (*get_stats)(const struct dpif *dpif, struct dpif_dp_stats *stats);
- /* Adds 'netdev' as a new port in 'dpif'. If successful, sets '*port_no'
- * to the new port's port number. */
+ /* Adds 'netdev' as a new port in 'dpif'. If '*port_no' is not
+ * UINT16_MAX, attempts to use that as the port's port number.
+ *
+ * If port is successfully added, sets '*port_no' to the new port's
+ * port number. Returns EBUSY if caller attempted to choose a port
+ * number, and it was in use. */
int (*port_add)(struct dpif *dpif, struct netdev *netdev,
uint16_t *port_no);
* actions as the OVS_USERSPACE_ATTR_PID attribute's value, for use in
* flows whose packets arrived on port 'port_no'.
*
+ * A 'port_no' of UINT16_MAX should be treated as a special case. The
+ * implementation should return a reserved PID, not allocated to any port,
+ * that the client may use for special purposes.
+ *
* The return value only needs to be meaningful when DPIF_UC_ACTION has
* been enabled in the 'dpif''s listen mask, and it is allowed to change
* when DPIF_UC_ACTION is disabled and then re-enabled.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return error;
}
-/* Attempts to add 'netdev' as a port on 'dpif'. If successful, returns 0 and
- * sets '*port_nop' to the new port's port number (if 'port_nop' is non-null).
- * On failure, returns a positive errno value and sets '*port_nop' to
- * UINT16_MAX (if 'port_nop' is non-null). */
+/* Attempts to add 'netdev' as a port on 'dpif'. If 'port_nop' is
+ * non-null and its value is not UINT16_MAX, then attempts to use the
+ * value as the port number.
+ *
+ * If successful, returns 0 and sets '*port_nop' to the new port's port
+ * number (if 'port_nop' is non-null). On failure, returns a positive
+ * errno value and sets '*port_nop' to UINT16_MAX (if 'port_nop' is
+ * non-null). */
int
dpif_port_add(struct dpif *dpif, struct netdev *netdev, uint16_t *port_nop)
{
const char *netdev_name = netdev_get_name(netdev);
- uint16_t port_no;
+ uint16_t port_no = UINT16_MAX;
int error;
COVERAGE_INC(dpif_port_add);
+ if (port_nop) {
+ port_no = *port_nop;
+ }
+
error = dpif->dpif_class->port_add(dpif, netdev, &port_no);
if (!error) {
VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu16,
* as the OVS_USERSPACE_ATTR_PID attribute's value, for use in flows whose
* packets arrived on port 'port_no'.
*
+ * A 'port_no' of UINT16_MAX is a special case: it returns a reserved PID, not
+ * allocated to any port, that the client may use for special purposes.
+ *
* The return value is only meaningful when DPIF_UC_ACTION has been enabled in
* the 'dpif''s listen mask. It is allowed to change when DPIF_UC_ACTION is
* disabled and then re-enabled, so a client that does that must be prepared to
}
/* Extracts the flow stats for a packet. The 'flow' and 'packet'
- * arguments must have been initialized through a call to flow_extract(). */
+ * arguments must have been initialized through a call to flow_extract().
+ * 'used' is stored into stats->used. */
void
dpif_flow_stats_extract(const struct flow *flow, const struct ofpbuf *packet,
- struct dpif_flow_stats *stats)
+ long long int used, struct dpif_flow_stats *stats)
{
stats->tcp_flags = packet_get_tcp_flags(packet, flow);
stats->n_bytes = packet->size;
stats->n_packets = 1;
- stats->used = time_msec();
+ stats->used = used;
}
/* Appends a human-readable representation of 'stats' to 's'. */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
};
void dpif_flow_stats_extract(const struct flow *, const struct ofpbuf *packet,
- struct dpif_flow_stats *);
+ long long int used, struct dpif_flow_stats *);
void dpif_flow_stats_format(const struct dpif_flow_stats *, struct ds *);
enum dpif_flow_put_flags {
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return EOF;
}
+/* Reads a line from 'file' into 'ds' and does some preprocessing on it:
+ *
+ * - If the line begins with #, prints it on stdout and reads the next line.
+ *
+ * - Otherwise, if the line contains an # somewhere else, strips it and
+ * everything following it (as a comment).
+ *
+ * - If (after comment removal) the line contains only white space, prints
+ * a blank line on stdout and reads the next line.
+ *
+ * - Otherwise, returns the line to the caller.
+ *
+ * This is useful in some of the OVS tests, where we want to check that parsing
+ * and then re-formatting some kind of data does not change it, but we also
+ * want to be able to put comments in the input.
+ *
+ * Returns 0 if successful, EOF if no non-blank line was found. */
+int
+ds_get_test_line(struct ds *ds, FILE *file)
+{
+ for (;;) {
+ char *s, *comment;
+ int retval;
+
+ retval = ds_get_line(ds, file);
+ if (retval) {
+ return retval;
+ }
+
+ s = ds_cstr(ds);
+ if (*s == '#') {
+ puts(s);
+ continue;
+ }
+
+ comment = strchr(s, '#');
+ if (comment) {
+ *comment = '\0';
+ }
+ if (s[strspn(s, " \t\n")] == '\0') {
+ putchar('\n');
+ continue;
+ }
+
+ return 0;
+ }
+}
+
char *
ds_cstr(struct ds *ds)
{
const char *
ds_cstr_ro(const struct ds *ds)
{
- return ds_cstr((struct ds *) ds);
+ return ds_cstr(CONST_CAST(struct ds *, ds));
}
/* Returns a null-terminated string representing the current contents of 'ds',
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
uintptr_t ofs, bool ascii);
int ds_get_line(struct ds *, FILE *);
int ds_get_preprocessed_line(struct ds *, FILE *);
+int ds_get_test_line(struct ds *, FILE *);
char *ds_cstr(struct ds *);
const char *ds_cstr_ro(const struct ds *);
-/* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2010 Nicira Networks
+/* Copyright (c) 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
inited = true;
xpipe(signal_fds);
- set_nonblocking(signal_fds[0]);
- set_nonblocking(signal_fds[1]);
+ xset_nonblocking(signal_fds[0]);
+ xset_nonblocking(signal_fds[1]);
sigemptyset(&fatal_signal_set);
for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
void
fatal_signal_run(void)
{
- int sig_nr;
+ sig_atomic_t sig_nr;
fatal_signal_init();
sig_nr = stored_sig_nr;
if (sig_nr != SIG_ATOMIC_MAX) {
+ VLOG_WARN("terminating with signal %d (%s)",
+ (int)sig_nr, signal_name(sig_nr));
call_hooks(sig_nr);
/* Re-raise the signal with the default handling so that the program
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <string.h>
#include "byte-order.h"
#include "coverage.h"
+#include "csum.h"
#include "dynamic-string.h"
#include "hash.h"
#include "ofpbuf.h"
|| (nexthdr == IPPROTO_DSTOPTS)) {
/* These headers, while different, have the fields we care about
* in the same location and with the same interpretation. */
- const struct ip6_ext *ext_hdr = (struct ip6_ext *)packet->data;
+ const struct ip6_ext *ext_hdr = packet->data;
nexthdr = ext_hdr->ip6e_nxt;
if (!ofpbuf_try_pull(packet, (ext_hdr->ip6e_len + 1) * 8)) {
return EINVAL;
* we care about are in the same location as the generic
* option header--only the header length is calculated
* differently. */
- const struct ip6_ext *ext_hdr = (struct ip6_ext *)packet->data;
+ const struct ip6_ext *ext_hdr = packet->data;
nexthdr = ext_hdr->ip6e_nxt;
if (!ofpbuf_try_pull(packet, (ext_hdr->ip6e_len + 2) * 4)) {
return EINVAL;
}
} else if (nexthdr == IPPROTO_FRAGMENT) {
- const struct ip6_frag *frag_hdr = (struct ip6_frag *)packet->data;
+ const struct ip6_frag *frag_hdr = packet->data;
nexthdr = frag_hdr->ip6f_nxt;
if (!ofpbuf_try_pull(packet, sizeof *frag_hdr)) {
const flow_wildcards_t wc = wildcards->wildcards;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
for (i = 0; i < FLOW_N_REGS; i++) {
flow->regs[i] &= wildcards->reg_masks[i];
}
flow->tun_id &= wildcards->tun_id_mask;
+ flow->metadata &= wildcards->metadata_mask;
flow->nw_src &= wildcards->nw_src_mask;
flow->nw_dst &= wildcards->nw_dst_mask;
if (wc & FWW_IN_PORT) {
}
flow->tp_src &= wildcards->tp_src_mask;
flow->tp_dst &= wildcards->tp_dst_mask;
- if (wc & FWW_DL_SRC) {
- memset(flow->dl_src, 0, sizeof flow->dl_src);
- }
- if (wc & FWW_DL_DST) {
- flow->dl_dst[0] &= 0x01;
- memset(&flow->dl_dst[1], 0, 5);
- }
- if (wc & FWW_ETH_MCAST) {
- flow->dl_dst[0] &= 0xfe;
- }
+ eth_addr_bitand(flow->dl_src, wildcards->dl_src_mask, flow->dl_src);
+ eth_addr_bitand(flow->dl_dst, wildcards->dl_dst_mask, flow->dl_dst);
if (wc & FWW_NW_PROTO) {
flow->nw_proto = 0;
}
- if (wc & FWW_IPV6_LABEL) {
- flow->ipv6_label = htonl(0);
- }
+ flow->ipv6_label &= wildcards->ipv6_label_mask;
if (wc & FWW_NW_DSCP) {
flow->nw_tos &= ~IP_DSCP_MASK;
}
flow->nw_ttl = 0;
}
flow->nw_frag &= wildcards->nw_frag_mask;
- if (wc & FWW_ARP_SHA) {
- memset(flow->arp_sha, 0, sizeof flow->arp_sha);
- }
- if (wc & FWW_ARP_THA) {
- memset(flow->arp_tha, 0, sizeof flow->arp_tha);
- }
+ eth_addr_bitand(flow->arp_sha, wildcards->arp_sha_mask, flow->arp_sha);
+ eth_addr_bitand(flow->arp_tha, wildcards->arp_tha_mask, flow->arp_tha);
flow->ipv6_src = ipv6_addr_bitand(&flow->ipv6_src,
&wildcards->ipv6_src_mask);
flow->ipv6_dst = ipv6_addr_bitand(&flow->ipv6_dst,
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
fmd->tun_id = flow->tun_id;
- fmd->tun_id_mask = htonll(UINT64_MAX);
-
+ fmd->metadata = flow->metadata;
memcpy(fmd->regs, flow->regs, sizeof fmd->regs);
- memset(fmd->reg_masks, 0xff, sizeof fmd->reg_masks);
-
fmd->in_port = flow->in_port;
}
{
ds_put_format(ds, "priority:%"PRIu32
",tunnel:%#"PRIx64
+ ",metadata:%#"PRIx64
",in_port:%04"PRIx16,
flow->skb_priority,
ntohll(flow->tun_id),
+ ntohll(flow->metadata),
flow->in_port);
ds_put_format(ds, ",tci(");
void
flow_wildcards_init_catchall(struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
wc->wildcards = FWW_ALL;
wc->tun_id_mask = htonll(0);
wc->nw_dst_mask = htonl(0);
wc->ipv6_src_mask = in6addr_any;
wc->ipv6_dst_mask = in6addr_any;
+ wc->ipv6_label_mask = htonl(0);
wc->nd_target_mask = in6addr_any;
memset(wc->reg_masks, 0, sizeof wc->reg_masks);
+ wc->metadata_mask = htonll(0);
wc->vlan_tci_mask = htons(0);
wc->nw_frag_mask = 0;
wc->tp_src_mask = htons(0);
wc->tp_dst_mask = htons(0);
+ memset(wc->dl_src_mask, 0, ETH_ADDR_LEN);
+ memset(wc->dl_dst_mask, 0, ETH_ADDR_LEN);
+ memset(wc->arp_sha_mask, 0, ETH_ADDR_LEN);
+ memset(wc->arp_tha_mask, 0, ETH_ADDR_LEN);
memset(wc->zeros, 0, sizeof wc->zeros);
}
void
flow_wildcards_init_exact(struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
wc->wildcards = 0;
wc->tun_id_mask = htonll(UINT64_MAX);
wc->nw_dst_mask = htonl(UINT32_MAX);
wc->ipv6_src_mask = in6addr_exact;
wc->ipv6_dst_mask = in6addr_exact;
+ wc->ipv6_label_mask = htonl(UINT32_MAX);
wc->nd_target_mask = in6addr_exact;
memset(wc->reg_masks, 0xff, sizeof wc->reg_masks);
+ wc->metadata_mask = htonll(UINT64_MAX);
wc->vlan_tci_mask = htons(UINT16_MAX);
wc->nw_frag_mask = UINT8_MAX;
wc->tp_src_mask = htons(UINT16_MAX);
wc->tp_dst_mask = htons(UINT16_MAX);
+ memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN);
+ memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN);
+ memset(wc->arp_sha_mask, 0xff, ETH_ADDR_LEN);
+ memset(wc->arp_tha_mask, 0xff, ETH_ADDR_LEN);
memset(wc->zeros, 0, sizeof wc->zeros);
}
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
if (wc->wildcards
|| wc->tun_id_mask != htonll(UINT64_MAX)
|| wc->tp_src_mask != htons(UINT16_MAX)
|| wc->tp_dst_mask != htons(UINT16_MAX)
|| wc->vlan_tci_mask != htons(UINT16_MAX)
+ || wc->metadata_mask != htonll(UINT64_MAX)
+ || !eth_mask_is_exact(wc->dl_src_mask)
+ || !eth_mask_is_exact(wc->dl_dst_mask)
+ || !eth_mask_is_exact(wc->arp_sha_mask)
+ || !eth_mask_is_exact(wc->arp_tha_mask)
|| !ipv6_mask_is_exact(&wc->ipv6_src_mask)
|| !ipv6_mask_is_exact(&wc->ipv6_dst_mask)
+ || wc->ipv6_label_mask != htonl(UINT32_MAX)
|| !ipv6_mask_is_exact(&wc->nd_target_mask)
|| wc->nw_frag_mask != UINT8_MAX) {
return false;
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
if (wc->wildcards != FWW_ALL
|| wc->tun_id_mask != htonll(0)
|| wc->tp_src_mask != htons(0)
|| wc->tp_dst_mask != htons(0)
|| wc->vlan_tci_mask != htons(0)
+ || wc->metadata_mask != htonll(0)
+ || !eth_addr_is_zero(wc->dl_src_mask)
+ || !eth_addr_is_zero(wc->dl_dst_mask)
+ || !eth_addr_is_zero(wc->arp_sha_mask)
+ || !eth_addr_is_zero(wc->arp_tha_mask)
|| !ipv6_mask_is_any(&wc->ipv6_src_mask)
|| !ipv6_mask_is_any(&wc->ipv6_dst_mask)
+ || wc->ipv6_label_mask != htonl(0)
|| !ipv6_mask_is_any(&wc->nd_target_mask)
|| wc->nw_frag_mask != 0) {
return false;
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
dst->wildcards = src1->wildcards | src2->wildcards;
dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask;
&src2->ipv6_src_mask);
dst->ipv6_dst_mask = ipv6_addr_bitand(&src1->ipv6_dst_mask,
&src2->ipv6_dst_mask);
+ dst->ipv6_label_mask = src1->ipv6_label_mask & src2->ipv6_label_mask;
dst->nd_target_mask = ipv6_addr_bitand(&src1->nd_target_mask,
&src2->nd_target_mask);
for (i = 0; i < FLOW_N_REGS; i++) {
dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i];
}
+ dst->metadata_mask = src1->metadata_mask & src2->metadata_mask;
dst->vlan_tci_mask = src1->vlan_tci_mask & src2->vlan_tci_mask;
dst->tp_src_mask = src1->tp_src_mask & src2->tp_src_mask;
dst->tp_dst_mask = src1->tp_dst_mask & src2->tp_dst_mask;
+ dst->nw_frag_mask = src1->nw_frag_mask & src2->nw_frag_mask;
+ eth_addr_bitand(src1->dl_src_mask, src2->dl_src_mask, dst->dl_src_mask);
+ eth_addr_bitand(src1->dl_dst_mask, src2->dl_dst_mask, dst->dl_dst_mask);
+ eth_addr_bitand(src1->arp_sha_mask, src2->arp_sha_mask, dst->arp_sha_mask);
+ eth_addr_bitand(src1->arp_tha_mask, src2->arp_tha_mask, dst->arp_tha_mask);
}
/* Returns a hash of the wildcards in 'wc'. */
/* If you change struct flow_wildcards and thereby trigger this
* assertion, please check that the new struct flow_wildcards has no holes
* in it before you update the assertion. */
- BUILD_ASSERT_DECL(sizeof *wc == 80 + FLOW_N_REGS * 4);
+ BUILD_ASSERT_DECL(sizeof *wc == 112 + FLOW_N_REGS * 4);
return hash_bytes(wc, sizeof *wc, basis);
}
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
if (a->wildcards != b->wildcards
|| a->tun_id_mask != b->tun_id_mask
|| a->nw_src_mask != b->nw_src_mask
|| a->nw_dst_mask != b->nw_dst_mask
|| a->vlan_tci_mask != b->vlan_tci_mask
+ || a->metadata_mask != b->metadata_mask
|| !ipv6_addr_equals(&a->ipv6_src_mask, &b->ipv6_src_mask)
|| !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask)
+ || a->ipv6_label_mask != b->ipv6_label_mask
|| !ipv6_addr_equals(&a->nd_target_mask, &b->nd_target_mask)
|| a->tp_src_mask != b->tp_src_mask
- || a->tp_dst_mask != b->tp_dst_mask) {
+ || a->tp_dst_mask != b->tp_dst_mask
+ || a->nw_frag_mask != b->nw_frag_mask
+ || !eth_addr_equals(a->dl_src_mask, b->dl_src_mask)
+ || !eth_addr_equals(a->dl_dst_mask, b->dl_dst_mask)
+ || !eth_addr_equals(a->arp_sha_mask, b->arp_sha_mask)
+ || !eth_addr_equals(a->arp_tha_mask, b->arp_tha_mask)) {
return false;
}
const struct flow_wildcards *b)
{
int i;
+ uint8_t eth_masked[ETH_ADDR_LEN];
struct in6_addr ipv6_masked;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
for (i = 0; i < FLOW_N_REGS; i++) {
if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
}
}
+ eth_addr_bitand(a->dl_src_mask, b->dl_src_mask, eth_masked);
+ if (!eth_addr_equals(eth_masked, b->dl_src_mask)) {
+ return true;
+ }
+
+ eth_addr_bitand(a->dl_dst_mask, b->dl_dst_mask, eth_masked);
+ if (!eth_addr_equals(eth_masked, b->dl_dst_mask)) {
+ return true;
+ }
+
+ eth_addr_bitand(a->arp_sha_mask, b->arp_sha_mask, eth_masked);
+ if (!eth_addr_equals(eth_masked, b->arp_sha_mask)) {
+ return true;
+ }
+
+ eth_addr_bitand(a->arp_tha_mask, b->arp_tha_mask, eth_masked);
+ if (!eth_addr_equals(eth_masked, b->arp_tha_mask)) {
+ return true;
+ }
+
ipv6_masked = ipv6_addr_bitand(&a->ipv6_src_mask, &b->ipv6_src_mask);
if (!ipv6_addr_equals(&ipv6_masked, &b->ipv6_src_mask)) {
return true;
|| (a->tun_id_mask & b->tun_id_mask) != b->tun_id_mask
|| (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask
|| (a->nw_dst_mask & b->nw_dst_mask) != b->nw_dst_mask
+ || (a->ipv6_label_mask & b->ipv6_label_mask) != b->ipv6_label_mask
|| (a->vlan_tci_mask & b->vlan_tci_mask) != b->vlan_tci_mask
+ || (a->metadata_mask & b->metadata_mask) != b->metadata_mask
|| (a->tp_src_mask & b->tp_src_mask) != b->tp_src_mask
- || (a->tp_dst_mask & b->tp_dst_mask) != b->tp_dst_mask);
+ || (a->tp_dst_mask & b->tp_dst_mask) != b->tp_dst_mask
+ || (a->nw_frag_mask & b->nw_frag_mask) != b->nw_frag_mask);
}
/* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
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)
*
* - Other values of 'vid' should not be used. */
void
-flow_set_vlan_vid(struct flow *flow, ovs_be16 vid)
+flow_set_dl_vlan(struct flow *flow, ovs_be16 vid)
{
- if (vid == htons(OFP_VLAN_NONE)) {
+ if (vid == htons(OFP10_VLAN_NONE)) {
flow->vlan_tci = htons(0);
} else {
vid &= htons(VLAN_VID_MASK);
}
}
+/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI). */
+void
+flow_set_vlan_vid(struct flow *flow, ovs_be16 vid)
+{
+ ovs_be16 mask = htons(VLAN_VID_MASK | VLAN_CFI);
+ flow->vlan_tci &= ~mask;
+ flow->vlan_tci |= vid & mask;
+}
+
/* Sets the VLAN PCP that 'flow' matches to 'pcp', which should be in the
* range 0...7.
*
* 'flow'.
*
* (This is useful only for testing, obviously, and the packet isn't really
- * valid. It hasn't got any checksums filled in, for one, and lots of fields
+ * valid. It hasn't got some checksums filled in, for one, and lots of fields
* are just zeroed.) */
void
flow_compose(struct ofpbuf *b, const struct flow *flow)
b->l4 = icmp = ofpbuf_put_zeros(b, sizeof *icmp);
icmp->icmp_type = ntohs(flow->tp_src);
icmp->icmp_code = ntohs(flow->tp_dst);
+ icmp->icmp_csum = csum(icmp, ICMP_HEADER_LEN);
}
}
+ ip = b->l3;
ip->ip_tot_len = htons((uint8_t *) b->data + b->size
- (uint8_t *) b->l3);
+ ip->ip_csum = csum(ip, sizeof *ip);
} else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
/* XXX */
} else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct dpif_flow_stats;
struct ds;
struct flow_wildcards;
-struct ofp_match;
struct ofpbuf;
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 10
+#define FLOW_WC_SEQ 14
#define FLOW_N_REGS 8
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
struct flow {
ovs_be64 tun_id; /* Encapsulating tunnel ID. */
+ ovs_be64 metadata; /* OpenFlow Metadata. */
struct in6_addr ipv6_src; /* IPv6 source address. */
struct in6_addr ipv6_dst; /* IPv6 destination address. */
struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */
uint8_t reserved[2]; /* Reserved for 64-bit packing. */
};
-/* Represents the metadata fields of struct flow. The masks are used to
- * indicate which metadata fields are relevant in a given context. Typically
- * they will be all 1 or all 0. */
+/* Represents the metadata fields of struct flow. */
struct flow_metadata {
ovs_be64 tun_id; /* Encapsulating tunnel ID. */
- ovs_be64 tun_id_mask; /* 1-bit in each significant tun_id bit.*/
-
+ ovs_be64 metadata; /* OpenFlow 1.1+ metadata field. */
uint32_t regs[FLOW_N_REGS]; /* Registers. */
- uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
-
uint16_t in_port; /* OpenFlow port or zero. */
};
/* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
* flow", followed by FLOW_PAD_SIZE bytes of padding. */
-#define FLOW_SIG_SIZE (110 + FLOW_N_REGS * 4)
+#define FLOW_SIG_SIZE (118 + FLOW_N_REGS * 4)
#define FLOW_PAD_SIZE 2
BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) == FLOW_SIG_SIZE - 1);
BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nw_frag) == 1);
BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 142 && FLOW_WC_SEQ == 10);
+BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 150 && FLOW_WC_SEQ == 14);
void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id,
uint16_t in_port, struct flow *);
static inline bool flow_equal(const struct flow *, const struct flow *);
static inline size_t flow_hash(const struct flow *, uint32_t basis);
+void flow_set_dl_vlan(struct flow *, ovs_be16 vid);
void flow_set_vlan_vid(struct flow *, ovs_be16 vid);
void flow_set_vlan_pcp(struct flow *, uint8_t pcp);
typedef unsigned int OVS_BITWISE flow_wildcards_t;
-/* Same values and meanings as corresponding OFPFW_* bits. */
+/* Same values and meanings as corresponding OFPFW10_* bits. */
#define FWW_IN_PORT ((OVS_FORCE flow_wildcards_t) (1 << 0))
-#define FWW_DL_SRC ((OVS_FORCE flow_wildcards_t) (1 << 2))
-#define FWW_DL_DST ((OVS_FORCE flow_wildcards_t) (1 << 3))
- /* excluding the multicast bit */
#define FWW_DL_TYPE ((OVS_FORCE flow_wildcards_t) (1 << 4))
#define FWW_NW_PROTO ((OVS_FORCE flow_wildcards_t) (1 << 5))
-/* No corresponding OFPFW_* bits. */
-#define FWW_ETH_MCAST ((OVS_FORCE flow_wildcards_t) (1 << 1))
- /* multicast bit only */
-#define FWW_NW_DSCP ((OVS_FORCE flow_wildcards_t) (1 << 6))
-#define FWW_NW_ECN ((OVS_FORCE flow_wildcards_t) (1 << 7))
-#define FWW_ARP_SHA ((OVS_FORCE flow_wildcards_t) (1 << 8))
-#define FWW_ARP_THA ((OVS_FORCE flow_wildcards_t) (1 << 9))
-#define FWW_IPV6_LABEL ((OVS_FORCE flow_wildcards_t) (1 << 10))
-#define FWW_NW_TTL ((OVS_FORCE flow_wildcards_t) (1 << 11))
-#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 12)) - 1))
+/* No corresponding OFPFW10_* bits. */
+#define FWW_NW_DSCP ((OVS_FORCE flow_wildcards_t) (1 << 1))
+#define FWW_NW_ECN ((OVS_FORCE flow_wildcards_t) (1 << 2))
+#define FWW_NW_TTL ((OVS_FORCE flow_wildcards_t) (1 << 3))
+#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 6)) - 1))
/* Remember to update FLOW_WC_SEQ when adding or removing FWW_*. */
-BUILD_ASSERT_DECL(FWW_ALL == ((1 << 12) - 1) && FLOW_WC_SEQ == 10);
+BUILD_ASSERT_DECL(FWW_ALL == ((1 << 6) - 1) && FLOW_WC_SEQ == 14);
/* Information on wildcards for a flow, as a supplement to "struct flow".
*
* the rest of the members. */
struct flow_wildcards {
ovs_be64 tun_id_mask; /* 1-bit in each significant tun_id bit. */
+ ovs_be64 metadata_mask; /* 1-bit in each significant metadata bit. */
flow_wildcards_t wildcards; /* 1-bit in each FWW_* wildcarded field. */
uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
ovs_be32 nw_src_mask; /* 1-bit in each significant nw_src bit. */
struct in6_addr ipv6_dst_mask; /* 1-bit in each signficant ipv6_dst bit. */
struct in6_addr nd_target_mask; /* 1-bit in each significant
nd_target bit. */
+ ovs_be32 ipv6_label_mask; /* 1 bit in each significant ipv6_label bit. */
ovs_be16 vlan_tci_mask; /* 1-bit in each significant vlan_tci bit. */
ovs_be16 tp_src_mask; /* 1-bit in each significant tp_src bit. */
ovs_be16 tp_dst_mask; /* 1-bit in each significant tp_dst bit. */
uint8_t nw_frag_mask; /* 1-bit in each significant nw_frag bit. */
- uint8_t zeros[5]; /* Padding field set to zero. */
+ uint8_t dl_src_mask[6]; /* 1-bit in each significant dl_src bit. */
+ uint8_t dl_dst_mask[6]; /* 1-bit in each significant dl_dst bit. */
+ uint8_t arp_sha_mask[6]; /* 1-bit in each significant dl_dst bit. */
+ uint8_t arp_tha_mask[6]; /* 1-bit in each significant dl_dst bit. */
+ uint8_t zeros[1]; /* Padding field set to zero. */
};
/* Remember to update FLOW_WC_SEQ when updating struct flow_wildcards. */
-BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 112 && FLOW_WC_SEQ == 10);
+BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 144 && FLOW_WC_SEQ == 14);
void flow_wildcards_init_catchall(struct flow_wildcards *);
void flow_wildcards_init_exact(struct flow_wildcards *);
/*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
while (node != NULL && node->hash != hash) {
node = node->next;
}
- return (struct hmap_node *) node;
+ return CONST_CAST(struct hmap_node *, node);
}
/* Returns the first node in 'hmap' with the given 'hash', or a null pointer if
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
json_array(const struct json *json)
{
assert(json->type == JSON_ARRAY);
- return (struct json_array *) &json->u.array;
+ return CONST_CAST(struct json_array *, &json->u.array);
}
struct shash *
json_object(const struct json *json)
{
assert(json->type == JSON_OBJECT);
- return (struct shash *) json->u.object;
+ return CONST_CAST(struct shash *, json->u.object);
}
bool
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
reconnect_set_name(s->reconnect, jsonrpc_get_name(jsonrpc));
reconnect_set_max_tries(s->reconnect, 0);
reconnect_connected(s->reconnect, time_msec());
+ s->dscp = 0;
s->rpc = jsonrpc;
s->stream = NULL;
s->pstream = NULL;
jsonrpc_session_set_dscp(struct jsonrpc_session *s,
uint8_t dscp)
{
- s->dscp = dscp;
+ if (s->dscp != dscp) {
+ s->dscp = dscp;
+ jsonrpc_session_force_reconnect(s);
+ }
}
/*
- * Copyright (c) 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2011 Nicira Networks
+/* Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
}
+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. */
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;
}
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));
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "dynamic-string.h"
#include "meta-flow.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "ofpbuf.h"
return value;
}
-static uint64_t
-get_bits(int n_bits, const void **p)
-{
- int n_segs = DIV_ROUND_UP(n_bits, 16);
- uint64_t value;
-
- value = 0;
- while (n_segs-- > 0) {
- value = (value << 16) | ntohs(get_be16(p));
- }
- return value;
-}
-
static void
get_subfield(int n_bits, const void **p, struct mf_subfield *sf)
{
return min_len;
}
-static enum ofperr
-learn_check_header(uint16_t header, size_t len)
+/* Converts 'nal' into a "struct ofpact_learn" and appends that struct to
+ * 'ofpacts'. Returns 0 if successful, otherwise an OFPERR_*. */
+enum ofperr
+learn_from_openflow(const struct nx_action_learn *nal, struct ofpbuf *ofpacts)
{
- int src_type = header & NX_LEARN_SRC_MASK;
- int dst_type = header & NX_LEARN_DST_MASK;
+ struct ofpact_learn *learn;
+ const void *p, *end;
- /* Check for valid src and dst type combination. */
- if (dst_type == NX_LEARN_DST_MATCH ||
- dst_type == NX_LEARN_DST_LOAD ||
- (dst_type == NX_LEARN_DST_OUTPUT &&
- src_type == NX_LEARN_SRC_FIELD)) {
- /* OK. */
- } else {
+ if (nal->pad) {
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- /* Check that the arguments don't overrun the end of the action. */
- if (len < learn_min_len(header)) {
- return OFPERR_OFPBAC_BAD_LEN;
- }
+ learn = ofpact_put_LEARN(ofpacts);
- return 0;
-}
-
-/* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a
- * valid action on 'flow'. */
-enum ofperr
-learn_check(const struct nx_action_learn *learn, const struct flow *flow)
-{
- struct cls_rule rule;
- const void *p, *end;
-
- cls_rule_init_catchall(&rule, 0);
+ learn->idle_timeout = ntohs(nal->idle_timeout);
+ learn->hard_timeout = ntohs(nal->hard_timeout);
+ learn->priority = ntohs(nal->priority);
+ learn->cookie = ntohll(nal->cookie);
+ learn->flags = ntohs(nal->flags);
+ learn->table_id = nal->table_id;
+ learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout);
+ learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout);
- if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM)
- || learn->pad
- || learn->table_id == 0xff) {
+ if (learn->flags & ~OFPFF_SEND_FLOW_REM || learn->table_id == 0xff) {
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- end = (char *) learn + ntohs(learn->len);
- for (p = learn + 1; p != end; ) {
+ end = (char *) nal + ntohs(nal->len);
+ for (p = nal + 1; p != end; ) {
+ struct ofpact_learn_spec *spec;
uint16_t header = ntohs(get_be16(&p));
- int n_bits = header & NX_LEARN_N_BITS_MASK;
- int src_type = header & NX_LEARN_SRC_MASK;
- int dst_type = header & NX_LEARN_DST_MASK;
-
- enum ofperr error;
- uint64_t value;
if (!header) {
break;
}
- error = learn_check_header(header, (char *) end - (char *) p);
- if (error) {
- return error;
- }
+ spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
+ learn = ofpacts->l2;
+ learn->n_specs++;
- /* Check the source. */
- if (src_type == NX_LEARN_SRC_FIELD) {
- struct mf_subfield src;
+ spec->src_type = header & NX_LEARN_SRC_MASK;
+ spec->dst_type = header & NX_LEARN_DST_MASK;
+ spec->n_bits = header & NX_LEARN_N_BITS_MASK;
- get_subfield(n_bits, &p, &src);
- error = mf_check_src(&src, flow);
- if (error) {
- return error;
- }
- value = 0;
+ /* Check for valid src and dst type combination. */
+ if (spec->dst_type == NX_LEARN_DST_MATCH ||
+ spec->dst_type == NX_LEARN_DST_LOAD ||
+ (spec->dst_type == NX_LEARN_DST_OUTPUT &&
+ spec->src_type == NX_LEARN_SRC_FIELD)) {
+ /* OK. */
} else {
- value = get_bits(n_bits, &p);
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- /* Check the destination. */
- if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
- struct mf_subfield dst;
+ /* Check that the arguments don't overrun the end of the action. */
+ if ((char *) end - (char *) p < learn_min_len(header)) {
+ return OFPERR_OFPBAC_BAD_LEN;
+ }
- get_subfield(n_bits, &p, &dst);
- error = (dst_type == NX_LEARN_DST_LOAD
- ? mf_check_dst(&dst, &rule.flow)
- : mf_check_src(&dst, &rule.flow));
- if (error) {
- return error;
- }
+ /* Get the source. */
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ get_subfield(spec->n_bits, &p, &spec->src);
+ } else {
+ int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
- if (dst_type == NX_LEARN_DST_MATCH
- && src_type == NX_LEARN_SRC_IMMEDIATE) {
- if (n_bits <= 64) {
- mf_set_subfield(&dst, value, &rule);
- } else {
- /* We're only setting subfields to allow us to check
- * prerequisites. No prerequisite depends on the value of
- * a field that is wider than 64 bits. So just skip
- * setting it entirely. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
- }
- }
+ bitwise_copy(p, p_bytes, 0,
+ &spec->src_imm, sizeof spec->src_imm, 0,
+ spec->n_bits);
+ p = (const uint8_t *) p + p_bytes;
+ }
+
+ /* Get the destination. */
+ if (spec->dst_type == NX_LEARN_DST_MATCH ||
+ spec->dst_type == NX_LEARN_DST_LOAD) {
+ get_subfield(spec->n_bits, &p, &spec->dst);
}
}
+ ofpact_update_len(ofpacts, &learn->ofpact);
+
if (!is_all_zeros(p, (char *) end - (char *) p)) {
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
return 0;
}
-void
-learn_execute(const struct nx_action_learn *learn, const struct flow *flow,
- struct ofputil_flow_mod *fm)
+/* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid,
+ * otherwise an OFPERR_*. */
+enum ofperr
+learn_check(const struct ofpact_learn *learn, const struct flow *flow)
{
- const void *p, *end;
- struct ofpbuf actions;
-
- cls_rule_init_catchall(&fm->cr, ntohs(learn->priority));
- fm->cookie = learn->cookie;
- fm->cookie_mask = htonll(UINT64_MAX);
- fm->table_id = learn->table_id;
- fm->command = OFPFC_MODIFY_STRICT;
- fm->idle_timeout = ntohs(learn->idle_timeout);
- fm->hard_timeout = ntohs(learn->hard_timeout);
- fm->buffer_id = UINT32_MAX;
- fm->out_port = OFPP_NONE;
- fm->flags = ntohs(learn->flags) & OFPFF_SEND_FLOW_REM;
- fm->actions = NULL;
- fm->n_actions = 0;
-
- ofpbuf_init(&actions, 64);
-
- if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
- struct nx_action_fin_timeout *naft;
-
- naft = ofputil_put_NXAST_FIN_TIMEOUT(&actions);
- naft->fin_idle_timeout = learn->fin_idle_timeout;
- naft->fin_hard_timeout = learn->fin_hard_timeout;
- }
-
- for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) {
- uint16_t header = ntohs(get_be16(&p));
- int n_bits = header & NX_LEARN_N_BITS_MASK;
- int src_type = header & NX_LEARN_SRC_MASK;
- int dst_type = header & NX_LEARN_DST_MASK;
- union mf_subvalue value;
-
- struct mf_subfield dst;
- int chunk, ofs;
-
- if (!header) {
- break;
- }
-
- if (src_type == NX_LEARN_SRC_FIELD) {
- struct mf_subfield src;
+ const struct ofpact_learn_spec *spec;
+ struct cls_rule rule;
- get_subfield(n_bits, &p, &src);
- mf_read_subfield(&src, flow, &value);
- } else {
- int p_bytes = 2 * DIV_ROUND_UP(n_bits, 16);
+ cls_rule_init_catchall(&rule, 0);
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+ enum ofperr error;
- memset(&value, 0, sizeof value);
- bitwise_copy(p, p_bytes, 0,
- &value, sizeof value, 0,
- n_bits);
- p = (const uint8_t *) p + p_bytes;
+ /* Check the source. */
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ error = mf_check_src(&spec->src, flow);
+ if (error) {
+ return error;
+ }
}
- switch (dst_type) {
+ /* Check the destination. */
+ switch (spec->dst_type) {
case NX_LEARN_DST_MATCH:
- get_subfield(n_bits, &p, &dst);
- mf_write_subfield(&dst, &value, &fm->cr);
+ error = mf_check_src(&spec->dst, &rule.flow);
+ if (error) {
+ return error;
+ }
+
+ mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
break;
case NX_LEARN_DST_LOAD:
- get_subfield(n_bits, &p, &dst);
- for (ofs = 0; ofs < n_bits; ofs += chunk) {
- struct nx_action_reg_load *load;
-
- chunk = MIN(n_bits - ofs, 64);
-
- load = ofputil_put_NXAST_REG_LOAD(&actions);
- load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs + ofs, chunk);
- load->dst = htonl(dst.field->nxm_header);
- bitwise_copy(&value, sizeof value, ofs,
- &load->value, sizeof load->value, 0,
- chunk);
+ error = mf_check_dst(&spec->dst, &rule.flow);
+ if (error) {
+ return error;
}
break;
case NX_LEARN_DST_OUTPUT:
- if (n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) {
- ofputil_put_OFPAT10_OUTPUT(&actions)->port = value.be16[7];
- }
+ /* Nothing to do. */
break;
}
}
-
- fm->actions = ofpbuf_steal_data(&actions);
- fm->n_actions = actions.size / sizeof(struct ofp_action_header);
+ return 0;
}
static void
put_be32(b, htonl(x));
}
-struct learn_spec {
- int n_bits;
+/* Converts 'learn' into a "struct nx_action_learn" and appends that action to
+ * 'ofpacts'. */
+void
+learn_to_nxast(const struct ofpact_learn *learn, struct ofpbuf *openflow)
+{
+ const struct ofpact_learn_spec *spec;
+ struct nx_action_learn *nal;
+ size_t start_ofs;
+
+ start_ofs = openflow->size;
+ nal = ofputil_put_NXAST_LEARN(openflow);
+ nal->idle_timeout = htons(learn->idle_timeout);
+ nal->hard_timeout = htons(learn->hard_timeout);
+ nal->fin_idle_timeout = htons(learn->fin_idle_timeout);
+ nal->fin_hard_timeout = htons(learn->fin_hard_timeout);
+ nal->priority = htons(learn->priority);
+ nal->cookie = htonll(learn->cookie);
+ nal->flags = htons(learn->flags);
+ nal->table_id = learn->table_id;
+
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+ put_u16(openflow, spec->n_bits | spec->dst_type | spec->src_type);
+
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ put_u32(openflow, spec->src.field->nxm_header);
+ put_u16(openflow, spec->src.ofs);
+ } else {
+ size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
+ uint8_t *bits = ofpbuf_put_zeros(openflow, n_dst_bytes);
+ bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0,
+ bits, n_dst_bytes, 0,
+ spec->n_bits);
+ }
+
+ if (spec->dst_type == NX_LEARN_DST_MATCH ||
+ spec->dst_type == NX_LEARN_DST_LOAD) {
+ put_u32(openflow, spec->dst.field->nxm_header);
+ put_u16(openflow, spec->dst.ofs);
+ }
+ }
+
+ if ((openflow->size - start_ofs) % 8) {
+ ofpbuf_put_zeros(openflow, 8 - (openflow->size - start_ofs) % 8);
+ }
+
+ nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal);
+ nal->len = htons(openflow->size - start_ofs);
+}
+
+/* Composes 'fm' so that executing it will implement 'learn' given that the
+ * packet being processed has 'flow' as its flow.
+ *
+ * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize
+ * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the
+ * 'ofpacts' buffer.
+ *
+ * The caller has to actually execute 'fm'. */
+void
+learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
+ struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts)
+{
+ const struct ofpact_learn_spec *spec;
- int src_type;
- struct mf_subfield src;
- union mf_subvalue src_imm;
+ cls_rule_init_catchall(&fm->cr, learn->priority);
+ fm->cookie = htonll(0);
+ fm->cookie_mask = htonll(0);
+ fm->new_cookie = htonll(learn->cookie);
+ fm->table_id = learn->table_id;
+ fm->command = OFPFC_MODIFY_STRICT;
+ fm->idle_timeout = learn->idle_timeout;
+ fm->hard_timeout = learn->hard_timeout;
+ fm->buffer_id = UINT32_MAX;
+ fm->out_port = OFPP_NONE;
+ fm->flags = learn->flags;
+ fm->ofpacts = NULL;
+ fm->ofpacts_len = 0;
- int dst_type;
- struct mf_subfield dst;
-};
+ if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
+ struct ofpact_fin_timeout *oft;
+
+ oft = ofpact_put_FIN_TIMEOUT(ofpacts);
+ oft->fin_idle_timeout = learn->fin_idle_timeout;
+ oft->fin_hard_timeout = learn->fin_hard_timeout;
+ }
+
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+ union mf_subvalue value;
+ int chunk, ofs;
+
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ mf_read_subfield(&spec->src, flow, &value);
+ } else {
+ value = spec->src_imm;
+ }
+
+ switch (spec->dst_type) {
+ case NX_LEARN_DST_MATCH:
+ mf_write_subfield(&spec->dst, &value, &fm->cr);
+ break;
+
+ case NX_LEARN_DST_LOAD:
+ for (ofs = 0; ofs < spec->n_bits; ofs += chunk) {
+ struct ofpact_reg_load *load;
+ ovs_be64 value_be;
+
+ chunk = MIN(spec->n_bits - ofs, 64);
+
+ load = ofpact_put_REG_LOAD(ofpacts);
+ load->dst.field = spec->dst.field;
+ load->dst.ofs = spec->dst.ofs + ofs;
+ load->dst.n_bits = chunk;
+
+ memset(&value_be, 0, sizeof value_be);
+ bitwise_copy(&value, sizeof value, ofs,
+ &value_be, sizeof value_be, 0,
+ chunk);
+ load->value = ntohll(value_be);
+ }
+ break;
+
+ case NX_LEARN_DST_OUTPUT:
+ if (spec->n_bits <= 16
+ || is_all_zeros(value.u8, sizeof value - 2)) {
+ uint16_t port = ntohs(value.be16[7]);
+
+ if (port < OFPP_MAX
+ || port == OFPP_IN_PORT
+ || port == OFPP_FLOOD
+ || port == OFPP_LOCAL
+ || port == OFPP_ALL) {
+ ofpact_put_OUTPUT(ofpacts)->port = port;
+ }
+ }
+ break;
+ }
+ }
+ ofpact_pad(ofpacts);
+
+ fm->ofpacts = ofpacts->data;
+ fm->ofpacts_len = ofpacts->size;
+}
static void
-learn_parse_load_immediate(const char *s, struct learn_spec *spec)
+learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
{
const char *full_s = s;
const char *arrow = strstr(s, "->");
static void
learn_parse_spec(const char *orig, char *name, char *value,
- struct learn_spec *spec)
+ struct ofpact_learn_spec *spec)
{
- memset(spec, 0, sizeof *spec);
if (mf_from_name(name)) {
const struct mf_field *dst = mf_from_name(name);
union mf_value imm;
if (value[strcspn(value, "[-")] == '-') {
learn_parse_load_immediate(value, spec);
} else {
- struct nx_action_reg_move move;
+ struct ofpact_reg_move move;
nxm_parse_reg_move(&move, value);
- spec->n_bits = ntohs(move.n_bits);
+ spec->n_bits = move.src.n_bits;
spec->src_type = NX_LEARN_SRC_FIELD;
- nxm_decode_discrete(&spec->src,
- move.src, move.src_ofs, move.n_bits);
+ spec->src = move.src;
spec->dst_type = NX_LEARN_DST_LOAD;
- nxm_decode_discrete(&spec->dst,
- move.dst, move.dst_ofs, move.n_bits);
+ spec->dst = move.dst;
}
} else if (!strcmp(name, "output")) {
if (mf_parse_subfield(&spec->src, value)[0] != '\0') {
}
/* Parses 'arg' as a set of arguments to the "learn" action and appends a
- * matching NXAST_LEARN action to 'b'. The format parsed is described in
- * ovs-ofctl(8).
+ * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the
+ * format parsed.
*
* Prints an error on stderr and aborts the program if 'arg' syntax is invalid.
*
*
* Modifies 'arg'. */
void
-learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
+learn_parse(char *arg, const struct flow *flow, struct ofpbuf *ofpacts)
{
char *orig = xstrdup(arg);
char *name, *value;
- enum ofperr error;
- size_t learn_ofs;
- size_t len;
- struct nx_action_learn *learn;
+ struct ofpact_learn *learn;
struct cls_rule rule;
+ enum ofperr error;
- learn_ofs = b->size;
- learn = ofputil_put_NXAST_LEARN(b);
- learn->idle_timeout = htons(OFP_FLOW_PERMANENT);
- learn->hard_timeout = htons(OFP_FLOW_PERMANENT);
- learn->priority = htons(OFP_DEFAULT_PRIORITY);
- learn->cookie = htonll(0);
- learn->flags = htons(0);
+ learn = ofpact_put_LEARN(ofpacts);
+ learn->idle_timeout = OFP_FLOW_PERMANENT;
+ learn->hard_timeout = OFP_FLOW_PERMANENT;
+ learn->priority = OFP_DEFAULT_PRIORITY;
learn->table_id = 1;
cls_rule_init_catchall(&rule, 0);
while (ofputil_parse_key_value(&arg, &name, &value)) {
- learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn);
if (!strcmp(name, "table")) {
learn->table_id = atoi(value);
if (learn->table_id == 255) {
orig);
}
} else if (!strcmp(name, "priority")) {
- learn->priority = htons(atoi(value));
+ learn->priority = atoi(value);
} else if (!strcmp(name, "idle_timeout")) {
- learn->idle_timeout = htons(atoi(value));
+ learn->idle_timeout = atoi(value);
} else if (!strcmp(name, "hard_timeout")) {
- learn->hard_timeout = htons(atoi(value));
+ learn->hard_timeout = atoi(value);
} else if (!strcmp(name, "fin_idle_timeout")) {
- learn->fin_idle_timeout = htons(atoi(value));
+ learn->fin_idle_timeout = atoi(value);
} else if (!strcmp(name, "fin_hard_timeout")) {
- learn->fin_hard_timeout = htons(atoi(value));
+ learn->fin_hard_timeout = atoi(value);
} else if (!strcmp(name, "cookie")) {
- learn->cookie = htonll(strtoull(value, NULL, 0));
+ learn->cookie = strtoull(value, NULL, 0);
} else {
- struct learn_spec spec;
+ struct ofpact_learn_spec *spec;
+
+ spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
+ learn = ofpacts->l2;
+ learn->n_specs++;
- learn_parse_spec(orig, name, value, &spec);
+ learn_parse_spec(orig, name, value, spec);
/* Check prerequisites. */
- if (spec.src_type == NX_LEARN_SRC_FIELD
- && flow && !mf_are_prereqs_ok(spec.src.field, flow)) {
+ if (spec->src_type == NX_LEARN_SRC_FIELD
+ && flow && !mf_are_prereqs_ok(spec->src.field, flow)) {
ovs_fatal(0, "%s: cannot specify source field %s because "
"prerequisites are not satisfied",
- orig, spec.src.field->name);
+ orig, spec->src.field->name);
}
- if ((spec.dst_type == NX_LEARN_DST_MATCH
- || spec.dst_type == NX_LEARN_DST_LOAD)
- && !mf_are_prereqs_ok(spec.dst.field, &rule.flow)) {
+ if ((spec->dst_type == NX_LEARN_DST_MATCH
+ || spec->dst_type == NX_LEARN_DST_LOAD)
+ && !mf_are_prereqs_ok(spec->dst.field, &rule.flow)) {
ovs_fatal(0, "%s: cannot specify destination field %s because "
"prerequisites are not satisfied",
- orig, spec.dst.field->name);
+ orig, spec->dst.field->name);
}
/* Update 'rule' to allow for satisfying destination
* prerequisites. */
- if (spec.src_type == NX_LEARN_SRC_IMMEDIATE
- && spec.dst_type == NX_LEARN_DST_MATCH) {
- mf_write_subfield(&spec.dst, &spec.src_imm, &rule);
- }
-
- /* Output the flow_mod_spec. */
- put_u16(b, spec.n_bits | spec.src_type | spec.dst_type);
- if (spec.src_type == NX_LEARN_SRC_IMMEDIATE) {
- int n_bytes = DIV_ROUND_UP(spec.n_bits, 16) * 2;
- int ofs = sizeof spec.src_imm - n_bytes;
- ofpbuf_put(b, &spec.src_imm.u8[ofs], n_bytes);
- } else {
- put_u32(b, spec.src.field->nxm_header);
- put_u16(b, spec.src.ofs);
- }
- if (spec.dst_type == NX_LEARN_DST_MATCH ||
- spec.dst_type == NX_LEARN_DST_LOAD) {
- put_u32(b, spec.dst.field->nxm_header);
- put_u16(b, spec.dst.ofs);
- } else {
- assert(spec.dst_type == NX_LEARN_DST_OUTPUT);
+ if (spec->src_type == NX_LEARN_SRC_IMMEDIATE
+ && spec->dst_type == NX_LEARN_DST_MATCH) {
+ mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
}
}
}
-
- put_u16(b, 0);
-
- len = b->size - learn_ofs;
- if (len % 8) {
- ofpbuf_put_zeros(b, 8 - len % 8);
- }
-
- learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn);
- learn->len = htons(b->size - learn_ofs);
+ ofpact_update_len(ofpacts, &learn->ofpact);
/* In theory the above should have caught any errors, but... */
if (flow) {
free(orig);
}
+static void
+format_subvalue(const union mf_subvalue *subvalue, struct ds *s)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(subvalue->u8); i++) {
+ if (subvalue->u8[i]) {
+ ds_put_format(s, "0x%"PRIx8, subvalue->u8[i]);
+ for (i++; i < ARRAY_SIZE(subvalue->u8); i++) {
+ ds_put_format(s, "%02"PRIx8, subvalue->u8[i]);
+ }
+ return;
+ }
+ }
+ ds_put_char(s, '0');
+}
+
+/* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
+ * describes. */
void
-learn_format(const struct nx_action_learn *learn, struct ds *s)
+learn_format(const struct ofpact_learn *learn, struct ds *s)
{
+ const struct ofpact_learn_spec *spec;
struct cls_rule rule;
- const void *p, *end;
cls_rule_init_catchall(&rule, 0);
ds_put_format(s, "learn(table=%"PRIu8, learn->table_id);
- if (learn->idle_timeout != htons(OFP_FLOW_PERMANENT)) {
- ds_put_format(s, ",idle_timeout=%"PRIu16, ntohs(learn->idle_timeout));
+ if (learn->idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, ",idle_timeout=%"PRIu16, learn->idle_timeout);
}
- if (learn->hard_timeout != htons(OFP_FLOW_PERMANENT)) {
- ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout));
+ if (learn->hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, ",hard_timeout=%"PRIu16, learn->hard_timeout);
}
if (learn->fin_idle_timeout) {
- ds_put_format(s, ",fin_idle_timeout=%"PRIu16,
- ntohs(learn->fin_idle_timeout));
+ ds_put_format(s, ",fin_idle_timeout=%"PRIu16, learn->fin_idle_timeout);
}
if (learn->fin_hard_timeout) {
- ds_put_format(s, ",fin_hard_timeout=%"PRIu16,
- ntohs(learn->fin_hard_timeout));
+ ds_put_format(s, ",fin_hard_timeout=%"PRIu16, learn->fin_hard_timeout);
}
- if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) {
- ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority));
+ if (learn->priority != OFP_DEFAULT_PRIORITY) {
+ ds_put_format(s, ",priority=%"PRIu16, learn->priority);
}
- if (learn->flags & htons(OFPFF_SEND_FLOW_REM)) {
+ if (learn->flags & OFPFF_SEND_FLOW_REM) {
ds_put_cstr(s, ",OFPFF_SEND_FLOW_REM");
}
- if (learn->flags & htons(~OFPFF_SEND_FLOW_REM)) {
- ds_put_format(s, ",***flags=%"PRIu16"***",
- ntohs(learn->flags) & ~OFPFF_SEND_FLOW_REM);
- }
- if (learn->cookie != htonll(0)) {
- ds_put_format(s, ",cookie=0x%"PRIx64, ntohll(learn->cookie));
+ if (learn->cookie != 0) {
+ ds_put_format(s, ",cookie=%#"PRIx64, learn->cookie);
}
- if (learn->pad != 0) {
- ds_put_cstr(s, ",***nonzero pad***");
- }
-
- end = (char *) learn + ntohs(learn->len);
- for (p = learn + 1; p != end; ) {
- uint16_t header = ntohs(get_be16(&p));
- int n_bits = header & NX_LEARN_N_BITS_MASK;
-
- int src_type = header & NX_LEARN_SRC_MASK;
- struct mf_subfield src;
- const uint8_t *src_value;
- int src_value_bytes;
-
- int dst_type = header & NX_LEARN_DST_MASK;
- struct mf_subfield dst;
-
- enum ofperr error;
- int i;
-
- if (!header) {
- break;
- }
-
- error = learn_check_header(header, (char *) end - (char *) p);
- if (error == OFPERR_OFPBAC_BAD_ARGUMENT) {
- ds_put_format(s, ",***bad flow_mod_spec header %"PRIx16"***)",
- header);
- return;
- } else if (error == OFPERR_OFPBAC_BAD_LEN) {
- ds_put_format(s, ",***flow_mod_spec at offset %td is %u bytes "
- "long but only %td bytes are left***)",
- (char *) p - (char *) (learn + 1) - 2,
- learn_min_len(header) + 2,
- (char *) end - (char *) p + 2);
- return;
- }
- assert(!error);
-
- /* Get the source. */
- if (src_type == NX_LEARN_SRC_FIELD) {
- get_subfield(n_bits, &p, &src);
- src_value_bytes = 0;
- src_value = NULL;
- } else {
- src.field = NULL;
- src.ofs = 0;
- src.n_bits = 0;
- src_value_bytes = 2 * DIV_ROUND_UP(n_bits, 16);
- src_value = p;
- p = (const void *) ((const uint8_t *) p + src_value_bytes);
- }
-
- /* Get the destination. */
- if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
- get_subfield(n_bits, &p, &dst);
- } else {
- dst.field = NULL;
- dst.ofs = 0;
- dst.n_bits = 0;
- }
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
ds_put_char(s, ',');
- switch (src_type | dst_type) {
+ switch (spec->src_type | spec->dst_type) {
case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH:
- if (dst.field && dst.ofs == 0 && n_bits == dst.field->n_bits) {
+ if (spec->dst.ofs == 0
+ && spec->dst.n_bits == spec->dst.field->n_bits) {
union mf_value value;
- uint8_t *bytes = (uint8_t *) &value;
-
- if (src_value_bytes > dst.field->n_bytes) {
- /* The destination field is an odd number of bytes, which
- * got rounded up to a multiple of 2 to be put into the
- * learning action. Skip over the leading byte, which
- * should be zero anyway. Otherwise the memcpy() below
- * will overrun the start of 'value'. */
- int diff = src_value_bytes - dst.field->n_bytes;
- src_value += diff;
- src_value_bytes -= diff;
- }
memset(&value, 0, sizeof value);
- memcpy(&bytes[dst.field->n_bytes - src_value_bytes],
- src_value, src_value_bytes);
- ds_put_format(s, "%s=", dst.field->name);
- mf_format(dst.field, &value, NULL, s);
+ bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0,
+ &value, spec->dst.field->n_bytes, 0,
+ spec->dst.field->n_bits);
+ ds_put_format(s, "%s=", spec->dst.field->name);
+ mf_format(spec->dst.field, &value, NULL, s);
} else {
- mf_format_subfield(&dst, s);
- ds_put_cstr(s, "=0x");
- for (i = 0; i < src_value_bytes; i++) {
- ds_put_format(s, "%02"PRIx8, src_value[i]);
- }
+ mf_format_subfield(&spec->dst, s);
+ ds_put_char(s, '=');
+ format_subvalue(&spec->src_imm, s);
}
break;
case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH:
- mf_format_subfield(&dst, s);
- if (src.field != dst.field || src.ofs != dst.ofs) {
+ mf_format_subfield(&spec->dst, s);
+ if (spec->src.field != spec->dst.field ||
+ spec->src.ofs != spec->dst.ofs) {
ds_put_char(s, '=');
- mf_format_subfield(&src, s);
+ mf_format_subfield(&spec->src, s);
}
break;
case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD:
- ds_put_cstr(s, "load:0x");
- for (i = 0; i < src_value_bytes; i++) {
- ds_put_format(s, "%02"PRIx8, src_value[i]);
- }
+ ds_put_format(s, "load:");
+ format_subvalue(&spec->src_imm, s);
ds_put_cstr(s, "->");
- mf_format_subfield(&dst, s);
+ mf_format_subfield(&spec->dst, s);
break;
case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD:
ds_put_cstr(s, "load:");
- mf_format_subfield(&src, s);
+ mf_format_subfield(&spec->src, s);
ds_put_cstr(s, "->");
- mf_format_subfield(&dst, s);
+ mf_format_subfield(&spec->dst, s);
break;
case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT:
ds_put_cstr(s, "output:");
- mf_format_subfield(&src, s);
+ mf_format_subfield(&spec->src, s);
break;
}
}
- if (!is_all_zeros(p, (char *) end - (char *) p)) {
- ds_put_cstr(s, ",***nonzero trailer***");
- }
ds_put_char(s, ')');
}
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ds;
struct flow;
struct ofpbuf;
+struct ofpact_learn;
struct ofputil_flow_mod;
struct nx_action_learn;
* See include/openflow/nicira-ext.h for NXAST_LEARN specification.
*/
-enum ofperr learn_check(const struct nx_action_learn *, const struct flow *);
-void learn_execute(const struct nx_action_learn *, const struct flow *,
- struct ofputil_flow_mod *);
+enum ofperr learn_from_openflow(const struct nx_action_learn *,
+ struct ofpbuf *ofpacts);
+enum ofperr learn_check(const struct ofpact_learn *, const struct flow *);
+void learn_to_nxast(const struct ofpact_learn *, struct ofpbuf *openflow);
-void learn_parse(struct ofpbuf *, char *, const struct flow *);
-void learn_format(const struct nx_action_learn *, struct ds *);
+void learn_execute(const struct ofpact_learn *, const struct flow *,
+ struct ofputil_flow_mod *, struct ofpbuf *ofpacts);
+
+void learn_parse(char *, const struct flow *, struct ofpbuf *ofpacts);
+void learn_format(const struct ofpact_learn *, struct ds *);
#endif /* learn.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "hmap.h"
#include "mac-learning.h"
#include "ofpbuf.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
+#include "ofp-msgs.h"
#include "ofp-parse.h"
#include "ofp-print.h"
#include "ofp-util.h"
#include "poll-loop.h"
#include "rconn.h"
#include "shash.h"
+#include "simap.h"
#include "timeval.h"
#include "vconn.h"
#include "vlog.h"
uint32_t queue_id; /* OpenFlow queue number. */
};
+enum lswitch_state {
+ S_CONNECTING, /* Waiting for connection to complete. */
+ S_FEATURES_REPLY, /* Waiting for features reply. */
+ S_SWITCHING, /* Switching flows. */
+};
+
struct lswitch {
+ struct rconn *rconn;
+ enum lswitch_state state;
+
/* If nonnegative, the switch sets up flows that expire after the given
* number of seconds (or never expire, if the value is OFP_FLOW_PERMANENT).
* Otherwise, the switch processes every packet. */
int max_idle;
+ enum ofputil_protocol protocol;
unsigned long long int datapath_id;
- time_t last_features_request;
struct mac_learning *ml; /* NULL to act as hub instead of switch. */
struct flow_wildcards wc; /* Wildcards to apply to flows. */
bool action_normal; /* Use OFPP_NORMAL? */
/* Number of outgoing queued packets on the rconn. */
struct rconn_packet_counter *queued;
+
+ /* If true, do not reply to any messages from the switch (for debugging
+ * fail-open mode). */
+ bool mute;
+
+ /* Optional "flow mod" requests to send to the switch at connection time,
+ * to set up the flow table. */
+ const struct ofputil_flow_mod *default_flows;
+ size_t n_default_flows;
};
/* The log messages here could actually be useful in debugging, so keep the
* rate limit relatively high. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
-static void queue_tx(struct lswitch *, struct rconn *, struct ofpbuf *);
-static void send_features_request(struct lswitch *, struct rconn *);
+static void queue_tx(struct lswitch *, struct ofpbuf *);
+static void send_features_request(struct lswitch *);
+static void lswitch_process_packet(struct lswitch *, const struct ofpbuf *);
static enum ofperr process_switch_features(struct lswitch *,
- struct ofp_switch_features *);
-static void process_packet_in(struct lswitch *, struct rconn *,
- const struct ofp_packet_in *);
-static void process_echo_request(struct lswitch *, struct rconn *,
- const struct ofp_header *);
+ struct ofp_header *);
+static void process_packet_in(struct lswitch *, const struct ofp_header *);
+static void process_echo_request(struct lswitch *, const struct ofp_header *);
/* Creates and returns a new learning switch whose configuration is given by
* 'cfg'.
lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
{
struct lswitch *sw;
+ uint32_t ofpfw;
sw = xzalloc(sizeof *sw);
+ sw->rconn = rconn;
+ sw->state = S_CONNECTING;
sw->max_idle = cfg->max_idle;
sw->datapath_id = 0;
- sw->last_features_request = time_now() - 1;
sw->ml = (cfg->mode == LSW_LEARN
? mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME)
: NULL);
sw->action_normal = cfg->mode == LSW_NORMAL;
- flow_wildcards_init_exact(&sw->wc);
- if (cfg->wildcards) {
- uint32_t ofpfw;
-
- if (cfg->wildcards == UINT32_MAX) {
- /* Try to wildcard as many fields as possible, but we cannot
- * wildcard all fields. We need in_port to detect moves. We need
- * Ethernet source and dest and VLAN VID to do L2 learning. */
- ofpfw = (OFPFW_DL_TYPE | OFPFW_DL_VLAN_PCP
- | OFPFW_NW_SRC_ALL | OFPFW_NW_DST_ALL
- | OFPFW_NW_TOS | OFPFW_NW_PROTO
- | OFPFW_TP_SRC | OFPFW_TP_DST);
- } else {
- ofpfw = cfg->wildcards;
- }
+ switch (cfg->wildcards) {
+ case 0:
+ ofpfw = 0;
+ break;
+
+ case UINT32_MAX:
+ /* Try to wildcard as many fields as possible, but we cannot
+ * wildcard all fields. We need in_port to detect moves. We need
+ * Ethernet source and dest and VLAN VID to do L2 learning. */
+ ofpfw = (OFPFW10_DL_TYPE | OFPFW10_DL_VLAN_PCP
+ | OFPFW10_NW_SRC_ALL | OFPFW10_NW_DST_ALL
+ | OFPFW10_NW_TOS | OFPFW10_NW_PROTO
+ | OFPFW10_TP_SRC | OFPFW10_TP_DST);
+ break;
- ofputil_wildcard_from_openflow(ofpfw, &sw->wc);
+ default:
+ ofpfw = cfg->wildcards;
+ break;
}
+ ofputil_wildcard_from_ofpfw10(ofpfw, &sw->wc);
sw->default_queue = cfg->default_queue;
hmap_init(&sw->queue_numbers);
shash_init(&sw->queue_names);
if (cfg->port_queues) {
- struct shash_node *node;
+ struct simap_node *node;
- SHASH_FOR_EACH (node, cfg->port_queues) {
+ SIMAP_FOR_EACH (node, cfg->port_queues) {
struct lswitch_port *port = xmalloc(sizeof *port);
hmap_node_nullify(&port->hmap_node);
- port->queue_id = (uintptr_t) node->data;
+ port->queue_id = node->data;
shash_add(&sw->queue_names, node->name, port);
}
}
+ sw->default_flows = cfg->default_flows;
+ sw->n_default_flows = cfg->n_default_flows;
+
sw->queued = rconn_packet_counter_create();
- send_features_request(sw, rconn);
- if (cfg->default_flows) {
+ return sw;
+}
+
+static void
+lswitch_handshake(struct lswitch *sw)
+{
+ enum ofputil_protocol protocol;
+
+ send_features_request(sw);
+
+ protocol = ofputil_protocol_from_ofp_version(rconn_get_version(sw->rconn));
+ if (sw->default_flows) {
enum ofputil_protocol usable_protocols;
- enum ofputil_protocol protocol;
struct ofpbuf *msg = NULL;
- int ofp_version;
int error = 0;
size_t i;
- /* Figure out the initial protocol on the connection. */
- ofp_version = rconn_get_version(rconn);
- protocol = ofputil_protocol_from_ofp_version(ofp_version);
-
/* If the initial protocol isn't good enough for default_flows, then
* pick one that will work and encode messages to set up that
* protocol.
* flow format with the switch, but that would require an asynchronous
* state machine. This version ought to work fine in practice. */
usable_protocols = ofputil_flow_mod_usable_protocols(
- cfg->default_flows, cfg->n_default_flows);
+ sw->default_flows, sw->n_default_flows);
if (!(protocol & usable_protocols)) {
enum ofputil_protocol want = rightmost_1bit(usable_protocols);
while (!error) {
if (!msg) {
break;
}
- error = rconn_send(rconn, msg, NULL);
+ error = rconn_send(sw->rconn, msg, NULL);
}
}
- for (i = 0; !error && i < cfg->n_default_flows; i++) {
- msg = ofputil_encode_flow_mod(&cfg->default_flows[i], protocol);
- error = rconn_send(rconn, msg, NULL);
+ for (i = 0; !error && i < sw->n_default_flows; i++) {
+ msg = ofputil_encode_flow_mod(&sw->default_flows[i], protocol);
+ error = rconn_send(sw->rconn, msg, NULL);
}
if (error) {
VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)",
- rconn_get_name(rconn), strerror(error));
+ rconn_get_name(sw->rconn), strerror(error));
}
}
+ sw->protocol = protocol;
+}
- return sw;
+bool
+lswitch_is_alive(const struct lswitch *sw)
+{
+ return rconn_is_alive(sw->rconn);
}
/* Destroys 'sw'. */
if (sw) {
struct lswitch_port *node, *next;
+ rconn_destroy(sw->rconn);
HMAP_FOR_EACH_SAFE (node, next, hmap_node, &sw->queue_numbers) {
hmap_remove(&sw->queue_numbers, &node->hmap_node);
free(node);
void
lswitch_run(struct lswitch *sw)
{
+ int i;
+
if (sw->ml) {
mac_learning_run(sw->ml, NULL);
}
+
+ rconn_run(sw->rconn);
+
+ if (sw->state == S_CONNECTING) {
+ if (rconn_get_version(sw->rconn) != -1) {
+ lswitch_handshake(sw);
+ sw->state = S_FEATURES_REPLY;
+ }
+ return;
+ }
+
+ for (i = 0; i < 50; i++) {
+ struct ofpbuf *msg;
+
+ msg = rconn_recv(sw->rconn);
+ if (!msg) {
+ break;
+ }
+
+ if (!sw->mute) {
+ lswitch_process_packet(sw, msg);
+ }
+ ofpbuf_delete(msg);
+ }
}
void
if (sw->ml) {
mac_learning_wait(sw->ml);
}
+ rconn_run_wait(sw->rconn);
+ rconn_recv_wait(sw->rconn);
}
/* Processes 'msg', which should be an OpenFlow received on 'rconn', according
* to the learning switch state in 'sw'. The most likely result of processing
* is that flow-setup and packet-out OpenFlow messages will be sent out on
* 'rconn'. */
-void
-lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
- const struct ofpbuf *msg)
+static void
+lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
{
- const struct ofp_header *oh = msg->data;
- const struct ofputil_msg_type *type;
+ enum ofptype type;
+ struct ofpbuf b;
- if (sw->datapath_id == 0
- && oh->type != OFPT_ECHO_REQUEST
- && oh->type != OFPT_FEATURES_REPLY) {
- send_features_request(sw, rconn);
+ b = *msg;
+ if (ofptype_pull(&type, &b)) {
return;
}
- ofputil_decode_msg_type(oh, &type);
- switch (ofputil_msg_type_code(type)) {
- case OFPUTIL_OFPT_ECHO_REQUEST:
- process_echo_request(sw, rconn, msg->data);
+ if (sw->state == S_FEATURES_REPLY
+ && type != OFPTYPE_ECHO_REQUEST
+ && type != OFPTYPE_FEATURES_REPLY) {
+ return;
+ }
+
+ switch (type) {
+ case OFPTYPE_ECHO_REQUEST:
+ process_echo_request(sw, msg->data);
break;
- case OFPUTIL_OFPT_FEATURES_REPLY:
- process_switch_features(sw, msg->data);
+ case OFPTYPE_FEATURES_REPLY:
+ if (sw->state == S_FEATURES_REPLY) {
+ if (!process_switch_features(sw, msg->data)) {
+ sw->state = S_SWITCHING;
+ } else {
+ rconn_disconnect(sw->rconn);
+ }
+ }
break;
- case OFPUTIL_OFPT_PACKET_IN:
- process_packet_in(sw, rconn, msg->data);
+ case OFPTYPE_PACKET_IN:
+ process_packet_in(sw, msg->data);
break;
- case OFPUTIL_OFPT_FLOW_REMOVED:
+ case OFPTYPE_FLOW_REMOVED:
/* Nothing to do. */
break;
- case OFPUTIL_MSG_INVALID:
- case OFPUTIL_OFPT_HELLO:
- case OFPUTIL_OFPT_ERROR:
- case OFPUTIL_OFPT_ECHO_REPLY:
- case OFPUTIL_OFPT_FEATURES_REQUEST:
- case OFPUTIL_OFPT_GET_CONFIG_REQUEST:
- case OFPUTIL_OFPT_GET_CONFIG_REPLY:
- case OFPUTIL_OFPT_SET_CONFIG:
- case OFPUTIL_OFPT_PORT_STATUS:
- case OFPUTIL_OFPT_PACKET_OUT:
- case OFPUTIL_OFPT_FLOW_MOD:
- case OFPUTIL_OFPT_PORT_MOD:
- case OFPUTIL_OFPT_BARRIER_REQUEST:
- case OFPUTIL_OFPT_BARRIER_REPLY:
- case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
- case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
- case OFPUTIL_OFPST_DESC_REQUEST:
- case OFPUTIL_OFPST_FLOW_REQUEST:
- case OFPUTIL_OFPST_AGGREGATE_REQUEST:
- case OFPUTIL_OFPST_TABLE_REQUEST:
- case OFPUTIL_OFPST_PORT_REQUEST:
- case OFPUTIL_OFPST_QUEUE_REQUEST:
- case OFPUTIL_OFPST_DESC_REPLY:
- case OFPUTIL_OFPST_FLOW_REPLY:
- case OFPUTIL_OFPST_QUEUE_REPLY:
- case OFPUTIL_OFPST_PORT_REPLY:
- case OFPUTIL_OFPST_TABLE_REPLY:
- case OFPUTIL_OFPST_AGGREGATE_REPLY:
- case OFPUTIL_NXT_ROLE_REQUEST:
- case OFPUTIL_NXT_ROLE_REPLY:
- case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
- case OFPUTIL_NXT_SET_FLOW_FORMAT:
- case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
- case OFPUTIL_NXT_PACKET_IN:
- case OFPUTIL_NXT_FLOW_MOD:
- case OFPUTIL_NXT_FLOW_REMOVED:
- case OFPUTIL_NXT_FLOW_AGE:
- case OFPUTIL_NXT_SET_ASYNC_CONFIG:
- case OFPUTIL_NXT_SET_CONTROLLER_ID:
- case OFPUTIL_NXST_FLOW_REQUEST:
- case OFPUTIL_NXST_AGGREGATE_REQUEST:
- case OFPUTIL_NXST_FLOW_REPLY:
- case OFPUTIL_NXST_AGGREGATE_REPLY:
+ case OFPTYPE_HELLO:
+ case OFPTYPE_ERROR:
+ case OFPTYPE_ECHO_REPLY:
+ case OFPTYPE_FEATURES_REQUEST:
+ case OFPTYPE_GET_CONFIG_REQUEST:
+ case OFPTYPE_GET_CONFIG_REPLY:
+ case OFPTYPE_SET_CONFIG:
+ case OFPTYPE_PORT_STATUS:
+ case OFPTYPE_PACKET_OUT:
+ case OFPTYPE_FLOW_MOD:
+ case OFPTYPE_PORT_MOD:
+ case OFPTYPE_BARRIER_REQUEST:
+ case OFPTYPE_BARRIER_REPLY:
+ case OFPTYPE_DESC_STATS_REQUEST:
+ case OFPTYPE_DESC_STATS_REPLY:
+ case OFPTYPE_FLOW_STATS_REQUEST:
+ case OFPTYPE_FLOW_STATS_REPLY:
+ case OFPTYPE_AGGREGATE_STATS_REQUEST:
+ case OFPTYPE_AGGREGATE_STATS_REPLY:
+ case OFPTYPE_TABLE_STATS_REQUEST:
+ case OFPTYPE_TABLE_STATS_REPLY:
+ case OFPTYPE_PORT_STATS_REQUEST:
+ case OFPTYPE_PORT_STATS_REPLY:
+ case OFPTYPE_QUEUE_STATS_REQUEST:
+ case OFPTYPE_QUEUE_STATS_REPLY:
+ case OFPTYPE_PORT_DESC_STATS_REQUEST:
+ case OFPTYPE_PORT_DESC_STATS_REPLY:
+ case OFPTYPE_ROLE_REQUEST:
+ case OFPTYPE_ROLE_REPLY:
+ case OFPTYPE_SET_FLOW_FORMAT:
+ case OFPTYPE_FLOW_MOD_TABLE_ID:
+ case OFPTYPE_SET_PACKET_IN_FORMAT:
+ case OFPTYPE_FLOW_AGE:
+ case OFPTYPE_SET_ASYNC_CONFIG:
+ case OFPTYPE_SET_CONTROLLER_ID:
+ case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+ case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
+ case OFPTYPE_FLOW_MONITOR_CANCEL:
+ case OFPTYPE_FLOW_MONITOR_PAUSED:
+ case OFPTYPE_FLOW_MONITOR_RESUMED:
default:
if (VLOG_IS_DBG_ENABLED()) {
char *s = ofp_to_string(msg->data, msg->size, 2);
}
\f
static void
-send_features_request(struct lswitch *sw, struct rconn *rconn)
+send_features_request(struct lswitch *sw)
{
- time_t now = time_now();
- if (now >= sw->last_features_request + 1) {
- struct ofpbuf *b;
- struct ofp_switch_config *osc;
+ struct ofpbuf *b;
+ struct ofp_switch_config *osc;
+ int ofp_version = rconn_get_version(sw->rconn);
- /* Send OFPT_FEATURES_REQUEST. */
- make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &b);
- queue_tx(sw, rconn, b);
+ assert(ofp_version > 0 && ofp_version < 0xff);
- /* Send OFPT_SET_CONFIG. */
- osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &b);
- osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
- queue_tx(sw, rconn, b);
+ /* Send OFPT_FEATURES_REQUEST. */
+ b = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, ofp_version, 0);
+ queue_tx(sw, b);
- sw->last_features_request = now;
- }
+ /* Send OFPT_SET_CONFIG. */
+ b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, ofp_version, sizeof *osc);
+ osc = ofpbuf_put_zeros(b, sizeof *osc);
+ osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN);
+ queue_tx(sw, b);
}
static void
-queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b)
+queue_tx(struct lswitch *sw, struct ofpbuf *b)
{
- int retval = rconn_send_with_limit(rconn, b, sw->queued, 10);
+ int retval = rconn_send_with_limit(sw->rconn, b, sw->queued, 10);
if (retval && retval != ENOTCONN) {
if (retval == EAGAIN) {
VLOG_INFO_RL(&rl, "%016llx: %s: tx queue overflow",
- sw->datapath_id, rconn_get_name(rconn));
+ sw->datapath_id, rconn_get_name(sw->rconn));
} else {
VLOG_WARN_RL(&rl, "%016llx: %s: send: %s",
- sw->datapath_id, rconn_get_name(rconn),
+ sw->datapath_id, rconn_get_name(sw->rconn),
strerror(retval));
}
}
}
static enum ofperr
-process_switch_features(struct lswitch *sw, struct ofp_switch_features *osf)
+process_switch_features(struct lswitch *sw, struct ofp_header *oh)
{
struct ofputil_switch_features features;
struct ofputil_phy_port port;
enum ofperr error;
struct ofpbuf b;
- error = ofputil_decode_switch_features(osf, &features, &b);
+ error = ofputil_decode_switch_features(oh, &features, &b);
if (error) {
VLOG_ERR("received invalid switch feature reply (%s)",
ofperr_to_string(error));
sw->datapath_id = features.datapath_id;
- while (!ofputil_pull_switch_features_port(&b, &port)) {
+ while (!ofputil_pull_phy_port(oh->version, &b, &port)) {
struct lswitch_port *lp = shash_find_data(&sw->queue_names, port.name);
if (lp && hmap_node_is_null(&lp->hmap_node)) {
lp->port_no = port.port_no;
}
static void
-process_packet_in(struct lswitch *sw, struct rconn *rconn,
- const struct ofp_packet_in *opi)
+process_packet_in(struct lswitch *sw, const struct ofp_header *oh)
{
- uint16_t in_port = ntohs(opi->in_port);
+ struct ofputil_packet_in pi;
uint32_t queue_id;
uint16_t out_port;
- struct ofp_action_header actions[2];
- size_t actions_len;
+ uint64_t ofpacts_stub[64 / 8];
+ struct ofpbuf ofpacts;
struct ofputil_packet_out po;
+ enum ofperr error;
- size_t pkt_ofs, pkt_len;
struct ofpbuf pkt;
struct flow flow;
+ error = ofputil_decode_packet_in(&pi, oh);
+ if (error) {
+ VLOG_WARN_RL(&rl, "failed to decode packet-in: %s",
+ ofperr_to_string(error));
+ return;
+ }
+
/* Ignore packets sent via output to OFPP_CONTROLLER. This library never
* uses such an action. You never know what experiments might be going on,
* though, and it seems best not to interfere with them. */
- if (opi->reason != OFPR_NO_MATCH) {
+ if (pi.reason != OFPR_NO_MATCH) {
return;
}
/* Extract flow data from 'opi' into 'flow'. */
- pkt_ofs = offsetof(struct ofp_packet_in, data);
- pkt_len = ntohs(opi->header.length) - pkt_ofs;
- ofpbuf_use_const(&pkt, opi->data, pkt_len);
- flow_extract(&pkt, 0, 0, in_port, &flow);
+ ofpbuf_use_const(&pkt, pi.packet, pi.packet_len);
+ flow_extract(&pkt, 0, pi.fmd.tun_id, pi.fmd.in_port, &flow);
/* Choose output port. */
out_port = lswitch_choose_destination(sw, &flow);
/* Make actions. */
- queue_id = get_queue_id(sw, in_port);
+ queue_id = get_queue_id(sw, pi.fmd.in_port);
+ ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
if (out_port == OFPP_NONE) {
- actions_len = 0;
+ /* No actions. */
} else if (queue_id == UINT32_MAX || out_port >= OFPP_MAX) {
- struct ofp_action_output oao;
-
- memset(&oao, 0, sizeof oao);
- oao.type = htons(OFPAT10_OUTPUT);
- oao.len = htons(sizeof oao);
- oao.port = htons(out_port);
-
- memcpy(actions, &oao, sizeof oao);
- actions_len = sizeof oao;
+ ofpact_put_OUTPUT(&ofpacts)->port = out_port;
} else {
- struct ofp_action_enqueue oae;
-
- memset(&oae, 0, sizeof oae);
- oae.type = htons(OFPAT10_ENQUEUE);
- oae.len = htons(sizeof oae);
- oae.port = htons(out_port);
- oae.queue_id = htonl(queue_id);
-
- memcpy(actions, &oae, sizeof oae);
- actions_len = sizeof oae;
+ struct ofpact_enqueue *enqueue = ofpact_put_ENQUEUE(&ofpacts);
+ enqueue->port = out_port;
+ enqueue->queue = queue_id;
}
- assert(actions_len <= sizeof actions);
+ ofpact_pad(&ofpacts);
/* Prepare packet_out in case we need one. */
- po.buffer_id = ntohl(opi->buffer_id);
+ po.buffer_id = pi.buffer_id;
if (po.buffer_id == UINT32_MAX) {
po.packet = pkt.data;
po.packet_len = pkt.size;
po.packet = NULL;
po.packet_len = 0;
}
- po.in_port = in_port;
- po.actions = (union ofp_action *) actions;
- po.n_actions = actions_len / sizeof *actions;
+ po.in_port = pi.fmd.in_port;
+ po.ofpacts = ofpacts.data;
+ po.ofpacts_len = ofpacts.size;
/* Send the packet, and possibly the whole flow, to the output port. */
if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
+ struct ofputil_flow_mod fm;
struct ofpbuf *buffer;
- struct cls_rule rule;
/* The output port is known, or we always flood everything, so add a
* new flow. */
- cls_rule_init(&flow, &sw->wc, 0, &rule);
- buffer = make_add_flow(&rule, ntohl(opi->buffer_id),
- sw->max_idle, actions_len);
- ofpbuf_put(buffer, actions, actions_len);
- queue_tx(sw, rconn, buffer);
+ memset(&fm, 0, sizeof fm);
+ cls_rule_init(&flow, &sw->wc, 0, &fm.cr);
+ ofputil_normalize_rule_quiet(&fm.cr);
+ fm.table_id = 0xff;
+ fm.command = OFPFC_ADD;
+ fm.idle_timeout = sw->max_idle;
+ fm.buffer_id = pi.buffer_id;
+ fm.out_port = OFPP_NONE;
+ fm.ofpacts = ofpacts.data;
+ fm.ofpacts_len = ofpacts.size;
+ buffer = ofputil_encode_flow_mod(&fm, sw->protocol);
+
+ queue_tx(sw, buffer);
/* If the switch didn't buffer the packet, we need to send a copy. */
- if (ntohl(opi->buffer_id) == UINT32_MAX && actions_len > 0) {
- queue_tx(sw, rconn, ofputil_encode_packet_out(&po));
+ if (pi.buffer_id == UINT32_MAX && out_port != OFPP_NONE) {
+ queue_tx(sw, ofputil_encode_packet_out(&po, sw->protocol));
}
} else {
/* We don't know that MAC, or we don't set up flows. Send along the
* packet without setting up a flow. */
- if (ntohl(opi->buffer_id) != UINT32_MAX || actions_len > 0) {
- queue_tx(sw, rconn, ofputil_encode_packet_out(&po));
+ if (pi.buffer_id != UINT32_MAX || out_port != OFPP_NONE) {
+ queue_tx(sw, ofputil_encode_packet_out(&po, sw->protocol));
}
}
}
static void
-process_echo_request(struct lswitch *sw, struct rconn *rconn,
- const struct ofp_header *rq)
+process_echo_request(struct lswitch *sw, const struct ofp_header *rq)
{
- queue_tx(sw, rconn, make_echo_reply(rq));
+ queue_tx(sw, make_echo_reply(rq));
}
/*
- * Copyright (c) 2008, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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.
*
* specifying a particular queue. */
uint32_t default_queue;
- /* Maps from a port name to a queue_id (cast to void *). */
- const struct shash *port_queues;
+ /* Maps from a port name to a queue_id. */
+ const struct simap *port_queues;
+
+ /* If true, do not reply to any messages from the switch (for debugging
+ * fail-open mode). */
+ bool mute;
};
struct lswitch *lswitch_create(struct rconn *, const struct lswitch_config *);
+bool lswitch_is_alive(const struct lswitch *);
void lswitch_set_queue(struct lswitch *sw, uint32_t queue);
void lswitch_run(struct lswitch *);
void lswitch_wait(struct lswitch *);
void lswitch_destroy(struct lswitch *);
-void lswitch_process_packet(struct lswitch *, struct rconn *,
- const struct ofpbuf *);
+void lswitch_mute(struct lswitch *);
#endif /* learning-switch.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct list *
list_front(const struct list *list_)
{
- struct list *list = (struct list *) list_;
+ struct list *list = CONST_CAST(struct list *, list_);
assert(!list_is_empty(list));
return list->next;
struct list *
list_back(const struct list *list_)
{
- struct list *list = (struct list *) list_;
+ struct list *list = CONST_CAST(struct list *, list_);
assert(!list_is_empty(list));
return list->prev;
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
- /* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks
+ /* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
static struct hmap lock_table = HMAP_INITIALIZER(&lock_table);
static void lockfile_unhash(struct lockfile *);
-static int lockfile_try_lock(const char *name, bool block,
- struct lockfile **lockfilep);
+static int lockfile_try_lock(const char *name, struct lockfile **lockfilep);
/* Returns the name of the lockfile that would be created for locking a file
- * named 'file_name'. The caller is responsible for freeing the returned
- * name, with free(), when it is no longer needed. */
+ * named 'filename_'. The caller is responsible for freeing the returned name,
+ * with free(), when it is no longer needed. */
char *
-lockfile_name(const char *file_name)
+lockfile_name(const char *filename_)
{
- const char *slash = strrchr(file_name, '/');
- return (slash
- ? xasprintf("%.*s/.%s.~lock~",
- (int) (slash - file_name), file_name, slash + 1)
- : xasprintf(".%s.~lock~", file_name));
+ char *filename;
+ const char *slash;
+ char *lockname;
+
+ /* If 'filename_' is a symlink, base the name of the lockfile on the
+ * symlink's target rather than the name of the symlink. That way, if a
+ * file is symlinked, but there is no symlink for its lockfile, then there
+ * is only a single lockfile for both the source and the target of the
+ * symlink, not one for each. */
+ filename = follow_symlinks(filename_);
+ slash = strrchr(filename, '/');
+ lockname = (slash
+ ? xasprintf("%.*s/.%s.~lock~",
+ (int) (slash - filename), filename, slash + 1)
+ : xasprintf(".%s.~lock~", filename));
+ free(filename);
+
+ return lockname;
}
/* Locks the configuration file against modification by other processes and
* re-reads it from disk.
*
- * The 'timeout' specifies the maximum number of milliseconds to wait for the
- * config file to become free. Use 0 to avoid waiting or INT_MAX to wait
- * forever.
- *
* Returns 0 on success, otherwise a positive errno value. On success,
* '*lockfilep' is set to point to a new "struct lockfile *" that may be
* unlocked with lockfile_unlock(). On failure, '*lockfilep' is set to
- * NULL. */
+ * NULL. Will not block if the lock cannot be immediately acquired. */
int
-lockfile_lock(const char *file, int timeout, struct lockfile **lockfilep)
+lockfile_lock(const char *file, struct lockfile **lockfilep)
{
/* Only exclusive ("write") locks are supported. This is not a problem
* because the Open vSwitch code that currently uses lock files does so in
* stylized ways such that any number of readers may access a file while it
* is being written. */
- long long int warn_elapsed = 1000;
- long long int start, elapsed;
char *lock_name;
int error;
COVERAGE_INC(lockfile_lock);
lock_name = lockfile_name(file);
- time_refresh();
- start = time_msec();
-
- do {
- error = lockfile_try_lock(lock_name, timeout > 0, lockfilep);
- time_refresh();
- elapsed = time_msec() - start;
- if (elapsed > warn_elapsed) {
- warn_elapsed *= 2;
- VLOG_WARN("%s: waiting for lock file, %lld ms elapsed",
- lock_name, elapsed);
- }
- } while (error == EINTR && (timeout == INT_MAX || elapsed < timeout));
-
- if (error == EINTR) {
- COVERAGE_INC(lockfile_timeout);
- VLOG_WARN("%s: giving up on lock file after %lld ms",
- lock_name, elapsed);
- error = ETIMEDOUT;
- } else if (error) {
+
+ error = lockfile_try_lock(lock_name, lockfilep);
+
+ if (error) {
COVERAGE_INC(lockfile_error);
if (error == EACCES) {
error = EAGAIN;
}
- VLOG_WARN("%s: failed to lock file "
- "(after %lld ms, with %d-ms timeout): %s",
- lock_name, elapsed, timeout, strerror(error));
+ VLOG_WARN("%s: failed to lock file: %s",
+ lock_name, strerror(error));
}
free(lock_name);
}
static int
-lockfile_try_lock(const char *name, bool block, struct lockfile **lockfilep)
+lockfile_try_lock(const char *name, struct lockfile **lockfilep)
{
struct flock l;
struct stat s;
*lockfilep = NULL;
- /* Open the lock file, first creating it if necessary. */
- for (;;) {
- /* Check whether we've already got a lock on that file. */
- if (!stat(name, &s)) {
- if (lockfile_find(s.st_dev, s.st_ino)) {
- return EDEADLK;
- }
- } else if (errno != ENOENT) {
- VLOG_WARN("%s: failed to stat lock file: %s",
- name, strerror(errno));
- return errno;
- }
-
- /* Try to open an existing lock file. */
- fd = open(name, O_RDWR);
- if (fd >= 0) {
- break;
- } else if (errno != ENOENT) {
- VLOG_WARN("%s: failed to open lock file: %s",
- name, strerror(errno));
- return errno;
- }
-
- /* Try to create a new lock file. */
- VLOG_INFO("%s: lock file does not exist, creating", name);
- fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
- if (fd >= 0) {
- break;
- } else if (errno != EEXIST) {
- VLOG_WARN("%s: failed to create lock file: %s",
- name, strerror(errno));
- return errno;
+ /* Check whether we've already got a lock on that file. */
+ if (!stat(name, &s)) {
+ if (lockfile_find(s.st_dev, s.st_ino)) {
+ return EDEADLK;
}
+ } else if (errno != ENOENT) {
+ VLOG_WARN("%s: failed to stat lock file: %s",
+ name, strerror(errno));
+ return errno;
+ }
- /* Someone else created the lock file. Try again. */
+ /* Open the lock file. */
+ fd = open(name, O_RDWR | O_CREAT, 0600);
+ if (fd < 0) {
+ VLOG_WARN("%s: failed to open lock file: %s",
+ name, strerror(errno));
+ return errno;
}
/* Get the inode and device number for the lock table. */
l.l_len = 0;
time_disable_restart();
- error = fcntl(fd, block ? F_SETLKW : F_SETLK, &l) == -1 ? errno : 0;
+ error = fcntl(fd, F_SETLK, &l) == -1 ? errno : 0;
time_enable_restart();
if (!error) {
-/* Copyright (c) 2008, 2009 Nicira Networks
+/* Copyright (c) 2008, 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct lockfile;
char *lockfile_name(const char *file);
-int lockfile_lock(const char *file, int timeout, struct lockfile **);
+int lockfile_lock(const char *file, struct lockfile **);
void lockfile_unlock(struct lockfile *);
void lockfile_postfork(void);
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
--- /dev/null
+.SS "MEMORY COMMANDS"
+These commands report memory usage.
+.
+.IP "\fBmemory/show\fR"
+Displays some basic statistics about \fB\*(PN\fR's memory usage.
+\fB\*(PN\fR also logs this information soon after startup and
+periodically as its memory consumption grows.
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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 */
/*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
MFP_NONE,
true,
NXM_NX_TUN_ID, "NXM_NX_TUN_ID",
- 0, NULL,
+ NXM_NX_TUN_ID, "NXM_NX_TUN_ID",
+ }, {
+ MFF_METADATA, "metadata", NULL,
+ MF_FIELD_SIZES(be64),
+ MFM_FULLY, 0,
+ MFS_HEXADECIMAL,
+ MFP_NONE,
+ true,
+ OXM_OF_METADATA, "OXM_OF_METADATA",
+ OXM_OF_METADATA, "OXM_OF_METADATA",
}, {
MFF_IN_PORT, "in_port", NULL,
MF_FIELD_SIZES(be16),
MFS_HEXADECIMAL, \
MFP_NONE, \
true, \
- NXM_NX_REG(IDX), \
- "NXM_NX_REG" #IDX, \
- 0, NULL, \
+ NXM_NX_REG(IDX), "NXM_NX_REG" #IDX, \
+ NXM_NX_REG(IDX), "NXM_NX_REG" #IDX, \
}
#if FLOW_N_REGS > 0
REGISTER(0),
{
MFF_ETH_SRC, "eth_src", "dl_src",
MF_FIELD_SIZES(mac),
- MFM_NONE, FWW_DL_SRC,
+ MFM_FULLY, 0,
MFS_ETHERNET,
MFP_NONE,
true,
}, {
MFF_ETH_DST, "eth_dst", "dl_dst",
MF_FIELD_SIZES(mac),
- MFM_MCAST, 0,
+ MFM_FULLY, 0,
MFS_ETHERNET,
MFP_NONE,
true,
MFP_NONE,
true,
NXM_OF_VLAN_TCI, "NXM_OF_VLAN_TCI",
- 0, NULL,
+ NXM_OF_VLAN_TCI, "NXM_OF_VLAN_TCI",
}, {
- MFF_VLAN_VID, "dl_vlan", NULL,
+ MFF_DL_VLAN, "dl_vlan", NULL,
sizeof(ovs_be16), 12,
MFM_NONE, 0,
MFS_DECIMAL,
MFP_NONE,
true,
0, NULL,
+ 0, NULL,
+ }, {
+ MFF_VLAN_VID, "vlan_vid", NULL,
+ sizeof(ovs_be16), 12,
+ MFM_FULLY, 0,
+ MFS_DECIMAL,
+ MFP_NONE,
+ true,
+ OXM_OF_VLAN_VID, "OXM_OF_VLAN_VID",
OXM_OF_VLAN_VID, "OXM_OF_VLAN_VID",
}, {
- MFF_VLAN_PCP, "dl_vlan_pcp", NULL,
+ MFF_DL_VLAN_PCP, "dl_vlan_pcp", NULL,
1, 3,
MFM_NONE, 0,
MFS_DECIMAL,
MFP_NONE,
true,
0, NULL,
+ 0, NULL,
+ }, {
+ MFF_VLAN_PCP, "vlan_pcp", NULL,
+ 1, 3,
+ MFM_NONE, 0,
+ MFS_DECIMAL,
+ MFP_VLAN_VID,
+ true,
+ OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP",
OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP",
},
{
MFF_IPV4_SRC, "ip_src", "nw_src",
MF_FIELD_SIZES(be32),
- MFM_CIDR, 0,
+ MFM_FULLY, 0,
MFS_IPV4,
MFP_IPV4,
true,
}, {
MFF_IPV4_DST, "ip_dst", "nw_dst",
MF_FIELD_SIZES(be32),
- MFM_CIDR, 0,
+ MFM_FULLY, 0,
MFS_IPV4,
MFP_IPV4,
true,
{
MFF_IPV6_SRC, "ipv6_src", NULL,
MF_FIELD_SIZES(ipv6),
- MFM_CIDR, 0,
+ MFM_FULLY, 0,
MFS_IPV6,
MFP_IPV6,
true,
}, {
MFF_IPV6_DST, "ipv6_dst", NULL,
MF_FIELD_SIZES(ipv6),
- MFM_CIDR, 0,
+ MFM_FULLY, 0,
MFS_IPV6,
MFP_IPV6,
true,
{
MFF_IPV6_LABEL, "ipv6_label", NULL,
4, 20,
- MFM_NONE, FWW_IPV6_LABEL,
+ MFM_FULLY, 0,
MFS_HEXADECIMAL,
MFP_IPV6,
false,
MFP_IP_ANY,
true,
NXM_NX_IP_TTL, "NXM_NX_IP_TTL",
- 0, NULL,
+ NXM_NX_IP_TTL, "NXM_NX_IP_TTL",
}, {
MFF_IP_FRAG, "ip_frag", NULL,
1, 2,
MFP_IP_ANY,
false,
NXM_NX_IP_FRAG, "NXM_NX_IP_FRAG",
- 0, NULL,
+ NXM_NX_IP_FRAG, "NXM_NX_IP_FRAG",
},
{
}, {
MFF_ARP_SPA, "arp_spa", NULL,
MF_FIELD_SIZES(be32),
- MFM_CIDR, 0,
+ MFM_FULLY, 0,
MFS_IPV4,
MFP_ARP,
false,
}, {
MFF_ARP_TPA, "arp_tpa", NULL,
MF_FIELD_SIZES(be32),
- MFM_CIDR, 0,
+ MFM_FULLY, 0,
MFS_IPV4,
MFP_ARP,
false,
}, {
MFF_ARP_SHA, "arp_sha", NULL,
MF_FIELD_SIZES(mac),
- MFM_NONE, FWW_ARP_SHA,
+ MFM_FULLY, 0,
MFS_ETHERNET,
MFP_ARP,
false,
}, {
MFF_ARP_THA, "arp_tha", NULL,
MF_FIELD_SIZES(mac),
- MFM_NONE, FWW_ARP_THA,
+ MFM_FULLY, 0,
MFS_ETHERNET,
MFP_ARP,
false,
{
MFF_ND_TARGET, "nd_target", NULL,
MF_FIELD_SIZES(ipv6),
- MFM_CIDR, 0,
+ MFM_FULLY, 0,
MFS_IPV6,
MFP_ND,
false,
}, {
MFF_ND_SLL, "nd_sll", NULL,
MF_FIELD_SIZES(mac),
- MFM_NONE, FWW_ARP_SHA,
+ MFM_FULLY, 0,
MFS_ETHERNET,
MFP_ND_SOLICIT,
false,
}, {
MFF_ND_TLL, "nd_tll", NULL,
MF_FIELD_SIZES(mac),
- MFM_NONE, FWW_ARP_THA,
+ MFM_FULLY, 0,
MFS_ETHERNET,
MFP_ND_ADVERT,
false,
}
};
+/* Maps an NXM or OXM header value to an mf_field. */
struct nxm_field {
- struct hmap_node hmap_node;
- uint32_t nxm_header;
+ struct hmap_node hmap_node; /* In 'all_fields' hmap. */
+ uint32_t header; /* NXM or OXM header value. */
const struct mf_field *mf;
};
-static struct hmap all_nxm_fields = HMAP_INITIALIZER(&all_nxm_fields);
+/* Contains 'struct nxm_field's. */
+static struct hmap all_fields = HMAP_INITIALIZER(&all_fields);
/* Rate limit for parse errors. These always indicate a bug in an OpenFlow
* controller and so there's not much point in showing a lot of them. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+const struct mf_field *mf_from_nxm_header__(uint32_t header);
+
/* Returns the field with the given 'id'. */
const struct mf_field *
mf_from_id(enum mf_field_id id)
}
static void
-add_nxm_field(uint32_t nxm_header, const struct mf_field *mf)
+add_nxm_field(uint32_t header, const struct mf_field *mf)
{
struct nxm_field *f;
f = xmalloc(sizeof *f);
- hmap_insert(&all_nxm_fields, &f->hmap_node, hash_int(nxm_header, 0));
- f->nxm_header = nxm_header;
+ hmap_insert(&all_fields, &f->hmap_node, hash_int(header, 0));
+ f->header = header;
f->mf = mf;
}
static void
-nxm_init(void)
+nxm_init_add_field(const struct mf_field *mf, uint32_t header)
{
- const struct mf_field *mf;
-
- for (mf = mf_fields; mf < &mf_fields[MFF_N_IDS]; mf++) {
- if (mf->nxm_header) {
- add_nxm_field(mf->nxm_header, mf);
- if (mf->maskable != MFM_NONE) {
- add_nxm_field(NXM_MAKE_WILD_HEADER(mf->nxm_header), mf);
- }
+ if (header) {
+ assert(!mf_from_nxm_header__(header));
+ add_nxm_field(header, mf);
+ if (mf->maskable != MFM_NONE) {
+ add_nxm_field(NXM_MAKE_WILD_HEADER(header), mf);
}
}
+}
+
+static void
+nxm_init(void)
+{
+ const struct mf_field *mf;
-#ifndef NDEBUG
- /* Verify that the header values are unique. */
for (mf = mf_fields; mf < &mf_fields[MFF_N_IDS]; mf++) {
- if (mf->nxm_header) {
- assert(mf_from_nxm_header(mf->nxm_header) == mf);
- if (mf->maskable != MFM_NONE) {
- assert(mf_from_nxm_header(NXM_MAKE_WILD_HEADER(mf->nxm_header))
- == mf);
- }
+ nxm_init_add_field(mf, mf->nxm_header);
+ if (mf->oxm_header != mf->nxm_header) {
+ nxm_init_add_field(mf, mf->oxm_header);
}
}
-#endif
}
const struct mf_field *
mf_from_nxm_header(uint32_t header)
{
- const struct nxm_field *f;
-
- if (hmap_is_empty(&all_nxm_fields)) {
+ if (hmap_is_empty(&all_fields)) {
nxm_init();
}
+ return mf_from_nxm_header__(header);
+}
+
+const struct mf_field *
+mf_from_nxm_header__(uint32_t header)
+{
+ const struct nxm_field *f;
- HMAP_FOR_EACH_IN_BUCKET (f, hmap_node, hash_int(header, 0),
- &all_nxm_fields) {
- if (f->nxm_header == header) {
+ HMAP_FOR_EACH_IN_BUCKET (f, hmap_node, hash_int(header, 0), &all_fields) {
+ if (f->header == header) {
return f->mf;
}
}
{
switch (mf->id) {
case MFF_IN_PORT:
- case MFF_ETH_SRC:
case MFF_ETH_TYPE:
case MFF_IP_PROTO:
case MFF_IP_DSCP:
case MFF_IP_ECN:
case MFF_IP_TTL:
- case MFF_IPV6_LABEL:
case MFF_ARP_OP:
- case MFF_ARP_SHA:
- case MFF_ARP_THA:
- case MFF_ND_SLL:
- case MFF_ND_TLL:
assert(mf->fww_bit != 0);
return (wc->wildcards & mf->fww_bit) != 0;
case MFF_TUN_ID:
return !wc->tun_id_mask;
+ case MFF_METADATA:
+ return !wc->metadata_mask;
-#if FLOW_N_REGS > 0
- case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
- case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
- case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
- case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
- case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
- case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
- case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
- case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+ CASE_MFF_REGS:
return !wc->reg_masks[mf->id - MFF_REG0];
+ case MFF_ETH_SRC:
+ return eth_addr_is_zero(wc->dl_src_mask);
case MFF_ETH_DST:
- return ((wc->wildcards & (FWW_ETH_MCAST | FWW_DL_DST))
- == (FWW_ETH_MCAST | FWW_DL_DST));
+ return eth_addr_is_zero(wc->dl_dst_mask);
+
+ case MFF_ARP_SHA:
+ case MFF_ND_SLL:
+ return eth_addr_is_zero(wc->arp_sha_mask);
+
+ case MFF_ARP_THA:
+ case MFF_ND_TLL:
+ return eth_addr_is_zero(wc->arp_tha_mask);
case MFF_VLAN_TCI:
return !wc->vlan_tci_mask;
- case MFF_VLAN_VID:
+ case MFF_DL_VLAN:
return !(wc->vlan_tci_mask & htons(VLAN_VID_MASK));
+ case MFF_VLAN_VID:
+ return !(wc->vlan_tci_mask & htons(VLAN_VID_MASK | VLAN_CFI));
+ case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
return !(wc->vlan_tci_mask & htons(VLAN_PCP_MASK));
case MFF_IPV6_DST:
return ipv6_mask_is_any(&wc->ipv6_dst_mask);
+ case MFF_IPV6_LABEL:
+ return !wc->ipv6_label_mask;
+
case MFF_ND_TARGET:
return ipv6_mask_is_any(&wc->nd_target_mask);
{
switch (mf->id) {
case MFF_IN_PORT:
- case MFF_ETH_SRC:
case MFF_ETH_TYPE:
case MFF_IP_PROTO:
case MFF_IP_DSCP:
case MFF_IP_ECN:
case MFF_IP_TTL:
- case MFF_IPV6_LABEL:
case MFF_ARP_OP:
- case MFF_ARP_SHA:
- case MFF_ARP_THA:
- case MFF_ND_SLL:
- case MFF_ND_TLL:
assert(mf->fww_bit != 0);
memset(mask, wc->wildcards & mf->fww_bit ? 0x00 : 0xff, mf->n_bytes);
break;
case MFF_TUN_ID:
mask->be64 = wc->tun_id_mask;
break;
+ case MFF_METADATA:
+ mask->be64 = wc->metadata_mask;
+ break;
-#if FLOW_N_REGS > 0
- case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
- case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
- case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
- case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
- case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
- case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
- case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
- case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+ CASE_MFF_REGS:
mask->be32 = htonl(wc->reg_masks[mf->id - MFF_REG0]);
break;
case MFF_ETH_DST:
- memcpy(mask->mac, flow_wildcards_to_dl_dst_mask(wc->wildcards),
- ETH_ADDR_LEN);
+ memcpy(mask->mac, wc->dl_dst_mask, ETH_ADDR_LEN);
+ break;
+
+ case MFF_ETH_SRC:
+ memcpy(mask->mac, wc->dl_src_mask, ETH_ADDR_LEN);
break;
case MFF_VLAN_TCI:
mask->be16 = wc->vlan_tci_mask;
break;
- case MFF_VLAN_VID:
+ case MFF_DL_VLAN:
mask->be16 = wc->vlan_tci_mask & htons(VLAN_VID_MASK);
break;
+ case MFF_VLAN_VID:
+ mask->be16 = wc->vlan_tci_mask & htons(VLAN_VID_MASK | VLAN_CFI);
+ break;
+ case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
mask->u8 = vlan_tci_to_pcp(wc->vlan_tci_mask);
break;
case MFF_IPV6_DST:
mask->ipv6 = wc->ipv6_dst_mask;
break;
+ case MFF_IPV6_LABEL:
+ mask->be32 = wc->ipv6_label_mask;
+ break;
case MFF_ND_TARGET:
mask->ipv6 = wc->nd_target_mask;
case MFF_ARP_TPA:
mask->be32 = wc->nw_dst_mask;
break;
+ case MFF_ARP_SHA:
+ case MFF_ND_SLL:
+ memcpy(mask->mac, wc->arp_sha_mask, ETH_ADDR_LEN);
+ break;
+ case MFF_ARP_THA:
+ case MFF_ND_TLL:
+ memcpy(mask->mac, wc->arp_tha_mask, ETH_ADDR_LEN);
+ break;
case MFF_TCP_SRC:
case MFF_UDP_SRC:
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();
return flow->dl_type == htons(ETH_TYPE_IP);
case MFP_IPV6:
return flow->dl_type == htons(ETH_TYPE_IPV6);
+ case MFP_VLAN_VID:
+ return (flow->vlan_tci & htons(VLAN_CFI)) != 0;
case MFP_IP_ANY:
return is_ip_any(flow);
{
switch (mf->id) {
case MFF_TUN_ID:
+ case MFF_METADATA:
case MFF_IN_PORT:
-#if FLOW_N_REGS > 0
- case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
- case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
- case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
- case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
- case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
- case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
- case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
- case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+ CASE_MFF_REGS:
case MFF_ETH_SRC:
case MFF_ETH_DST:
case MFF_ETH_TYPE:
case MFF_ARP_OP:
return !(value->be16 & htons(0xff00));
- case MFF_VLAN_VID:
+ case MFF_DL_VLAN:
return !(value->be16 & htons(VLAN_CFI | VLAN_PCP_MASK));
+ case MFF_VLAN_VID:
+ return !(value->be16 & htons(VLAN_PCP_MASK));
+ case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
- return !(value->u8 & ~7);
+ return !(value->u8 & ~(VLAN_PCP_MASK >> VLAN_PCP_SHIFT));
case MFF_IPV6_LABEL:
return !(value->be32 & ~htonl(IPV6_LABEL_MASK));
case MFF_TUN_ID:
value->be64 = flow->tun_id;
break;
+ case MFF_METADATA:
+ value->be64 = flow->metadata;
+ break;
case MFF_IN_PORT:
value->be16 = htons(flow->in_port);
break;
-#if FLOW_N_REGS > 0
- case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
- case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
- case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
- case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
- case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
- case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
- case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
- case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+ CASE_MFF_REGS:
value->be32 = htonl(flow->regs[mf->id - MFF_REG0]);
break;
value->be16 = flow->vlan_tci;
break;
- case MFF_VLAN_VID:
+ case MFF_DL_VLAN:
value->be16 = flow->vlan_tci & htons(VLAN_VID_MASK);
break;
+ case MFF_VLAN_VID:
+ value->be16 = flow->vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI);
+ break;
+ case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
value->u8 = vlan_tci_to_pcp(flow->vlan_tci);
break;
break;
case MFF_TCP_SRC:
- value->be16 = flow->tp_src;
- break;
-
- case MFF_TCP_DST:
- value->be16 = flow->tp_dst;
- break;
-
case MFF_UDP_SRC:
value->be16 = flow->tp_src;
break;
+ case MFF_TCP_DST:
case MFF_UDP_DST:
value->be16 = flow->tp_dst;
break;
case MFF_TUN_ID:
cls_rule_set_tun_id(rule, value->be64);
break;
+ case MFF_METADATA:
+ cls_rule_set_metadata(rule, value->be64);
+ break;
case MFF_IN_PORT:
cls_rule_set_in_port(rule, ntohs(value->be16));
break;
-#if FLOW_N_REGS > 0
- case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
- case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
- case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
- case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
- case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
- case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
- case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
- case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
-#if FLOW_N_REGS > 0
+ CASE_MFF_REGS:
cls_rule_set_reg(rule, mf->id - MFF_REG0, ntohl(value->be32));
break;
-#endif
case MFF_ETH_SRC:
cls_rule_set_dl_src(rule, value->mac);
cls_rule_set_dl_tci(rule, value->be16);
break;
- case MFF_VLAN_VID:
+ case MFF_DL_VLAN:
cls_rule_set_dl_vlan(rule, value->be16);
break;
+ case MFF_VLAN_VID:
+ cls_rule_set_vlan_vid(rule, value->be16);
+ break;
+ case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
cls_rule_set_dl_vlan_pcp(rule, value->u8);
break;
break;
case MFF_TCP_SRC:
- cls_rule_set_tp_src(rule, value->be16);
- break;
-
- case MFF_TCP_DST:
- cls_rule_set_tp_dst(rule, value->be16);
- break;
-
case MFF_UDP_SRC:
cls_rule_set_tp_src(rule, value->be16);
break;
+ case MFF_TCP_DST:
case MFF_UDP_DST:
cls_rule_set_tp_dst(rule, value->be16);
break;
case MFF_TUN_ID:
flow->tun_id = value->be64;
break;
+ case MFF_METADATA:
+ flow->metadata = value->be64;
+ break;
case MFF_IN_PORT:
flow->in_port = ntohs(value->be16);
break;
-#if FLOW_N_REGS > 0
- case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
- case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
- case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
- case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
- case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
- case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
- case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
- case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
-#if FLOW_N_REGS > 0
+ CASE_MFF_REGS:
flow->regs[mf->id - MFF_REG0] = ntohl(value->be32);
break;
-#endif
case MFF_ETH_SRC:
memcpy(flow->dl_src, value->mac, ETH_ADDR_LEN);
flow->vlan_tci = value->be16;
break;
+ case MFF_DL_VLAN:
+ flow_set_dl_vlan(flow, value->be16);
+ break;
case MFF_VLAN_VID:
flow_set_vlan_vid(flow, value->be16);
break;
+ case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
flow_set_vlan_pcp(flow, value->u8);
break;
}
}
+/* Returns true if 'mf' has a zero value in 'flow', false if it is nonzero.
+ *
+ * The caller is responsible for ensuring that 'flow' meets 'mf''s
+ * prerequisites. */
+bool
+mf_is_zero(const struct mf_field *mf, const struct flow *flow)
+{
+ union mf_value value;
+
+ mf_get_value(mf, flow, &value);
+ return is_all_zeros((const uint8_t *) &value, mf->n_bytes);
+}
+
/* Makes 'rule' wildcard field 'mf'.
*
* The caller is responsible for ensuring that 'rule' meets 'mf''s
case MFF_TUN_ID:
cls_rule_set_tun_id_masked(rule, htonll(0), htonll(0));
break;
+ case MFF_METADATA:
+ cls_rule_set_metadata_masked(rule, htonll(0), htonll(0));
case MFF_IN_PORT:
rule->wc.wildcards |= FWW_IN_PORT;
rule->flow.in_port = 0;
break;
-#if FLOW_N_REGS > 0
- case MFF_REG0:
- cls_rule_set_reg_masked(rule, 0, 0, 0);
- break;
-#endif
-#if FLOW_N_REGS > 1
- case MFF_REG1:
- cls_rule_set_reg_masked(rule, 1, 0, 0);
- break;
-#endif
-#if FLOW_N_REGS > 2
- case MFF_REG2:
- cls_rule_set_reg_masked(rule, 2, 0, 0);
- break;
-#endif
-#if FLOW_N_REGS > 3
- case MFF_REG3:
- cls_rule_set_reg_masked(rule, 3, 0, 0);
- break;
-#endif
-#if FLOW_N_REGS > 4
- case MFF_REG4:
- cls_rule_set_reg_masked(rule, 4, 0, 0);
- break;
-#endif
-#if FLOW_N_REGS > 5
- case MFF_REG5:
- cls_rule_set_reg_masked(rule, 5, 0, 0);
- break;
-#endif
-#if FLOW_N_REGS > 6
- case MFF_REG6:
- cls_rule_set_reg_masked(rule, 6, 0, 0);
- break;
-#endif
-#if FLOW_N_REGS > 7
- case MFF_REG7:
- cls_rule_set_reg_masked(rule, 7, 0, 0);
+ CASE_MFF_REGS:
+ cls_rule_set_reg_masked(rule, mf->id - MFF_REG0, 0, 0);
break;
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
case MFF_ETH_SRC:
- rule->wc.wildcards |= FWW_DL_SRC;
- memset(rule->flow.dl_src, 0, sizeof rule->flow.dl_src);
+ memset(rule->flow.dl_src, 0, ETH_ADDR_LEN);
+ memset(rule->wc.dl_src_mask, 0, ETH_ADDR_LEN);
break;
case MFF_ETH_DST:
- rule->wc.wildcards |= FWW_DL_DST | FWW_ETH_MCAST;
- memset(rule->flow.dl_dst, 0, sizeof rule->flow.dl_dst);
+ memset(rule->flow.dl_dst, 0, ETH_ADDR_LEN);
+ memset(rule->wc.dl_dst_mask, 0, ETH_ADDR_LEN);
break;
case MFF_ETH_TYPE:
cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
break;
+ case MFF_DL_VLAN:
case MFF_VLAN_VID:
cls_rule_set_any_vid(rule);
break;
+ case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
cls_rule_set_any_pcp(rule);
break;
break;
case MFF_IPV6_LABEL:
- rule->wc.wildcards |= FWW_IPV6_LABEL;
+ rule->wc.ipv6_label_mask = 0;
rule->flow.ipv6_label = 0;
break;
case MFF_ARP_SHA:
case MFF_ND_SLL:
- rule->wc.wildcards |= FWW_ARP_SHA;
- memset(rule->flow.arp_sha, 0, sizeof rule->flow.arp_sha);
+ memset(rule->flow.arp_sha, 0, ETH_ADDR_LEN);
+ memset(rule->wc.arp_sha_mask, 0, ETH_ADDR_LEN);
break;
case MFF_ARP_THA:
case MFF_ND_TLL:
- rule->wc.wildcards |= FWW_ARP_THA;
- memset(rule->flow.arp_tha, 0, sizeof rule->flow.arp_tha);
+ memset(rule->flow.arp_tha, 0, ETH_ADDR_LEN);
+ memset(rule->wc.arp_tha_mask, 0, ETH_ADDR_LEN);
break;
case MFF_TCP_SRC:
switch (mf->id) {
case MFF_IN_PORT:
- case MFF_ETH_SRC:
case MFF_ETH_TYPE:
- case MFF_VLAN_VID:
+ case MFF_DL_VLAN:
+ case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
- case MFF_IPV6_LABEL:
case MFF_IP_PROTO:
case MFF_IP_TTL:
case MFF_IP_DSCP:
case MFF_IP_ECN:
case MFF_ARP_OP:
- case MFF_ARP_SHA:
- case MFF_ARP_THA:
case MFF_ICMPV4_TYPE:
case MFF_ICMPV4_CODE:
case MFF_ICMPV6_TYPE:
case MFF_ICMPV6_CODE:
- case MFF_ND_SLL:
- case MFF_ND_TLL:
NOT_REACHED();
case MFF_TUN_ID:
cls_rule_set_tun_id_masked(rule, value->be64, mask->be64);
break;
+ case MFF_METADATA:
+ cls_rule_set_metadata_masked(rule, value->be64, mask->be64);
+ break;
-#if FLOW_N_REGS > 0
- case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
- case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
- case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
- case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
- case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
- case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
- case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
- case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+ CASE_MFF_REGS:
cls_rule_set_reg_masked(rule, mf->id - MFF_REG0,
ntohl(value->be32), ntohl(mask->be32));
break;
case MFF_ETH_DST:
- if (flow_wildcards_is_dl_dst_mask_valid(mask->mac)) {
- cls_rule_set_dl_dst_masked(rule, value->mac, mask->mac);
- }
+ cls_rule_set_dl_dst_masked(rule, value->mac, mask->mac);
+ break;
+
+ case MFF_ETH_SRC:
+ cls_rule_set_dl_src_masked(rule, value->mac, mask->mac);
+ break;
+
+ case MFF_ARP_SHA:
+ case MFF_ND_SLL:
+ cls_rule_set_arp_sha_masked(rule, value->mac, mask->mac);
+ break;
+
+ case MFF_ARP_THA:
+ case MFF_ND_TLL:
+ cls_rule_set_arp_tha_masked(rule, value->mac, mask->mac);
break;
case MFF_VLAN_TCI:
cls_rule_set_dl_tci_masked(rule, value->be16, mask->be16);
break;
+ case MFF_VLAN_VID:
+ cls_rule_set_vlan_vid_masked(rule, value->be16, mask->be16);
+ break;
+
case MFF_IPV4_SRC:
cls_rule_set_nw_src_masked(rule, value->be32, mask->be32);
break;
cls_rule_set_ipv6_dst_masked(rule, &value->ipv6, &mask->ipv6);
break;
+ case MFF_IPV6_LABEL:
+ if ((mask->be32 & htonl(IPV6_LABEL_MASK)) == htonl(IPV6_LABEL_MASK)) {
+ mf_set_value(mf, value, rule);
+ } else {
+ cls_rule_set_ipv6_label_masked(rule, value->be32, mask->be32);
+ }
+ break;
+
case MFF_ND_TARGET:
cls_rule_set_nd_target_masked(rule, &value->ipv6, &mask->ipv6);
break;
switch (mf->id) {
case MFF_TUN_ID:
+ case MFF_METADATA:
case MFF_IN_PORT:
-#if FLOW_N_REGS > 0
- case MFF_REG0:
-#endif
-#if FLOW_N_REGS > 1
- case MFF_REG1:
-#endif
-#if FLOW_N_REGS > 2
- case MFF_REG2:
-#endif
-#if FLOW_N_REGS > 3
- case MFF_REG3:
-#endif
-#if FLOW_N_REGS > 4
- case MFF_REG4:
-#endif
-#if FLOW_N_REGS > 5
- case MFF_REG5:
-#endif
-#if FLOW_N_REGS > 6
- case MFF_REG6:
-#endif
-#if FLOW_N_REGS > 7
- case MFF_REG7:
-#endif
-#if FLOW_N_REGS > 8
-#error
-#endif
+ CASE_MFF_REGS:
case MFF_ETH_SRC:
case MFF_ETH_DST:
case MFF_ETH_TYPE:
value->be16 &= htons(0xff);
break;
- case MFF_VLAN_VID:
+ case MFF_DL_VLAN:
value->be16 &= htons(VLAN_VID_MASK);
break;
+ case MFF_VLAN_VID:
+ value->be16 &= htons(VLAN_VID_MASK | VLAN_CFI);
+ break;
+ case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
value->u8 &= 0x07;
break;
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;
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:
\f
/* Makes subfield 'sf' within 'rule' exactly match the 'sf->n_bits'
* least-significant bits in 'x'.
- *
- * See mf_set_subfield() for an example.
- *
- * The difference between this function and mf_set_subfield() is that the
- * latter function can only handle subfields up to 64 bits wide, whereas this
- * one handles the general case. On the other hand, mf_set_subfield() is
- * arguably easier to use. */
+ */
void
mf_write_subfield(const struct mf_subfield *sf, const union mf_subvalue *x,
struct cls_rule *rule)
mf_set(field, &value, &mask, rule);
}
-/* Makes subfield 'sf' within 'rule' exactly match the 'sf->n_bits'
- * least-significant bits of 'x'.
- *
- * Example: suppose that 'sf->field' is originally the following 2-byte field
- * in 'rule':
- *
- * value == 0xe00a == 2#1110000000001010
- * mask == 0xfc3f == 2#1111110000111111
- *
- * The call mf_set_subfield(sf, 0x55, 8, 7, rule), where sf->ofs == 8 and
- * sf->n_bits == 7 would have the following effect (note that 0x55 is
- * 2#1010101):
- *
- * value == 0xd50a == 2#1101010100001010
- * mask == 0xff3f == 2#1111111100111111
- * ^^^^^^^ affected bits
- *
- * The caller is responsible for ensuring that the result will be a valid
- * wildcard pattern for 'sf->field'. The caller is responsible for ensuring
- * that 'rule' meets 'sf->field''s prerequisites. */
-void
-mf_set_subfield(const struct mf_subfield *sf, uint64_t x,
- struct cls_rule *rule)
-{
- const struct mf_field *field = sf->field;
- unsigned int n_bits = sf->n_bits;
- unsigned int ofs = sf->ofs;
-
- if (ofs == 0 && field->n_bytes * 8 == n_bits) {
- union mf_value value;
- int i;
-
- for (i = field->n_bytes - 1; i >= 0; i--) {
- ((uint8_t *) &value)[i] = x;
- x >>= 8;
- }
- mf_set_value(field, &value, rule);
- } else {
- union mf_value value, mask;
- uint8_t *vp = (uint8_t *) &value;
- uint8_t *mp = (uint8_t *) &mask;
-
- mf_get(field, rule, &value, &mask);
- bitwise_put(x, vp, field->n_bytes, ofs, n_bits);
- bitwise_put(UINT64_MAX, mp, field->n_bytes, ofs, n_bits);
- mf_set(field, &value, &mask, rule);
- }
-}
-
-/* Similar to mf_set_subfield() but modifies only a flow, not a cls_rule. */
-void
-mf_set_subfield_value(const struct mf_subfield *sf, uint64_t x,
- struct flow *flow)
-{
- const struct mf_field *field = sf->field;
- unsigned int n_bits = sf->n_bits;
- unsigned int ofs = sf->ofs;
- union mf_value value;
-
- if (ofs == 0 && field->n_bytes * 8 == n_bits) {
- int i;
-
- for (i = field->n_bytes - 1; i >= 0; i--) {
- ((uint8_t *) &value)[i] = x;
- x >>= 8;
- }
- mf_set_flow_value(field, &value, flow);
- } else {
- mf_get_value(field, flow, &value);
- bitwise_put(x, &value, field->n_bytes, ofs, n_bits);
- mf_set_flow_value(field, &value, flow);
- }
-}
-
/* Initializes 'x' to the value of 'sf' within 'flow'. 'sf' must be valid for
* reading 'flow', e.g. as checked by mf_check_src(). */
void
&& mf->nxm_name[name_len] == '\0') {
return mf;
}
+ if (mf->oxm_name
+ && !strncmp(mf->oxm_name, name, name_len)
+ && mf->oxm_name[name_len] == '\0') {
+ return mf;
+ }
}
return NULL;
/*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
enum mf_field_id {
/* Metadata. */
MFF_TUN_ID, /* be64 */
+ MFF_METADATA, /* be64 */
MFF_IN_PORT, /* be16 */
#if FLOW_N_REGS > 0
#endif
#if FLOW_N_REGS > 7
MFF_REG7, /* be32 */
-#endif
-#if FLOW_N_REGS > 8
-#error
#endif
/* L2. */
MFF_ETH_TYPE, /* be16 */
MFF_VLAN_TCI, /* be16 */
- MFF_VLAN_VID, /* be16 */
- MFF_VLAN_PCP, /* u8 */
+ MFF_DL_VLAN, /* be16 (OpenFlow 1.0 compatibility) */
+ MFF_VLAN_VID, /* be16 (OpenFlow 1.2 compatibility) */
+ MFF_DL_VLAN_PCP, /* u8 (OpenFlow 1.0 compatibility) */
+ MFF_VLAN_PCP, /* be16 (OpenFlow 1.2 compatibility) */
/* L3. */
MFF_IPV4_SRC, /* be32 */
MFF_N_IDS
};
+/* Use this macro as CASE_MFF_REGS: in a switch statement to choose all of the
+ * MFF_REGx cases. */
+#if FLOW_N_REGS == 1
+# define CASE_MFF_REGS \
+ case MFF_REG0
+#elif FLOW_N_REGS == 2
+# define CASE_MFF_REGS \
+ case MFF_REG0: case MFF_REG1
+#elif FLOW_N_REGS == 3
+# define CASE_MFF_REGS \
+ case MFF_REG0: case MFF_REG1: case MFF_REG2
+#elif FLOW_N_REGS == 4
+# define CASE_MFF_REGS \
+ case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3
+#elif FLOW_N_REGS == 5
+# define CASE_MFF_REGS \
+ case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3: \
+ case MFF_REG4
+#elif FLOW_N_REGS == 6
+# define CASE_MFF_REGS \
+ case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3: \
+ case MFF_REG4: case MFF_REG5
+#elif FLOW_N_REGS == 7
+# define CASE_MFF_REGS \
+ case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3: \
+ case MFF_REG4: case MFF_REG5: case MFF_REG6
+#elif FLOW_N_REGS == 8
+# define CASE_MFF_REGS \
+ case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3: \
+ case MFF_REG4: case MFF_REG5: case MFF_REG6: case MFF_REG7
+#else
+# error
+#endif
+
/* Prerequisites for matching a field.
*
* A field may only be matched if the correct lower-level protocols are also
/* L2 requirements. */
MFP_ARP,
+ MFP_VLAN_VID,
MFP_IPV4,
MFP_IPV6,
MFP_IP_ANY,
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. */
enum mf_prereqs prereqs;
bool writable; /* May be written by actions? */
- /* NXM properties.
+ /* NXM and OXM properties.
*
- * A few "mf_field"s don't correspond to NXM fields. Those have 0 and
- * NULL for the following members, respectively. */
- uint32_t nxm_header; /* An NXM_* constant (a few fields have 0). */
- const char *nxm_name; /* The "NXM_*" constant's name. */
-
- /* OXM properties */
- uint32_t oxm_header; /* Field id in the OXM basic class,
- * an OXM_* constant.
- * Ignored if oxm_name is NULL */
- const char *oxm_name; /* The OXM_* constant's name,
- * NULL if the field is not present
- * in the OXM basic class */
-
+ * There are the following possibilities for these members for a given
+ * mf_field:
+ *
+ * - Neither NXM nor OXM defines such a field: these members will all be
+ * zero or NULL.
+ *
+ * - NXM and OXM both define such a field: nxm_header and oxm_header will
+ * both be nonzero and different, similarly for nxm_name and oxm_name.
+ *
+ * - Only NXM or only OXM defines such a field: nxm_header and oxm_header
+ * will both have the same value (either an OXM_* or NXM_* value) and
+ * similarly for nxm_name and oxm_name.
+ *
+ * Thus, 'nxm_header' is the appropriate header to use when outputting an
+ * NXM formatted match, since it will be an NXM_* constant when possible
+ * for compatibility with OpenFlow implementations that expect that, with
+ * OXM_* constants used for fields that OXM adds. Conversely, 'oxm_header'
+ * is the header to use when outputting an OXM formatted match. */
+ uint32_t nxm_header; /* An NXM_* (or OXM_*) constant. */
+ const char *nxm_name; /* The nxm_header constant's name. */
+ uint32_t oxm_header; /* An OXM_* (or NXM_*) constant. */
+ const char *oxm_name; /* The oxm_header constant's name */
};
/* The representation of a field's value. */
struct cls_rule *);
void mf_set_flow_value(const struct mf_field *, const union mf_value *value,
struct flow *);
+bool mf_is_zero(const struct mf_field *, const struct flow *);
void mf_get(const struct mf_field *, const struct cls_rule *,
union mf_value *value, union mf_value *mask);
/* Subfields. */
void mf_write_subfield(const struct mf_subfield *, const union mf_subvalue *,
struct cls_rule *);
-void mf_set_subfield(const struct mf_subfield *, uint64_t value,
- struct cls_rule *);
-void mf_set_subfield_value(const struct mf_subfield *, uint64_t value,
- struct flow *);
void mf_read_subfield(const struct mf_subfield *, const struct flow *,
union mf_subvalue *);
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <sys/types.h>
#include <netinet/in.h>
#include "dynamic-string.h"
-#include "meta-flow.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "openflow/nicira-ext.h"
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
\f
-/* multipath_check(). */
+/* Converts 'nam' into 'mp'. Returns 0 if successful, otherwise an
+ * OFPERR_*. */
enum ofperr
-multipath_check(const struct nx_action_multipath *mp, const struct flow *flow)
+multipath_from_openflow(const struct nx_action_multipath *nam,
+ struct ofpact_multipath *mp)
{
- uint32_t n_links = ntohs(mp->max_link) + 1;
+ uint32_t n_links = ntohs(nam->max_link) + 1;
size_t min_n_bits = log_2_ceil(n_links);
- struct mf_subfield dst;
- enum ofperr error;
- nxm_decode(&dst, mp->dst, mp->ofs_nbits);
- error = mf_check_dst(&dst, flow);
- if (error) {
- return error;
- }
-
- if (!flow_hash_fields_valid(ntohs(mp->fields))) {
- VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, ntohs(mp->fields));
- } else if (mp->algorithm != htons(NX_MP_ALG_MODULO_N)
- && mp->algorithm != htons(NX_MP_ALG_HASH_THRESHOLD)
- && mp->algorithm != htons(NX_MP_ALG_HRW)
- && mp->algorithm != htons(NX_MP_ALG_ITER_HASH)) {
- VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16,
- ntohs(mp->algorithm));
- } else if (dst.n_bits < min_n_bits) {
+ ofpact_init_MULTIPATH(mp);
+ mp->fields = ntohs(nam->fields);
+ mp->basis = ntohs(nam->basis);
+ mp->algorithm = ntohs(nam->algorithm);
+ mp->max_link = ntohs(nam->max_link);
+ mp->arg = ntohl(nam->arg);
+ mp->dst.field = mf_from_nxm_header(ntohl(nam->dst));
+ mp->dst.ofs = nxm_decode_ofs(nam->ofs_nbits);
+ mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits);
+
+ if (!flow_hash_fields_valid(mp->fields)) {
+ VLOG_WARN_RL(&rl, "unsupported fields %d", (int) mp->fields);
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ } else if (mp->algorithm != NX_MP_ALG_MODULO_N
+ && mp->algorithm != NX_MP_ALG_HASH_THRESHOLD
+ && mp->algorithm != NX_MP_ALG_HRW
+ && mp->algorithm != NX_MP_ALG_ITER_HASH) {
+ VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) mp->algorithm);
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ } else if (mp->dst.n_bits < min_n_bits) {
VLOG_WARN_RL(&rl, "multipath action requires at least %zu bits for "
"%"PRIu32" links", min_n_bits, n_links);
- } else {
- return 0;
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- return OFPERR_OFPBAC_BAD_ARGUMENT;
+ return multipath_check(mp, NULL);
+}
+
+/* Checks that 'mp' is valid on flow. Returns 0 if it is valid, otherwise an
+ * OFPERR_*. */
+enum ofperr
+multipath_check(const struct ofpact_multipath *mp,
+ const struct flow *flow)
+{
+ return mf_check_dst(&mp->dst, flow);
+}
+
+/* Converts 'mp' into an OpenFlow NXAST_MULTIPATH action, which it appends to
+ * 'openflow'. */
+void
+multipath_to_nxast(const struct ofpact_multipath *mp, struct ofpbuf *openflow)
+{
+ struct nx_action_multipath *nam = ofputil_put_NXAST_MULTIPATH(openflow);
+
+ nam->fields = htons(mp->fields);
+ nam->basis = htons(mp->basis);
+ nam->algorithm = htons(mp->algorithm);
+ nam->max_link = htons(mp->max_link);
+ nam->arg = htonl(mp->arg);
+ nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits);
+ nam->dst = htonl(mp->dst.field->nxm_header);
}
\f
/* multipath_execute(). */
static uint16_t multipath_algorithm(uint32_t hash, enum nx_mp_algorithm,
unsigned int n_links, unsigned int arg);
+/* Executes 'mp' based on the current contents of 'flow', writing the results
+ * back into 'flow'. */
void
-multipath_execute(const struct nx_action_multipath *mp, struct flow *flow)
+multipath_execute(const struct ofpact_multipath *mp, struct flow *flow)
{
/* Calculate value to store. */
- uint32_t hash = flow_hash_fields(flow, ntohs(mp->fields),
- ntohs(mp->basis));
- uint16_t link = multipath_algorithm(hash, ntohs(mp->algorithm),
- ntohs(mp->max_link) + 1,
- ntohl(mp->arg));
- struct mf_subfield dst;
-
- nxm_decode(&dst, mp->dst, mp->ofs_nbits);
- mf_set_subfield_value(&dst, link, flow);
+ uint32_t hash = flow_hash_fields(flow, mp->fields, mp->basis);
+ uint16_t link = multipath_algorithm(hash, mp->algorithm,
+ mp->max_link + 1, mp->arg);
+
+ nxm_reg_load(&mp->dst, link, flow);
}
static uint16_t
NOT_REACHED();
}
\f
-/* multipath_parse(). */
-
+/* Parses 's_' as a set of arguments to the "multipath" action and initializes
+ * 'mp' accordingly. ovs-ofctl(8) describes the format parsed.
+ *
+ * Prints an error on stderr and aborts the program if 's_' syntax is
+ * invalid. */
void
-multipath_parse(struct nx_action_multipath *mp, const char *s_)
+multipath_parse(struct ofpact_multipath *mp, const char *s_)
{
char *s = xstrdup(s_);
char *save_ptr = NULL;
- char *fields, *basis, *algorithm, *n_links_str, *arg, *dst_s;
- struct mf_subfield dst;
+ char *fields, *basis, *algorithm, *n_links_str, *arg, *dst;
int n_links;
fields = strtok_r(s, ", ", &save_ptr);
algorithm = strtok_r(NULL, ", ", &save_ptr);
n_links_str = strtok_r(NULL, ", ", &save_ptr);
arg = strtok_r(NULL, ", ", &save_ptr);
- dst_s = strtok_r(NULL, ", ", &save_ptr);
- if (!dst_s) {
+ dst = strtok_r(NULL, ", ", &save_ptr);
+ if (!dst) {
ovs_fatal(0, "%s: not enough arguments to multipath action", s_);
}
- ofputil_init_NXAST_MULTIPATH(mp);
+ ofpact_init_MULTIPATH(mp);
if (!strcasecmp(fields, "eth_src")) {
- mp->fields = htons(NX_HASH_FIELDS_ETH_SRC);
+ mp->fields = NX_HASH_FIELDS_ETH_SRC;
} else if (!strcasecmp(fields, "symmetric_l4")) {
- mp->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4);
+ mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
} else {
ovs_fatal(0, "%s: unknown fields `%s'", s_, fields);
}
- mp->basis = htons(atoi(basis));
+ mp->basis = atoi(basis);
if (!strcasecmp(algorithm, "modulo_n")) {
- mp->algorithm = htons(NX_MP_ALG_MODULO_N);
+ mp->algorithm = NX_MP_ALG_MODULO_N;
} else if (!strcasecmp(algorithm, "hash_threshold")) {
- mp->algorithm = htons(NX_MP_ALG_HASH_THRESHOLD);
+ mp->algorithm = NX_MP_ALG_HASH_THRESHOLD;
} else if (!strcasecmp(algorithm, "hrw")) {
- mp->algorithm = htons(NX_MP_ALG_HRW);
+ mp->algorithm = NX_MP_ALG_HRW;
} else if (!strcasecmp(algorithm, "iter_hash")) {
- mp->algorithm = htons(NX_MP_ALG_ITER_HASH);
+ mp->algorithm = NX_MP_ALG_ITER_HASH;
} else {
ovs_fatal(0, "%s: unknown algorithm `%s'", s_, algorithm);
}
ovs_fatal(0, "%s: n_links %d is not in valid range 1 to 65536",
s_, n_links);
}
- mp->max_link = htons(n_links - 1);
- mp->arg = htonl(atoi(arg));
+ mp->max_link = n_links - 1;
+ mp->arg = atoi(arg);
- mf_parse_subfield(&dst, dst_s);
- if (dst.n_bits < 16 && n_links > (1u << dst.n_bits)) {
+ mf_parse_subfield(&mp->dst, dst);
+ if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) {
ovs_fatal(0, "%s: %d-bit destination field has %u possible values, "
"less than specified n_links %d",
- s_, dst.n_bits, 1u << dst.n_bits, n_links);
+ s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links);
}
- mp->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
- mp->dst = htonl(dst.field->nxm_header);
free(s);
}
+/* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8)
+ * describes. */
void
-multipath_format(const struct nx_action_multipath *mp, struct ds *s)
+multipath_format(const struct ofpact_multipath *mp, struct ds *s)
{
const char *fields, *algorithm;
- uint16_t mp_fields = ntohs(mp->fields);
- uint16_t mp_algorithm = ntohs(mp->algorithm);
-
- struct mf_subfield dst;
-
- fields = flow_hash_fields_to_str(mp_fields);
+ fields = flow_hash_fields_to_str(mp->fields);
- switch ((enum nx_mp_algorithm) mp_algorithm) {
+ switch (mp->algorithm) {
case NX_MP_ALG_MODULO_N:
algorithm = "modulo_n";
break;
}
ds_put_format(s, "multipath(%s,%"PRIu16",%s,%d,%"PRIu16",",
- fields, ntohs(mp->basis), algorithm, ntohs(mp->max_link) + 1,
- ntohl(mp->arg));
- nxm_decode(&dst, mp->dst, mp->ofs_nbits);
- mf_format_subfield(&dst, s);
+ fields, mp->basis, algorithm, mp->max_link + 1,
+ mp->arg);
+ mf_format_subfield(&mp->dst, s);
ds_put_char(s, ')');
}
/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ds;
struct flow;
struct nx_action_multipath;
-struct nx_action_reg_move;
+struct ofpact_multipath;
+struct ofpbuf;
/* NXAST_MULTIPATH helper functions.
*
* See include/openflow/nicira-ext.h for NXAST_MULTIPATH specification.
*/
-enum ofperr multipath_check(const struct nx_action_multipath *,
+enum ofperr multipath_from_openflow(const struct nx_action_multipath *,
+ struct ofpact_multipath *);
+enum ofperr multipath_check(const struct ofpact_multipath *,
const struct flow *);
-void multipath_execute(const struct nx_action_multipath *, struct flow *);
+void multipath_to_nxast(const struct ofpact_multipath *,
+ struct ofpbuf *openflow);
-void multipath_parse(struct nx_action_multipath *, const char *);
-void multipath_format(const struct nx_action_multipath *, struct ds *);
+void multipath_execute(const struct ofpact_multipath *, struct flow *);
+
+void multipath_parse(struct ofpact_multipath *, const char *);
+void multipath_format(const struct ofpact_multipath *, struct ds *);
#endif /* multipath.h */
--- /dev/null
+/*
+ * Copyright (c) 2011 Gaetano Catalli.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <config.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <ifaddrs.h>
+#include <pcap/pcap.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_tap.h>
+#include <netinet/in.h>
+#include <net/if_mib.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+
+#include "rtbsd.h"
+#include "coverage.h"
+#include "dynamic-string.h"
+#include "fatal-signal.h"
+#include "netdev-provider.h"
+#include "ofpbuf.h"
+#include "openflow/openflow.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "shash.h"
+#include "svec.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(netdev_bsd);
+
+\f
+/*
+ * This file implements objects to access interfaces.
+ * Externally, interfaces are represented by two structures:
+ * + struct netdev_dev, representing a network device,
+ * containing e.g. name and a refcount;
+ * We can have private variables by embedding the
+ * struct netdev_dev into our own structure
+ * (e.g. netdev_dev_bsd)
+ *
+ * + struct netdev, representing an instance of an open netdev_dev.
+ * The structure contains a pointer to the 'struct netdev'
+ * representing the device. Again, private information
+ * such as file descriptor etc. are stored in our
+ * own struct netdev_bsd which includes a struct netdev.
+ *
+ * Both 'struct netdev' and 'struct netdev_dev' are referenced
+ * in containers which hold pointers to the data structures.
+ * We can reach our own struct netdev_XXX_bsd by putting a
+ * struct netdev_XXX within our own struct, and using CONTAINER_OF
+ * to access the parent structure.
+ */
+struct netdev_bsd {
+ struct netdev netdev;
+
+ int netdev_fd; /* Selectable file descriptor for the network device.
+ This descriptor will be used for polling operations */
+
+ pcap_t *pcap_handle; /* Packet capture descriptor for a system network
+ device */
+};
+
+struct netdev_dev_bsd {
+ struct netdev_dev netdev_dev;
+ unsigned int cache_valid;
+ unsigned int change_seq;
+
+ int ifindex;
+ uint8_t etheraddr[ETH_ADDR_LEN];
+ struct in_addr in4;
+ struct in6_addr in6;
+ int mtu;
+ int carrier;
+
+ bool tap_opened;
+ int tap_fd; /* TAP character device, if any */
+};
+
+
+enum {
+ VALID_IFINDEX = 1 << 0,
+ VALID_ETHERADDR = 1 << 1,
+ VALID_IN4 = 1 << 2,
+ VALID_IN6 = 1 << 3,
+ VALID_MTU = 1 << 4,
+ VALID_CARRIER = 1 << 5
+};
+
+/* An AF_INET socket (used for ioctl operations). */
+static int af_inet_sock = -1;
+
+#define PCAP_SNAPLEN 2048
+
+
+/*
+ * Notifier used to invalidate device informations in case of status change.
+ *
+ * It will be registered with a 'rtbsd_notifier_register()' when the first
+ * device will be created with the call of either 'netdev_bsd_tap_create()' or
+ * 'netdev_bsd_system_create()'.
+ *
+ * The callback associated with this notifier ('netdev_bsd_cache_cb()') will
+ * invalidate cached information about the device.
+ */
+static struct rtbsd_notifier netdev_bsd_cache_notifier;
+static int cache_notifier_refcount;
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+
+static int netdev_bsd_do_ioctl(const struct netdev *, struct ifreq *,
+ unsigned long cmd, const char *cmd_name);
+static void destroy_tap(int fd, const char *name);
+static int get_flags(const struct netdev *, int *flagsp);
+static int set_flags(struct netdev *, int flags);
+static int do_set_addr(struct netdev *netdev,
+ int ioctl_nr, const char *ioctl_name,
+ struct in_addr addr);
+static int get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]);
+static int set_etheraddr(const char *netdev_name, int hwaddr_family,
+ int hwaddr_len, const uint8_t[ETH_ADDR_LEN]);
+static int get_ifindex(const struct netdev *, int *ifindexp);
+
+static int netdev_bsd_init(void);
+
+static bool
+is_netdev_bsd_class(const struct netdev_class *netdev_class)
+{
+ return netdev_class->init == netdev_bsd_init;
+}
+
+static struct netdev_bsd *
+netdev_bsd_cast(const struct netdev *netdev)
+{
+ assert(is_netdev_bsd_class(netdev_dev_get_class(netdev_get_dev(netdev))));
+ return CONTAINER_OF(netdev, struct netdev_bsd, netdev);
+}
+
+static struct netdev_dev_bsd *
+netdev_dev_bsd_cast(const struct netdev_dev *netdev_dev)
+{
+ assert(is_netdev_bsd_class(netdev_dev_get_class(netdev_dev)));
+ return CONTAINER_OF(netdev_dev, struct netdev_dev_bsd, netdev_dev);
+}
+
+/* Initialize the AF_INET socket used for ioctl operations */
+static int
+netdev_bsd_init(void)
+{
+ static int status = -1;
+
+ if (status >= 0) { /* already initialized */
+ return status;
+ }
+
+ af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ status = af_inet_sock >= 0 ? 0 : errno;
+
+ if (status) {
+ VLOG_ERR("failed to create inet socket: %s", strerror(status));
+ }
+
+ return status;
+}
+
+/*
+ * Perform periodic work needed by netdev. In BSD netdevs it checks for any
+ * interface status changes, and eventually calls all the user callbacks.
+ */
+static void
+netdev_bsd_run(void)
+{
+ rtbsd_notifier_run();
+}
+
+/*
+ * Arranges for poll_block() to wake up if the "run" member function needs to
+ * be called.
+ */
+static void
+netdev_bsd_wait(void)
+{
+ rtbsd_notifier_wait();
+}
+
+static void
+netdev_dev_bsd_changed(struct netdev_dev_bsd *dev)
+{
+ dev->change_seq++;
+ if (!dev->change_seq) {
+ dev->change_seq++;
+ }
+}
+
+/* Invalidate cache in case of interface status change. */
+static void
+netdev_bsd_cache_cb(const struct rtbsd_change *change,
+ void *aux OVS_UNUSED)
+{
+ struct netdev_dev_bsd *dev;
+
+ if (change) {
+ struct netdev_dev *base_dev = netdev_dev_from_name(change->if_name);
+
+ if (base_dev) {
+ const struct netdev_class *netdev_class =
+ netdev_dev_get_class(base_dev);
+
+ if (is_netdev_bsd_class(netdev_class)) {
+ dev = netdev_dev_bsd_cast(base_dev);
+ dev->cache_valid = 0;
+ netdev_dev_bsd_changed(dev);
+ }
+ }
+ } else {
+ /*
+ * XXX the API is lacking, we should be able to iterate on the list of
+ * netdevs without having to store the info in a temp shash.
+ */
+ struct shash device_shash;
+ struct shash_node *node;
+
+ shash_init(&device_shash);
+ netdev_dev_get_devices(&netdev_bsd_class, &device_shash);
+ SHASH_FOR_EACH (node, &device_shash) {
+ dev = node->data;
+ dev->cache_valid = 0;
+ netdev_dev_bsd_changed(dev);
+ }
+ shash_destroy(&device_shash);
+ }
+}
+
+static int
+cache_notifier_ref(void)
+{
+ int ret = 0;
+
+ if (!cache_notifier_refcount) {
+ ret = rtbsd_notifier_register(&netdev_bsd_cache_notifier,
+ netdev_bsd_cache_cb, NULL);
+ if (ret) {
+ return ret;
+ }
+ }
+ cache_notifier_refcount++;
+ return 0;
+}
+
+static int
+cache_notifier_unref(void)
+{
+ cache_notifier_refcount--;
+ if (cache_notifier_refcount == 0) {
+ rtbsd_notifier_unregister(&netdev_bsd_cache_notifier);
+ }
+ return 0;
+}
+
+/* Allocate a netdev_dev_bsd structure */
+static int
+netdev_bsd_create_system(const struct netdev_class *class, const char *name,
+ struct netdev_dev **netdev_devp)
+{
+ struct netdev_dev_bsd *netdev_dev;
+ int error;
+
+ error = cache_notifier_ref();
+ if (error) {
+ return error;
+ }
+
+ netdev_dev = xzalloc(sizeof *netdev_dev);
+ netdev_dev->change_seq = 1;
+ netdev_dev_init(&netdev_dev->netdev_dev, name, class);
+ *netdev_devp = &netdev_dev->netdev_dev;
+
+ return 0;
+}
+
+/*
+ * Allocate a netdev_dev_bsd structure with 'tap' class.
+ */
+static int
+netdev_bsd_create_tap(const struct netdev_class *class, const char *name,
+ struct netdev_dev **netdev_devp)
+{
+ struct netdev_dev_bsd *netdev_dev = NULL;
+ int error = 0;
+ struct ifreq ifr;
+
+ error = cache_notifier_ref();
+ if (error) {
+ goto error;
+ }
+
+ /* allocate the device structure and set the internal flag */
+ netdev_dev = xzalloc(sizeof *netdev_dev);
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ /* Create a tap device by opening /dev/tap. The TAPGIFNAME ioctl is used
+ * to retrieve the name of the tap device. */
+ netdev_dev->tap_fd = open("/dev/tap", O_RDWR);
+ netdev_dev->change_seq = 1;
+ if (netdev_dev->tap_fd < 0) {
+ error = errno;
+ VLOG_WARN("opening \"/dev/tap\" failed: %s", strerror(error));
+ goto error_undef_notifier;
+ }
+
+ /* Retrieve tap name (e.g. tap0) */
+ if (ioctl(netdev_dev->tap_fd, TAPGIFNAME, &ifr) == -1) {
+ /* XXX Need to destroy the device? */
+ error = errno;
+ goto error_undef_notifier;
+ }
+
+ /* Change the name of the tap device */
+ ifr.ifr_data = (void *)name;
+ if (ioctl(af_inet_sock, SIOCSIFNAME, &ifr) == -1) {
+ error = errno;
+ destroy_tap(netdev_dev->tap_fd, ifr.ifr_name);
+ goto error_undef_notifier;
+ }
+
+ /* set non-blocking. */
+ error = set_nonblocking(netdev_dev->tap_fd);
+ if (error) {
+ destroy_tap(netdev_dev->tap_fd, name);
+ goto error_undef_notifier;
+ }
+
+ /* Turn device UP */
+ ifr.ifr_flags = (uint16_t)IFF_UP;
+ ifr.ifr_flagshigh = 0;
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+ if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) {
+ error = errno;
+ destroy_tap(netdev_dev->tap_fd, name);
+ goto error_undef_notifier;
+ }
+
+ /* initialize the device structure and
+ * link the structure to its netdev */
+ netdev_dev_init(&netdev_dev->netdev_dev, name, class);
+ *netdev_devp = &netdev_dev->netdev_dev;
+
+ return 0;
+
+error_undef_notifier:
+ cache_notifier_unref();
+error:
+ free(netdev_dev);
+ return error;
+}
+
+static void
+netdev_bsd_destroy(struct netdev_dev *netdev_dev_)
+{
+ struct netdev_dev_bsd *netdev_dev = netdev_dev_bsd_cast(netdev_dev_);
+
+ cache_notifier_unref();
+
+ if (netdev_dev->tap_fd >= 0 &&
+ !strcmp(netdev_dev_get_type(netdev_dev_), "tap")) {
+ destroy_tap(netdev_dev->tap_fd, netdev_dev_get_name(netdev_dev_));
+ }
+ free(netdev_dev);
+}
+
+
+static int
+netdev_bsd_open_system(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
+{
+ struct netdev_dev_bsd *netdev_dev = netdev_dev_bsd_cast(netdev_dev_);
+ struct netdev_bsd *netdev;
+ int error;
+ enum netdev_flags flags;
+
+ /* Allocate network device. */
+ netdev = xcalloc(1, sizeof *netdev);
+ netdev->netdev_fd = -1;
+ netdev_init(&netdev->netdev, netdev_dev_);
+
+ /* Verify that the netdev really exists by attempting to read its flags */
+ error = netdev_get_flags(&netdev->netdev, &flags);
+ if (error == ENXIO) {
+ goto error;
+ }
+
+ /* The first user that opens a tap port(from dpif_create_and_open()) will
+ * receive the file descriptor associated with the tap device. Instead, the
+ * following users will open the tap device as a normal 'system' device. */
+ if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap") &&
+ !netdev_dev->tap_opened) {
+ netdev_dev->tap_opened = true;
+ netdev->netdev_fd = netdev_dev->tap_fd;
+ }
+
+ *netdevp = &netdev->netdev;
+ return 0;
+
+error:
+ netdev_uninit(&netdev->netdev, true);
+ return error;
+}
+
+
+
+/* Close a 'netdev'. */
+static void
+netdev_bsd_close(struct netdev *netdev_)
+{
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
+ if (netdev->netdev_fd >= 0 && strcmp(netdev_get_type(netdev_), "tap")) {
+ pcap_close(netdev->pcap_handle);
+ }
+
+ free(netdev);
+}
+
+static int
+netdev_bsd_listen(struct netdev *netdev_)
+{
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+ char errbuf[PCAP_ERRBUF_SIZE];
+ int error;
+ int fd = -1;
+ int one = 1;
+
+ if (netdev->netdev_fd >= 0) {
+ return 0;
+ }
+
+ /* open the pcap device. The device is opened in non-promiscuous mode
+ * because the interface flags are manually set by the caller. */
+ errbuf[0] = '\0';
+ netdev->pcap_handle = pcap_open_live(netdev_get_name(netdev_), PCAP_SNAPLEN,
+ 0, 1000, errbuf);
+ if (netdev->pcap_handle == NULL) {
+ VLOG_ERR("%s: pcap_open_live failed: %s",
+ netdev_get_name(netdev_), errbuf);
+ error = EIO;
+ goto error;
+ } else if (errbuf[0] != '\0') {
+ VLOG_WARN("%s: pcap_open_live: %s",
+ netdev_get_name(netdev_), errbuf);
+ }
+
+ netdev_dev_bsd_changed(netdev_dev_bsd_cast(netdev_get_dev(netdev_)));
+
+ /* initialize netdev->netdev_fd */
+ fd = pcap_get_selectable_fd(netdev->pcap_handle);
+ if (fd == -1) {
+ error = errno;
+ goto error;
+ }
+
+ /* Set non-blocking mode. Also the BIOCIMMEDIATE ioctl must be called
+ * on the file descriptor returned by pcap_get_selectable_fd to achieve
+ * a real non-blocking behaviour.*/
+ error = pcap_setnonblock(netdev->pcap_handle, 1, errbuf);
+ if (error == -1) {
+ error = errno;
+ goto error;
+ }
+
+ /* This call assure that reads return immediately upon packet reception.
+ * Otherwise, a read will block until either the kernel buffer becomes
+ * full or a timeout occurs. */
+ if(ioctl(fd, BIOCIMMEDIATE, &one) < 0 ) {
+ VLOG_ERR("ioctl(BIOCIMMEDIATE) on %s device failed: %s",
+ netdev_get_name(netdev_), strerror(errno));
+ error = errno;
+ goto error;
+ }
+
+ /* Capture only incoming packets */
+ error = pcap_setdirection(netdev->pcap_handle, PCAP_D_IN);
+ if (error == -1) {
+ error = errno;
+ goto error;
+ }
+
+ netdev->netdev_fd = fd;
+ return 0;
+
+error:
+ if (fd >= 0) {
+ close(netdev->netdev_fd);
+ }
+ return error;
+}
+
+
+/* The recv callback of the netdev class returns the number of bytes of the
+ * received packet.
+ *
+ * This can be done by the pcap_next() function. Unfortunately pcap_next() does
+ * not make difference between a missing packet on the capture interface and
+ * an error during the file capture. We can use the pcap_dispatch() function
+ * instead, which is able to distinguish between errors and null packet.
+ *
+ * To make pcap_dispatch() returns the number of bytes read from the interface
+ * we need to define the following callback and argument.
+ */
+struct pcap_arg {
+ void *data;
+ int size;
+ int retval;
+};
+
+/*
+ * This callback will be executed on every captured packet.
+ *
+ * If the packet captured by pcap_dispatch() does not fit the pcap buffer,
+ * pcap returns a truncated packet and we follow this behavior.
+ *
+ * The argument args->retval is the packet size in bytes.
+ */
+static void
+proc_pkt(u_char *args_, const struct pcap_pkthdr *hdr, const u_char *packet)
+{
+ struct pcap_arg *args = (struct pcap_arg *)args_;
+
+ if (args->size < hdr->len) {
+ VLOG_WARN_RL(&rl, "packet truncated");
+ args->retval = args->size;
+ } else {
+ args->retval = hdr->len;
+ }
+
+ /* copy the packet to our buffer */
+ memcpy(args->data, packet, args->retval);
+}
+
+/*
+ * This function attempts to receive a packet from the specified network
+ * device. It is assumed that the network device is a system device or a tap
+ * device opened as a system one. In this case the read operation is performed
+ * on the 'netdev' pcap descriptor.
+ */
+static int
+netdev_bsd_recv_system(struct netdev_bsd *netdev, void *data, size_t size)
+{
+ struct pcap_arg arg;
+ int ret;
+
+ if (netdev->netdev_fd < 0) {
+ return -EAGAIN;
+ }
+
+ /* prepare the pcap argument to store the packet */
+ arg.size = size;
+ arg.data = data;
+
+ for (;;) {
+ ret = pcap_dispatch(netdev->pcap_handle, 1, proc_pkt, (u_char *)&arg);
+
+ if (ret > 0) {
+ return arg.retval; /* arg.retval < 0 is handled in the caller */
+ }
+ if (ret == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ }
+
+ return -EAGAIN;
+ }
+}
+
+/*
+ * This function attempts to receive a packet from the specified network
+ * device. It is assumed that the network device is a tap device and the
+ * 'netdev_fd' member of the 'netdev' structure is initialized with the tap
+ * file descriptor.
+ */
+static int
+netdev_bsd_recv_tap(struct netdev_bsd *netdev, void *data, size_t size)
+{
+ if (netdev->netdev_fd < 0) {
+ return -EAGAIN;
+ }
+
+ for (;;) {
+ ssize_t retval = read(netdev->netdev_fd, data, size);
+ if (retval >= 0) {
+ return retval;
+ } else if (errno != EINTR) {
+ if (errno != EAGAIN) {
+ VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
+ strerror(errno), netdev->netdev.netdev_dev->name);
+ }
+ return -errno;
+ }
+ }
+}
+
+
+/*
+ * According with the nature of the device a different function must be called.
+ * If the device is the bridge local port the 'netdev_bsd_recv_tap' function
+ * must be called, otherwise the 'netdev_bsd_recv_system' function is called.
+ *
+ * type!="tap" ---> system device.
+ * type=="tap" && netdev_fd == tap_fd ---> internal tap device
+ * type=="tap" && netdev_fd != tap_fd ---> internal tap device
+ * opened as a system
+ * device.
+ */
+static int
+netdev_bsd_recv(struct netdev *netdev_, void* data, size_t size)
+{
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+ struct netdev_dev_bsd * netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+ if (!strcmp(netdev_get_type(netdev_), "tap") &&
+ netdev->netdev_fd == netdev_dev->tap_fd) {
+ return netdev_bsd_recv_tap(netdev, data, size);
+ } else {
+ return netdev_bsd_recv_system(netdev, data, size);
+ }
+}
+
+
+/*
+ * Registers with the poll loop to wake up from the next call to poll_block()
+ * when a packet is ready to be received with netdev_recv() on 'netdev'.
+ */
+static void
+netdev_bsd_recv_wait(struct netdev *netdev_)
+{
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
+ if (netdev->netdev_fd >= 0) {
+ poll_fd_wait(netdev->netdev_fd, POLLIN);
+ }
+}
+
+/* Discards all packets waiting to be received from 'netdev'. */
+static int
+netdev_bsd_drain(struct netdev *netdev_)
+{
+ struct ifreq ifr;
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
+ strcpy(ifr.ifr_name, netdev_get_name(netdev_));
+ if (ioctl(netdev->netdev_fd, BIOCFLUSH, &ifr) == -1) {
+ VLOG_DBG_RL(&rl, "%s: ioctl(BIOCFLUSH) failed: %s",
+ netdev_get_name(netdev_), strerror(errno));
+ return errno;
+ }
+ return 0;
+}
+
+/*
+ * Send a packet on the specified network device. The device could be either a
+ * system or a tap device.
+ */
+static int
+netdev_bsd_send(struct netdev *netdev_, const void *data, size_t size)
+{
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+ struct netdev_dev_bsd * netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+ if (netdev->netdev_fd < 0) {
+ return EPIPE;
+ }
+
+ for (;;) {
+ ssize_t retval;
+ if (!strcmp(netdev_get_type(netdev_), "tap") &&
+ netdev_dev->tap_fd == netdev->netdev_fd) {
+ retval = write(netdev->netdev_fd, data, size);
+ } else {
+ retval = pcap_inject(netdev->pcap_handle, data, size);
+ }
+ if (retval < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno != EAGAIN) {
+ VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
+ netdev_get_name(netdev_), strerror(errno));
+ }
+ return errno;
+ } else if (retval != size) {
+ VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%zd bytes of "
+ "%zu) on %s", retval, size,
+ netdev_get_name(netdev_));
+ return EMSGSIZE;
+ } else {
+ return 0;
+ }
+ }
+}
+
+/*
+ * Registers with the poll loop to wake up from the next call to poll_block()
+ * when the packet transmission queue has sufficient room to transmit a packet
+ * with netdev_send().
+ */
+static void
+netdev_bsd_send_wait(struct netdev *netdev_)
+{
+ struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
+
+ if (netdev->netdev_fd < 0) { /* Nothing to do. */
+ return;
+ }
+
+ if (strcmp(netdev_get_type(netdev_), "tap")) {
+ poll_fd_wait(netdev->netdev_fd, POLLOUT);
+ } else {
+ /* TAP device always accepts packets. */
+ poll_immediate_wake();
+ }
+}
+
+/*
+ * Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful,
+ * otherwise a positive errno value.
+ */
+static int
+netdev_bsd_set_etheraddr(struct netdev *netdev_,
+ const uint8_t mac[ETH_ADDR_LEN])
+{
+ struct netdev_dev_bsd *netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+ int error;
+
+ if (!(netdev_dev->cache_valid & VALID_ETHERADDR)
+ || !eth_addr_equals(netdev_dev->etheraddr, mac)) {
+ error = set_etheraddr(netdev_get_name(netdev_), AF_LINK, ETH_ADDR_LEN,
+ mac);
+ if (!error) {
+ netdev_dev->cache_valid |= VALID_ETHERADDR;
+ memcpy(netdev_dev->etheraddr, mac, ETH_ADDR_LEN);
+ netdev_dev_bsd_changed(netdev_dev);
+ }
+ } else {
+ error = 0;
+ }
+ return error;
+}
+
+/*
+ * Returns a pointer to 'netdev''s MAC address. The caller must not modify or
+ * free the returned buffer.
+ */
+static int
+netdev_bsd_get_etheraddr(const struct netdev *netdev_,
+ uint8_t mac[ETH_ADDR_LEN])
+{
+ struct netdev_dev_bsd *netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+ if (!(netdev_dev->cache_valid & VALID_ETHERADDR)) {
+ int error = get_etheraddr(netdev_get_name(netdev_),
+ netdev_dev->etheraddr);
+ if (error) {
+ return error;
+ }
+ netdev_dev->cache_valid |= VALID_ETHERADDR;
+ }
+ memcpy(mac, netdev_dev->etheraddr, ETH_ADDR_LEN);
+
+ return 0;
+}
+
+/*
+ * Returns the maximum size of transmitted (and received) packets on 'netdev',
+ * in bytes, not including the hardware header; thus, this is typically 1500
+ * bytes for Ethernet devices.
+ */
+static int
+netdev_bsd_get_mtu(const struct netdev *netdev_, int *mtup)
+{
+ struct netdev_dev_bsd *netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+ if (!(netdev_dev->cache_valid & VALID_MTU)) {
+ struct ifreq ifr;
+ int error;
+
+ error = netdev_bsd_do_ioctl(netdev_, &ifr, SIOCGIFMTU, "SIOCGIFMTU");
+ if (error) {
+ return error;
+ }
+ netdev_dev->mtu = ifr.ifr_mtu;
+ netdev_dev->cache_valid |= VALID_MTU;
+ }
+
+ *mtup = netdev_dev->mtu;
+ return 0;
+}
+
+static int
+netdev_bsd_get_ifindex(const struct netdev *netdev)
+{
+ int ifindex, error;
+
+ error = get_ifindex(netdev, &ifindex);
+ return error ? -error : ifindex;
+}
+
+static int
+netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier)
+{
+ struct netdev_dev_bsd *netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+ if (!(netdev_dev->cache_valid & VALID_CARRIER)) {
+ struct ifmediareq ifmr;
+
+ memset(&ifmr, 0, sizeof(ifmr));
+ strncpy(ifmr.ifm_name, netdev_get_name(netdev_), sizeof ifmr.ifm_name);
+
+ if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+ VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
+ netdev_get_name(netdev_), strerror(errno));
+ return errno;
+ }
+
+ netdev_dev->carrier = (ifmr.ifm_status & IFM_ACTIVE) == IFM_ACTIVE;
+ netdev_dev->cache_valid |= VALID_CARRIER;
+
+ /* If the interface doesn't report whether the media is active,
+ * just assume it is active. */
+ if ((ifmr.ifm_status & IFM_AVALID) == 0) {
+ netdev_dev->carrier = true;
+ }
+ }
+ *carrier = netdev_dev->carrier;
+
+ return 0;
+}
+
+/* Retrieves current device stats for 'netdev'. */
+static int
+netdev_bsd_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
+{
+ int if_count, i;
+ int mib[6];
+ size_t len;
+ struct ifmibdata ifmd;
+
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_LINK;
+ mib[2] = NETLINK_GENERIC;
+ mib[3] = IFMIB_SYSTEM;
+ mib[4] = IFMIB_IFCOUNT;
+
+ len = sizeof(if_count);
+
+ if (sysctl(mib, 5, &if_count, &len, (void *)0, 0) == -1) {
+ VLOG_DBG_RL(&rl, "%s: sysctl failed: %s",
+ netdev_get_name(netdev_), strerror(errno));
+ return errno;
+ }
+
+ mib[5] = IFDATA_GENERAL;
+ mib[3] = IFMIB_IFDATA;
+ len = sizeof(ifmd);
+ for (i = 1; i <= if_count; i++) {
+ mib[4] = i; //row
+ if (sysctl(mib, 6, &ifmd, &len, (void *)0, 0) == -1) {
+ VLOG_DBG_RL(&rl, "%s: sysctl failed: %s",
+ netdev_get_name(netdev_), strerror(errno));
+ return errno;
+ } else if (!strcmp(ifmd.ifmd_name, netdev_get_name(netdev_))) {
+ stats->rx_packets = ifmd.ifmd_data.ifi_ipackets;
+ stats->tx_packets = ifmd.ifmd_data.ifi_opackets;
+ stats->rx_bytes = ifmd.ifmd_data.ifi_ibytes;
+ stats->tx_bytes = ifmd.ifmd_data.ifi_obytes;
+ stats->rx_errors = ifmd.ifmd_data.ifi_ierrors;
+ stats->tx_errors = ifmd.ifmd_data.ifi_oerrors;
+ stats->rx_dropped = ifmd.ifmd_data.ifi_iqdrops;
+ stats->tx_dropped = 0;
+ stats->multicast = ifmd.ifmd_data.ifi_imcasts;
+ stats->collisions = ifmd.ifmd_data.ifi_collisions;
+
+ stats->rx_length_errors = 0;
+ stats->rx_over_errors = 0;
+ stats->rx_crc_errors = 0;
+ stats->rx_frame_errors = 0;
+ stats->rx_fifo_errors = 0;
+ stats->rx_missed_errors = 0;
+
+ stats->tx_aborted_errors = 0;
+ stats->tx_carrier_errors = 0;
+ stats->tx_fifo_errors = 0;
+ stats->tx_heartbeat_errors = 0;
+ stats->tx_window_errors = 0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static uint32_t
+netdev_bsd_parse_media(int media)
+{
+ uint32_t supported = 0;
+ bool half_duplex = media & IFM_HDX ? true : false;
+
+ switch (IFM_SUBTYPE(media)) {
+ case IFM_10_2:
+ case IFM_10_5:
+ case IFM_10_STP:
+ case IFM_10_T:
+ supported |= half_duplex ? NETDEV_F_10MB_HD : NETDEV_F_10MB_FD;
+ supported |= NETDEV_F_COPPER;
+ break;
+
+ case IFM_10_FL:
+ supported |= half_duplex ? NETDEV_F_10MB_HD : NETDEV_F_10MB_FD;
+ supported |= NETDEV_F_FIBER;
+ break;
+
+ case IFM_100_T2:
+ case IFM_100_T4:
+ case IFM_100_TX:
+ case IFM_100_VG:
+ supported |= half_duplex ? NETDEV_F_100MB_HD : NETDEV_F_100MB_FD;
+ supported |= NETDEV_F_COPPER;
+ break;
+
+ case IFM_100_FX:
+ supported |= half_duplex ? NETDEV_F_100MB_HD : NETDEV_F_100MB_FD;
+ supported |= NETDEV_F_FIBER;
+ break;
+
+ case IFM_1000_CX:
+ case IFM_1000_T:
+ supported |= half_duplex ? NETDEV_F_1GB_HD : NETDEV_F_1GB_FD;
+ supported |= NETDEV_F_COPPER;
+ break;
+
+ case IFM_1000_LX:
+ case IFM_1000_SX:
+ supported |= half_duplex ? NETDEV_F_1GB_HD : NETDEV_F_1GB_FD;
+ supported |= NETDEV_F_FIBER;
+ break;
+
+ case IFM_10G_CX4:
+ supported |= NETDEV_F_10GB_FD;
+ supported |= NETDEV_F_COPPER;
+ break;
+
+ case IFM_10G_LR:
+ case IFM_10G_SR:
+ supported |= NETDEV_F_10GB_FD;
+ supported |= NETDEV_F_FIBER;
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (IFM_SUBTYPE(media) == IFM_AUTO) {
+ supported |= NETDEV_F_AUTONEG;
+ }
+ /*
+ if (media & IFM_ETH_FMASK) {
+ supported |= NETDEV_F_PAUSE;
+ }
+ */
+
+ return supported;
+}
+
+/*
+ * Stores the features supported by 'netdev' into each of '*current',
+ * '*advertised', '*supported', and '*peer' that are non-null. Each value is a
+ * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if
+ * successful, otherwise a positive errno value. On failure, all of the
+ * passed-in values are set to 0.
+ */
+static int
+netdev_bsd_get_features(const struct netdev *netdev,
+ enum netdev_features *current, uint32_t *advertised,
+ enum netdev_features *supported, uint32_t *peer)
+{
+ struct ifmediareq ifmr;
+ int *media_list;
+ int i;
+ int error;
+
+
+ /* XXX Look into SIOCGIFCAP instead of SIOCGIFMEDIA */
+
+ memset(&ifmr, 0, sizeof(ifmr));
+ strncpy(ifmr.ifm_name, netdev_get_name(netdev), sizeof ifmr.ifm_name);
+
+ /* We make two SIOCGIFMEDIA ioctl calls. The first to determine the
+ * number of supported modes, and a second with a buffer to retrieve
+ * them. */
+ if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+ VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
+ netdev_get_name(netdev), strerror(errno));
+ return errno;
+ }
+
+ media_list = xcalloc(ifmr.ifm_count, sizeof(int));
+ ifmr.ifm_ulist = media_list;
+
+ if (!IFM_TYPE(ifmr.ifm_current) & IFM_ETHER) {
+ VLOG_DBG_RL(&rl, "%s: doesn't appear to be ethernet",
+ netdev_get_name(netdev));
+ error = EINVAL;
+ goto cleanup;
+ }
+
+ if (ioctl(af_inet_sock, SIOCGIFMEDIA, &ifmr) == -1) {
+ VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s",
+ netdev_get_name(netdev), strerror(errno));
+ error = errno;
+ goto cleanup;
+ }
+
+ /* Current settings. */
+ *current = netdev_bsd_parse_media(ifmr.ifm_active);
+
+ /* Advertised features. */
+ *advertised = netdev_bsd_parse_media(ifmr.ifm_current);
+
+ /* Supported features. */
+ *supported = 0;
+ for (i = 0; i < ifmr.ifm_count; i++) {
+ *supported |= netdev_bsd_parse_media(ifmr.ifm_ulist[i]);
+ }
+
+ /* Peer advertisements. */
+ *peer = 0; /* XXX */
+
+ error = 0;
+cleanup:
+ free(media_list);
+ return error;
+}
+
+/*
+ * If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if
+ * 'in4' is non-null) and returns true. Otherwise, returns false.
+ */
+static int
+netdev_bsd_get_in4(const struct netdev *netdev_, struct in_addr *in4,
+ struct in_addr *netmask)
+{
+ struct netdev_dev_bsd *netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+
+ if (!(netdev_dev->cache_valid & VALID_IN4)) {
+ const struct sockaddr_in *sin;
+ struct ifreq ifr;
+ int error;
+
+ ifr.ifr_addr.sa_family = AF_INET;
+ error = netdev_bsd_do_ioctl(netdev_, &ifr,
+ SIOCGIFADDR, "SIOCGIFADDR");
+ if (error) {
+ return error;
+ }
+
+ sin = (struct sockaddr_in *) &ifr.ifr_addr;
+ netdev_dev->in4 = sin->sin_addr;
+ netdev_dev->cache_valid |= VALID_IN4;
+ error = netdev_bsd_do_ioctl(netdev_, &ifr,
+ SIOCGIFNETMASK, "SIOCGIFNETMASK");
+ if (error) {
+ return error;
+ }
+ *netmask = ((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr;
+ }
+ *in4 = netdev_dev->in4;
+
+ return in4->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0;
+}
+
+/*
+ * Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If
+ * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a
+ * positive errno value.
+ */
+static int
+netdev_bsd_set_in4(struct netdev *netdev_, struct in_addr addr,
+ struct in_addr mask)
+{
+ struct netdev_dev_bsd *netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+ int error;
+
+ error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", addr);
+ if (!error) {
+ netdev_dev->cache_valid |= VALID_IN4;
+ netdev_dev->in4 = addr;
+ if (addr.s_addr != INADDR_ANY) {
+ error = do_set_addr(netdev_, SIOCSIFNETMASK,
+ "SIOCSIFNETMASK", mask);
+ }
+ netdev_dev_bsd_changed(netdev_dev);
+ }
+ return error;
+}
+
+static int
+netdev_bsd_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
+{
+ struct netdev_dev_bsd *netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+ if (!(netdev_dev->cache_valid & VALID_IN6)) {
+ struct ifaddrs *ifa, *head;
+ struct sockaddr_in6 *sin6;
+ const char *netdev_name = netdev_get_name(netdev_);
+
+ if (getifaddrs(&head) != 0) {
+ VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name,
+ strerror(errno));
+ return errno;
+ }
+
+ for (ifa = head; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family == AF_INET6 &&
+ !strcmp(ifa->ifa_name, netdev_name)) {
+ sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (sin6) {
+ memcpy(&netdev_dev->in6, &sin6->sin6_addr, sin6->sin6_len);
+ netdev_dev->cache_valid |= VALID_IN6;
+ *in6 = netdev_dev->in6;
+ freeifaddrs(head);
+ return 0;
+ }
+ }
+ }
+ return EADDRNOTAVAIL;
+ }
+ *in6 = netdev_dev->in6;
+ return 0;
+}
+
+static void
+make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr)
+{
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr = addr;
+ sin.sin_port = 0;
+
+ memset(sa, 0, sizeof *sa);
+ memcpy(sa, &sin, sizeof sin);
+}
+
+static int
+do_set_addr(struct netdev *netdev,
+ int ioctl_nr, const char *ioctl_name, struct in_addr addr)
+{
+ struct ifreq ifr;
+ make_in4_sockaddr(&ifr.ifr_addr, addr);
+ return netdev_bsd_do_ioctl(netdev, &ifr, ioctl_nr, ioctl_name);
+}
+
+static int
+nd_to_iff_flags(enum netdev_flags nd)
+{
+ int iff = 0;
+ if (nd & NETDEV_UP) {
+ iff |= IFF_UP;
+ }
+ if (nd & NETDEV_PROMISC) {
+ iff |= IFF_PROMISC;
+ iff |= IFF_PPROMISC;
+ }
+ return iff;
+}
+
+static int
+iff_to_nd_flags(int iff)
+{
+ enum netdev_flags nd = 0;
+ if (iff & IFF_UP) {
+ nd |= NETDEV_UP;
+ }
+ if (iff & IFF_PROMISC) {
+ nd |= NETDEV_PROMISC;
+ }
+ return nd;
+}
+
+static int
+netdev_bsd_update_flags(struct netdev *netdev, enum netdev_flags off,
+ enum netdev_flags on, enum netdev_flags *old_flagsp)
+{
+ int old_flags, new_flags;
+ int error;
+
+ error = get_flags(netdev, &old_flags);
+ if (!error) {
+ *old_flagsp = iff_to_nd_flags(old_flags);
+ new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
+ if (new_flags != old_flags) {
+ error = set_flags(netdev, new_flags);
+ netdev_dev_bsd_changed(netdev_dev_bsd_cast(netdev_get_dev(netdev)));
+ }
+ }
+ return error;
+}
+
+static unsigned int
+netdev_bsd_change_seq(const struct netdev *netdev)
+{
+ return netdev_dev_bsd_cast(netdev_get_dev(netdev))->change_seq;
+}
+
+
+const struct netdev_class netdev_bsd_class = {
+ "system",
+
+ netdev_bsd_init,
+ netdev_bsd_run,
+ netdev_bsd_wait,
+ netdev_bsd_create_system,
+ netdev_bsd_destroy,
+ NULL, /* get_config */
+ NULL, /* set_config */
+ netdev_bsd_open_system,
+ netdev_bsd_close,
+
+ netdev_bsd_listen,
+
+ netdev_bsd_recv,
+ netdev_bsd_recv_wait,
+ netdev_bsd_drain,
+
+ netdev_bsd_send,
+ netdev_bsd_send_wait,
+
+ netdev_bsd_set_etheraddr,
+ netdev_bsd_get_etheraddr,
+ netdev_bsd_get_mtu,
+ NULL, /* set_mtu */
+ netdev_bsd_get_ifindex,
+ netdev_bsd_get_carrier,
+ NULL, /* get_carrier_resets */
+ NULL, /* set_miimon_interval */
+ netdev_bsd_get_stats,
+ NULL, /* set_stats */
+
+ netdev_bsd_get_features,
+ NULL, /* set_advertisement */
+ NULL, /* set_policing */
+ NULL, /* get_qos_type */
+ NULL, /* get_qos_capabilities */
+ NULL, /* get_qos */
+ NULL, /* set_qos */
+ NULL, /* get_queue */
+ NULL, /* set_queue */
+ NULL, /* delete_queue */
+ NULL, /* get_queue_stats */
+ NULL, /* dump_queue */
+ NULL, /* dump_queue_stats */
+
+ netdev_bsd_get_in4,
+ netdev_bsd_set_in4,
+ netdev_bsd_get_in6,
+ NULL, /* add_router */
+ NULL, /* get_next_hop */
+ NULL, /* get_drv_info */
+ NULL, /* arp_lookup */
+
+ netdev_bsd_update_flags,
+
+ netdev_bsd_change_seq
+};
+
+const struct netdev_class netdev_tap_class = {
+ "tap",
+
+ netdev_bsd_init,
+ netdev_bsd_run,
+ netdev_bsd_wait,
+ netdev_bsd_create_tap,
+ netdev_bsd_destroy,
+ NULL, /* get_config */
+ NULL, /* set_config */
+ netdev_bsd_open_system,
+ netdev_bsd_close,
+
+ netdev_bsd_listen,
+
+ netdev_bsd_recv,
+ netdev_bsd_recv_wait,
+ netdev_bsd_drain,
+
+ netdev_bsd_send,
+ netdev_bsd_send_wait,
+
+ netdev_bsd_set_etheraddr,
+ netdev_bsd_get_etheraddr,
+ netdev_bsd_get_mtu,
+ NULL, /* set_mtu */
+ netdev_bsd_get_ifindex,
+ netdev_bsd_get_carrier,
+ NULL, /* get_carrier_resets */
+ NULL, /* set_miimon_interval */
+ netdev_bsd_get_stats,
+ NULL, /* set_stats */
+
+ netdev_bsd_get_features,
+ NULL, /* set_advertisement */
+ NULL, /* set_policing */
+ NULL, /* get_qos_type */
+ NULL, /* get_qos_capabilities */
+ NULL, /* get_qos */
+ NULL, /* set_qos */
+ NULL, /* get_queue */
+ NULL, /* set_queue */
+ NULL, /* delete_queue */
+ NULL, /* get_queue_stats */
+ NULL, /* dump_queue */
+ NULL, /* dump_queue_stats */
+
+ netdev_bsd_get_in4,
+ netdev_bsd_set_in4,
+ netdev_bsd_get_in6,
+ NULL, /* add_router */
+ NULL, /* get_next_hop */
+ NULL, /* get_drv_info */
+ NULL, /* arp_lookup */
+
+ netdev_bsd_update_flags,
+
+ netdev_bsd_change_seq
+};
+\f
+
+static void
+destroy_tap(int fd, const char *name)
+{
+ struct ifreq ifr;
+
+ close(fd);
+ strcpy(ifr.ifr_name, name);
+ /* XXX What to do if this call fails? */
+ ioctl(af_inet_sock, SIOCIFDESTROY, &ifr);
+}
+
+static int
+get_flags(const struct netdev *netdev, int *flags)
+{
+ struct ifreq ifr;
+ int error;
+
+ error = netdev_bsd_do_ioctl(netdev, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS");
+
+ *flags = 0xFFFF0000 & (ifr.ifr_flagshigh << 16);
+ *flags |= 0x0000FFFF & ifr.ifr_flags;
+
+ return error;
+}
+
+static int
+set_flags(struct netdev *netdev, int flags)
+{
+ struct ifreq ifr;
+
+ ifr.ifr_flags = 0x0000FFFF & flags;
+ ifr.ifr_flagshigh = (0xFFFF0000 & flags) >> 16;
+
+ return netdev_bsd_do_ioctl(netdev, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS");
+}
+
+static int
+get_ifindex(const struct netdev *netdev_, int *ifindexp)
+{
+ struct netdev_dev_bsd *netdev_dev =
+ netdev_dev_bsd_cast(netdev_get_dev(netdev_));
+ *ifindexp = 0;
+ if (!(netdev_dev->cache_valid & VALID_IFINDEX)) {
+ int ifindex = if_nametoindex(netdev_get_name(netdev_));
+ if (ifindex <= 0) {
+ return errno;
+ }
+ netdev_dev->cache_valid |= VALID_IFINDEX;
+ netdev_dev->ifindex = ifindex;
+ }
+ *ifindexp = netdev_dev->ifindex;
+ return 0;
+}
+
+static int
+get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
+{
+ struct ifaddrs *head;
+ struct ifaddrs *ifa;
+ struct sockaddr_dl *sdl;
+
+ if (getifaddrs(&head) != 0) {
+ VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name,
+ strerror(errno));
+ return errno;
+ }
+
+ for (ifa = head; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family == AF_LINK) {
+ if (!strcmp(ifa->ifa_name, netdev_name)) {
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ if (sdl) {
+ memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+ freeifaddrs(head);
+ return 0;
+ }
+ }
+ }
+ }
+
+ VLOG_ERR("could not find ethernet address for %s device", netdev_name);
+ freeifaddrs(head);
+ return ENODEV;
+}
+
+static int
+set_etheraddr(const char *netdev_name, int hwaddr_family,
+ int hwaddr_len, const uint8_t mac[ETH_ADDR_LEN])
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof ifr);
+ strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
+ ifr.ifr_addr.sa_family = hwaddr_family;
+ ifr.ifr_addr.sa_len = hwaddr_len;
+ memcpy(ifr.ifr_addr.sa_data, mac, hwaddr_len);
+ if (ioctl(af_inet_sock, SIOCSIFLLADDR, &ifr) < 0) {
+ VLOG_ERR("ioctl(SIOCSIFLLADDR) on %s device failed: %s",
+ netdev_name, strerror(errno));
+ return errno;
+ }
+ return 0;
+}
+
+static int
+netdev_bsd_do_ioctl(const struct netdev *netdev, struct ifreq *ifr,
+ unsigned long cmd, const char *cmd_name)
+{
+ strncpy(ifr->ifr_name, netdev_get_name(netdev), sizeof ifr->ifr_name);
+ if (ioctl(af_inet_sock, cmd, ifr) == -1) {
+ VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s",
+ netdev_get_name(netdev), cmd_name, strerror(errno));
+ return errno;
+ }
+ return 0;
+}
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
static struct shash dummy_netdev_devs = SHASH_INITIALIZER(&dummy_netdev_devs);
+static unixctl_cb_func netdev_dummy_set_admin_state;
static int netdev_dummy_create(const struct netdev_class *, const char *,
struct netdev_dev **);
-static void netdev_dummy_poll_notify(const struct netdev *);
+static void netdev_dev_dummy_poll_notify(struct netdev_dev_dummy *);
+static int netdev_dev_dummy_update_flags(struct netdev_dev_dummy *,
+ enum netdev_flags off,
+ enum netdev_flags on,
+ enum netdev_flags *old_flagsp);
static bool
is_dummy_class(const struct netdev_class *class)
if (!eth_addr_equals(dev->hwaddr, mac)) {
memcpy(dev->hwaddr, mac, ETH_ADDR_LEN);
- netdev_dummy_poll_notify(netdev);
+ netdev_dev_dummy_poll_notify(dev);
}
return 0;
struct netdev_dev_dummy *dev =
netdev_dev_dummy_cast(netdev_get_dev(netdev));
+ return netdev_dev_dummy_update_flags(dev, off, on, old_flagsp);
+}
+
+static int
+netdev_dev_dummy_update_flags(struct netdev_dev_dummy *dev,
+ enum netdev_flags off, enum netdev_flags on,
+ enum netdev_flags *old_flagsp)
+{
if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) {
return EINVAL;
}
dev->flags |= on;
dev->flags &= ~off;
if (*old_flagsp != dev->flags) {
- netdev_dummy_poll_notify(netdev);
+ netdev_dev_dummy_poll_notify(dev);
}
return 0;
}
/* Helper functions. */
static void
-netdev_dummy_poll_notify(const struct netdev *netdev)
+netdev_dev_dummy_poll_notify(struct netdev_dev_dummy *dev)
{
- struct netdev_dev_dummy *dev =
- netdev_dev_dummy_cast(netdev_get_dev(netdev));
-
dev->change_seq++;
if (!dev->change_seq) {
dev->change_seq++;
}
}
+static void
+netdev_dev_dummy_set_admin_state(struct netdev_dev_dummy *dev,
+ bool admin_state)
+{
+ enum netdev_flags old_flags;
+
+ if (admin_state) {
+ netdev_dev_dummy_update_flags(dev, 0, NETDEV_UP, &old_flags);
+ } else {
+ netdev_dev_dummy_update_flags(dev, NETDEV_UP, 0, &old_flags);
+ }
+}
+
+static void
+netdev_dummy_set_admin_state(struct unixctl_conn *conn, int argc,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ bool up;
+
+ if (!strcasecmp(argv[argc - 1], "up")) {
+ up = true;
+ } else if ( !strcasecmp(argv[argc - 1], "down")) {
+ up = false;
+ } else {
+ unixctl_command_reply_error(conn, "Invalid Admin State");
+ return;
+ }
+
+ if (argc > 2) {
+ struct netdev_dev_dummy *dummy_dev;
+
+ dummy_dev = shash_find_data(&dummy_netdev_devs, argv[1]);
+ if (dummy_dev) {
+ netdev_dev_dummy_set_admin_state(dummy_dev, up);
+ } else {
+ unixctl_command_reply_error(conn, "Unknown Dummy Interface");
+ return;
+ }
+ } else {
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &dummy_netdev_devs) {
+ netdev_dev_dummy_set_admin_state(node->data, up);
+ }
+ }
+ unixctl_command_reply(conn, "OK");
+}
+
void
netdev_dummy_register(bool override)
{
unixctl_command_register("netdev-dummy/receive", "NAME PACKET|FLOW...",
2, INT_MAX, netdev_dummy_receive, NULL);
+ unixctl_command_register("netdev-dummy/set-admin-state",
+ "[netdev] up|down", 1, 2,
+ netdev_dummy_set_admin_state, NULL);
if (override) {
struct sset types;
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
COVERAGE_DEFINE(netdev_get_ifindex);
COVERAGE_DEFINE(netdev_get_hwaddr);
COVERAGE_DEFINE(netdev_set_hwaddr);
-COVERAGE_DEFINE(netdev_ethtool);
+COVERAGE_DEFINE(netdev_get_ethtool);
+COVERAGE_DEFINE(netdev_set_ethtool);
\f
/* These were introduced in Linux 2.6.14, so they might be missing if we have
*
* (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
*
* 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.
*
* 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'.
* 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
* 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'.
return 0;
}
+ COVERAGE_INC(netdev_get_ethtool);
memset(&netdev_dev->drvinfo, 0, sizeof netdev_dev->drvinfo);
error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name,
(struct ethtool_cmd *)&netdev_dev->drvinfo,
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
- iov.iov_base = (void *) data;
+ iov.iov_base = CONST_CAST(void *, data);
iov.iov_len = size;
msg.msg_name = &sll;
VLOG_DBG_RL(&rl, "%s: failed to query MII, falling back to ethtool",
name);
+ COVERAGE_INC(netdev_get_ethtool);
memset(&ecmd, 0, sizeof ecmd);
error = netdev_linux_do_ethtool(name, &ecmd, ETHTOOL_GLINK,
"ETHTOOL_GLINK");
return;
}
+ COVERAGE_INC(netdev_get_ethtool);
memset(&ecmd, 0, sizeof ecmd);
error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name, &ecmd,
ETHTOOL_GSET, "ETHTOOL_GSET");
struct ethtool_cmd ecmd;
int error;
+ COVERAGE_INC(netdev_get_ethtool);
memset(&ecmd, 0, sizeof ecmd);
error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
ETHTOOL_GSET, "ETHTOOL_GSET");
if (advertise & NETDEV_F_PAUSE_ASYM) {
ecmd.advertising |= ADVERTISED_Asym_Pause;
}
+ COVERAGE_INC(netdev_set_ethtool);
return netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
ETHTOOL_SSET, "ETHTOOL_SSET");
}
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));
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));
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));
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));
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;
}
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) {
last_error = error;
}
}
- shash_destroy(&details);
+ smap_destroy(&details);
return last_error;
}
}
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 =
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;
}
}
static int
-netdev_tap_pl_update_flags(struct netdev *netdev, enum netdev_flags off,
- enum netdev_flags on, enum netdev_flags *old_flagsp)
+netdev_tap_pl_update_flags(struct netdev *netdev OVS_UNUSED, enum netdev_flags off OVS_UNUSED,
+ enum netdev_flags on OVS_UNUSED, enum netdev_flags *old_flagsp OVS_UNUSED)
{
return 0;
}
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;
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);
}
static int
-htb_tc_install(struct netdev *netdev, const struct shash *details)
+htb_tc_install(struct netdev *netdev, const struct smap *details)
{
int error;
}
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;
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;
}
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) {
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;
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);
}
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;
}
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;
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;
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;
}
/* Instantiate it. */
- load_error = ops->tc_load((struct netdev *) netdev, qdisc);
+ load_error = ops->tc_load(CONST_CAST(struct netdev *, netdev), qdisc);
assert((load_error == 0) == (netdev_dev->tc != NULL));
ofpbuf_delete(qdisc);
uint32_t new_flags;
int error;
+ COVERAGE_INC(netdev_get_ethtool);
memset(&evalue, 0, sizeof evalue);
error = netdev_linux_do_ethtool(netdev_name,
(struct ethtool_cmd *)&evalue,
return error;
}
+ COVERAGE_INC(netdev_set_ethtool);
evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0);
error = netdev_linux_do_ethtool(netdev_name,
(struct ethtool_cmd *)&evalue,
return error;
}
+ COVERAGE_INC(netdev_get_ethtool);
memset(&evalue, 0, sizeof evalue);
error = netdev_linux_do_ethtool(netdev_name,
(struct ethtool_cmd *)&evalue,
ifr.ifr_data = (caddr_t) ecmd;
ecmd->cmd = cmd;
- COVERAGE_INC(netdev_ethtool);
if (ioctl(af_inet_sock, SIOCETHTOOL, &ifr) == 0) {
return 0;
} else {
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "netdev.h"
#include "list.h"
#include "shash.h"
+#include "smap.h"
#ifdef __cplusplus
extern "C" {
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. */
/* Sets 'netdev''s Ethernet address to 'mac' */
int (*set_etheraddr)(struct netdev *netdev, const uint8_t mac[6]);
- /* Retrieves 'netdev''s Ethernet address into 'mac'. */
+ /* Retrieves 'netdev''s Ethernet address into 'mac'.
+ *
+ * This address will be advertised as 'netdev''s MAC address through the
+ * OpenFlow protocol, among other uses. */
int (*get_etheraddr)(const struct netdev *netdev, uint8_t mac[6]);
/* Retrieves 'netdev''s MTU into '*mtup'.
*
* 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'.
*
* 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
* 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
*
* 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'.
*
*/
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);
* 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
extern const struct netdev_class netdev_linux_class;
extern const struct netdev_class netdev_internal_class;
extern const struct netdev_class netdev_tap_class;
+#ifdef __FreeBSD__
+extern const struct netdev_class netdev_bsd_class;
+#endif
extern const struct netdev_class netdev_tap_pl_class;
extern const struct netdev_class netdev_tunnel_class;
}
static int
-netdev_tunnel_get_config(struct netdev_dev *dev_, struct shash *args)
+netdev_tunnel_get_config(struct netdev_dev *dev_, struct smap *args)
{
struct netdev_dev_tunnel *netdev_dev = netdev_dev_tunnel_cast(dev_);
if (netdev_dev->valid_remote_ip)
- shash_add(args, "remote_ip",
+ smap_add(args, "remote_ip",
xasprintf(IP_FMT, IP_ARGS(&netdev_dev->remote_addr.sin_addr)));
if (netdev_dev->valid_remote_port)
- shash_add(args, "remote_port",
+ smap_add(args, "remote_port",
xasprintf("%"PRIu16, ntohs(netdev_dev->remote_addr.sin_port)));
return 0;
}
}
static int
-netdev_tunnel_set_config(struct netdev_dev *dev_, const struct shash *args)
+netdev_tunnel_set_config(struct netdev_dev *dev_, const struct smap *args)
{
struct netdev_dev_tunnel *netdev_dev = netdev_dev_tunnel_cast(dev_);
struct shash_node *node;
VLOG_DBG("tunnel_set_config(%s)", netdev_dev_get_name(dev_));
- SHASH_FOR_EACH(node, args) {
+ SMAP_FOR_EACH(node, args) {
VLOG_DBG("arg: %s->%s", node->name, (char*)node->data);
if (!strcmp(node->name, "remote_ip")) {
struct in_addr addr;
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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);
}
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);
}
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);
}
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);
}
}
/* 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";
}
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);
flags &= ~TNL_F_HDR_CACHE;
}
- SHASH_FOR_EACH (node, args) {
- if (!strcmp(node->name, "remote_ip")) {
+ SMAP_FOR_EACH (node, args) {
+ if (!strcmp(node->key, "remote_ip")) {
struct in_addr in_addr;
- if (lookup_ip(node->data, &in_addr)) {
+ if (lookup_ip(node->value, &in_addr)) {
VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
} else {
daddr = in_addr.s_addr;
}
- } else if (!strcmp(node->name, "local_ip")) {
+ } else if (!strcmp(node->key, "local_ip")) {
struct in_addr in_addr;
- if (lookup_ip(node->data, &in_addr)) {
+ if (lookup_ip(node->value, &in_addr)) {
VLOG_WARN("%s: bad %s 'local_ip'", name, type);
} else {
saddr = in_addr.s_addr;
}
- } else if (!strcmp(node->name, "tos")) {
- if (!strcmp(node->data, "inherit")) {
+ } else if (!strcmp(node->key, "tos")) {
+ if (!strcmp(node->value, "inherit")) {
flags |= TNL_F_TOS_INHERIT;
} else {
- nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, atoi(node->data));
+ char *endptr;
+ int tos;
+ tos = strtol(node->value, &endptr, 0);
+ if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) {
+ nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, tos);
+ } else {
+ VLOG_WARN("%s: invalid TOS %s", name, node->value);
+ }
}
- } else if (!strcmp(node->name, "ttl")) {
- if (!strcmp(node->data, "inherit")) {
+ } else if (!strcmp(node->key, "ttl")) {
+ if (!strcmp(node->value, "inherit")) {
flags |= TNL_F_TTL_INHERIT;
} else {
- nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->data));
+ nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->value));
}
- } else if (!strcmp(node->name, "csum") && is_gre) {
- if (!strcmp(node->data, "true")) {
+ } else if (!strcmp(node->key, "csum") && is_gre) {
+ if (!strcmp(node->value, "true")) {
flags |= TNL_F_CSUM;
}
- } else if (!strcmp(node->name, "df_inherit")) {
- if (!strcmp(node->data, "true")) {
+ } else if (!strcmp(node->key, "df_inherit")) {
+ if (!strcmp(node->value, "true")) {
flags |= TNL_F_DF_INHERIT;
}
- } else if (!strcmp(node->name, "df_default")) {
- if (!strcmp(node->data, "false")) {
+ } else if (!strcmp(node->key, "df_default")) {
+ if (!strcmp(node->value, "false")) {
flags &= ~TNL_F_DF_DEFAULT;
}
- } else if (!strcmp(node->name, "pmtud")) {
- if (!strcmp(node->data, "false")) {
+ } else if (!strcmp(node->key, "pmtud")) {
+ if (!strcmp(node->value, "false")) {
flags &= ~TNL_F_PMTUD;
}
- } else if (!strcmp(node->name, "header_cache")) {
- if (!strcmp(node->data, "false")) {
+ } else if (!strcmp(node->key, "header_cache")) {
+ if (!strcmp(node->value, "false")) {
flags &= ~TNL_F_HDR_CACHE;
}
- } else if (!strcmp(node->name, "peer_cert") && is_ipsec) {
- if (shash_find(args, "certificate")) {
+ } else if (!strcmp(node->key, "peer_cert") && is_ipsec) {
+ if (smap_get(args, "certificate")) {
ipsec_mech_set = true;
} else {
const char *use_ssl_cert;
* 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);
}
ipsec_mech_set = true;
}
- } else if (!strcmp(node->name, "psk") && is_ipsec) {
+ } else if (!strcmp(node->key, "psk") && is_ipsec) {
ipsec_mech_set = true;
} else if (is_ipsec
- && (!strcmp(node->name, "certificate")
- || !strcmp(node->name, "private_key")
- || !strcmp(node->name, "use_ssl_cert"))) {
+ && (!strcmp(node->key, "certificate")
+ || !strcmp(node->key, "private_key")
+ || !strcmp(node->key, "use_ssl_cert"))) {
/* Ignore options not used by the netdev. */
- } else if (!strcmp(node->name, "key") ||
- !strcmp(node->name, "in_key") ||
- !strcmp(node->name, "out_key")) {
+ } else if (!strcmp(node->key, "key") ||
+ !strcmp(node->key, "in_key") ||
+ !strcmp(node->key, "out_key")) {
/* Handled separately below. */
} else {
- VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->name);
+ VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->key);
}
}
if (is_ipsec) {
- char *file_name = xasprintf("%s/%s", ovs_rundir(),
- "ovs-monitor-ipsec.pid");
- pid_t pid = read_pidfile(file_name);
- free(file_name);
+ static pid_t pid = 0;
+ if (pid <= 0) {
+ char *file_name = xasprintf("%s/%s", ovs_rundir(),
+ "ovs-monitor-ipsec.pid");
+ pid = read_pidfile(file_name);
+ free(file_name);
+ }
+
if (pid < 0) {
VLOG_ERR("%s: IPsec requires the ovs-monitor-ipsec daemon",
name);
return EINVAL;
}
- if (shash_find(args, "peer_cert") && shash_find(args, "psk")) {
+ if (smap_get(args, "peer_cert") && smap_get(args, "psk")) {
VLOG_ERR("%s: cannot define both 'peer_cert' and 'psk'", name);
return EINVAL;
}
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;
}
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]) {
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);
}
}
}
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) {
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;
}
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,
/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "packets.h"
#include "poll-loop.h"
#include "shash.h"
+#include "smap.h"
#include "sset.h"
#include "svec.h"
#include "vlog.h"
netdev_register_provider(&netdev_tap_class);
netdev_register_provider(&netdev_tap_pl_class);
netdev_vport_register();
+#endif
+#ifdef __FreeBSD__
+ netdev_register_provider(&netdev_tap_class);
+ netdev_register_provider(&netdev_bsd_class);
#endif
netdev_register_provider(&netdev_tunnel_class);
}
/* 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));
}
}
/* 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;
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);
}
int
netdev_get_flags(const struct netdev *netdev_, enum netdev_flags *flagsp)
{
- struct netdev *netdev = (struct netdev *) netdev_;
+ struct netdev *netdev = CONST_CAST(struct netdev *, netdev_);
return do_update_flags(netdev, 0, 0, flagsp, false);
}
*
* 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'.
*
* 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;
retval = class->get_qos(netdev, typep, details);
if (retval) {
*typep = NULL;
- shash_clear_free_data(details);
+ smap_clear(details);
}
return retval;
} else {
* 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;
if (class->set_qos) {
if (!details) {
- static struct shash empty = SHASH_INITIALIZER(&empty);
+ static struct smap empty = SMAP_INITIALIZER(&empty);
details = ∅
}
return class->set_qos(netdev, type, details);
* 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;
? class->get_queue(netdev, queue_id, details)
: EOPNOTSUPP);
if (retval) {
- shash_clear_free_data(details);
+ smap_clear(details);
}
return retval;
}
* 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
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ofpbuf;
struct in_addr;
struct in6_addr;
-struct shash;
+struct smap;
struct sset;
enum netdev_flags {
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 *);
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 *);
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);
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
static int
-nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg, bool wait)
+nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg,
+ uint32_t nlmsg_seq, bool wait)
{
struct nlmsghdr *nlmsg = nl_msg_nlmsghdr(msg);
int error;
nlmsg->nlmsg_len = msg->size;
- nlmsg->nlmsg_seq = nl_sock_allocate_seq(sock, 1);
+ nlmsg->nlmsg_seq = nlmsg_seq;
nlmsg->nlmsg_pid = sock->pid;
do {
int retval;
}
/* Tries to send 'msg', which must contain a Netlink message, to the kernel on
- * 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, and
- * nlmsg_pid will be set to 'sock''s pid, before the message is sent.
+ * 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, nlmsg_pid
+ * will be set to 'sock''s pid, and nlmsg_seq will be initialized to a fresh
+ * sequence number, before the message is sent.
*
* Returns 0 if successful, otherwise a positive errno value. If
* 'wait' is true, then the send will wait until buffer space is ready;
* otherwise, returns EAGAIN if the 'sock' send buffer is full. */
int
nl_sock_send(struct nl_sock *sock, const struct ofpbuf *msg, bool wait)
+{
+ return nl_sock_send_seq(sock, msg, nl_sock_allocate_seq(sock, 1), wait);
+}
+
+/* Tries to send 'msg', which must contain a Netlink message, to the kernel on
+ * 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, nlmsg_pid
+ * will be set to 'sock''s pid, and nlmsg_seq will be initialized to
+ * 'nlmsg_seq', before the message is sent.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. If
+ * 'wait' is true, then the send will wait until buffer space is ready;
+ * otherwise, returns EAGAIN if the 'sock' send buffer is full.
+ *
+ * This function is suitable for sending a reply to a request that was received
+ * with sequence number 'nlmsg_seq'. Otherwise, use nl_sock_send() instead. */
+int
+nl_sock_send_seq(struct nl_sock *sock, const struct ofpbuf *msg,
+ uint32_t nlmsg_seq, bool wait)
{
int error = nl_sock_cow__(sock);
if (error) {
return error;
}
- return nl_sock_send__(sock, msg, wait);
+ return nl_sock_send__(sock, msg, nlmsg_seq, wait);
}
/* This stress option is useful for testing that OVS properly tolerates
struct nl_transaction *transactionp;
struct nl_transaction transaction;
- transaction.request = (struct ofpbuf *) request;
+ transaction.request = CONST_CAST(struct ofpbuf *, request);
transaction.reply = replyp ? ofpbuf_new(1024) : NULL;
transactionp = &transaction;
}
nl_msg_nlmsghdr(request)->nlmsg_flags |= NLM_F_DUMP | NLM_F_ACK;
- dump->status = nl_sock_send__(sock, request, true);
+ dump->status = nl_sock_send__(sock, request, nl_sock_allocate_seq(sock, 1),
+ true);
dump->seq = nl_msg_nlmsghdr(request)->nlmsg_seq;
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
int nl_sock_leave_mcgroup(struct nl_sock *, unsigned int multicast_group);
int nl_sock_send(struct nl_sock *, const struct ofpbuf *, bool wait);
+int nl_sock_send_seq(struct nl_sock *, const struct ofpbuf *,
+ uint32_t nlmsg_seq, bool wait);
int nl_sock_recv(struct nl_sock *, struct ofpbuf *, bool wait);
int nl_sock_transact(struct nl_sock *, const struct ofpbuf *request,
struct ofpbuf **replyp);
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "classifier.h"
#include "dynamic-string.h"
#include "meta-flow.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "ofpbuf.h"
if (match_len < 4) {
if (match_len) {
- VLOG_DBG_RL(&rl, "nx_match ends with partial nxm_header");
+ VLOG_DBG_RL(&rl, "nx_match ends with partial (%u-byte) nxm_header",
+ match_len);
}
return 0;
}
}
static enum ofperr
-nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
- uint16_t priority, struct cls_rule *rule,
- ovs_be64 *cookie, ovs_be64 *cookie_mask)
+nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
+ uint16_t priority, struct cls_rule *rule,
+ ovs_be64 *cookie, ovs_be64 *cookie_mask)
{
uint32_t header;
- uint8_t *p;
assert((cookie != NULL) == (cookie_mask != NULL));
- p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
- if (!p) {
- VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a "
- "multiple of 8, is longer than space in message (max "
- "length %zu)", match_len, b->size);
- return OFPERR_OFPBMC_BAD_LEN;
- }
-
cls_rule_init_catchall(rule, priority);
if (cookie) {
*cookie = *cookie_mask = htonll(0);
}
+ if (!match_len) {
+ return 0;
+ }
+
for (;
(header = nx_entry_ok(p, match_len)) != 0;
p += 4 + NXM_LENGTH(header), match_len -= 4 + NXM_LENGTH(header)) {
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;
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. */
return match_len ? OFPERR_OFPBMC_BAD_LEN : 0;
}
+static enum ofperr
+nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
+ uint16_t priority, struct cls_rule *rule,
+ ovs_be64 *cookie, ovs_be64 *cookie_mask)
+{
+ uint8_t *p = NULL;
+
+ if (match_len) {
+ p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
+ if (!p) {
+ VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a "
+ "multiple of 8, is longer than space in message (max "
+ "length %zu)", match_len, b->size);
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+ }
+
+ return nx_pull_raw(p, match_len, strict, priority, rule,
+ cookie, cookie_mask);
+}
+
/* Parses the nx_match formatted match description in 'b' with length
* 'match_len'. The results are stored in 'rule', which is initialized with
* 'priority'. If 'cookie' and 'cookie_mask' contain valid pointers, then the
return nx_pull_match__(b, match_len, false, priority, rule, cookie,
cookie_mask);
}
+
+static enum ofperr
+oxm_pull_match__(struct ofpbuf *b, bool strict,
+ uint16_t priority, struct cls_rule *rule)
+{
+ struct ofp11_match_header *omh = b->data;
+ uint8_t *p;
+ uint16_t match_len;
+
+ if (b->size < sizeof *omh) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+
+ match_len = ntohs(omh->length);
+ if (match_len < sizeof *omh) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+
+ if (omh->type != htons(OFPMT_OXM)) {
+ return OFPERR_OFPBMC_BAD_TYPE;
+ }
+
+ p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
+ if (!p) {
+ VLOG_DBG_RL(&rl, "oxm length %u, rounded up to a "
+ "multiple of 8, is longer than space in message (max "
+ "length %zu)", match_len, b->size);
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
+
+ return nx_pull_raw(p + sizeof *omh, match_len - sizeof *omh,
+ strict, priority, rule, NULL, NULL);
+}
+
+/* Parses the oxm formatted match description preceeded by a struct
+ * ofp11_match in 'b' with length 'match_len'. The results are stored in
+ * 'rule', which is initialized with 'priority'.
+ *
+ * Fails with an error when encountering unknown OXM headers.
+ *
+ * Returns 0 if successful, otherwise an OpenFlow error code. */
+enum ofperr
+oxm_pull_match(struct ofpbuf *b, uint16_t priority, struct cls_rule *rule)
+{
+ return oxm_pull_match__(b, true, priority, rule);
+}
+
+/* Behaves the same as oxm_pull_match() with one exception. Skips over unknown
+ * PXM headers instead of failing with an error when they are encountered. */
+enum ofperr
+oxm_pull_match_loose(struct ofpbuf *b, uint16_t priority,
+ struct cls_rule *rule)
+{
+ return oxm_pull_match__(b, false, priority, rule);
+}
\f
/* nx_put_match() and helpers.
*
}
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);
+ }
}
}
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;
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));
}
/* Appends to 'b' the nx_match format that expresses 'cr' (except for
- * 'cr->priority', because priority is not part of nx_match), plus enough
- * zero bytes to pad the nx_match out to a multiple of 8. For Flow Mod
- * and Flow Stats Requests messages, a 'cookie' and 'cookie_mask' may be
- * supplied. Otherwise, 'cookie_mask' should be zero.
+ * 'cr->priority', because priority is not part of nx_match). For Flow Mod and
+ * Flow Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied.
+ * Otherwise, 'cookie_mask' should be zero.
*
* This function can cause 'b''s data to be reallocated.
*
*
* If 'cr' is a catch-all rule that matches every packet, then this function
* appends nothing to 'b' and returns 0. */
-int
-nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
- ovs_be64 cookie, ovs_be64 cookie_mask)
+static int
+nx_put_raw(struct ofpbuf *b, bool oxm, const struct cls_rule *cr,
+ ovs_be64 cookie, ovs_be64 cookie_mask)
{
const flow_wildcards_t wc = cr->wc.wildcards;
const struct flow *flow = &cr->flow;
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
/* Metadata. */
if (!(wc & FWW_IN_PORT)) {
uint16_t in_port = flow->in_port;
- nxm_put_16(b, NXM_OF_IN_PORT, htons(in_port));
+ if (oxm) {
+ nxm_put_32(b, OXM_OF_IN_PORT, ofputil_port_to_ofp11(in_port));
+ } else {
+ nxm_put_16(b, NXM_OF_IN_PORT, htons(in_port));
+ }
}
/* Ethernet. */
- nxm_put_eth_dst(b, wc, flow->dl_dst);
- if (!(wc & FWW_DL_SRC)) {
- nxm_put_eth(b, NXM_OF_ETH_SRC, flow->dl_src);
- }
+ nxm_put_eth_masked(b, oxm ? OXM_OF_ETH_SRC : NXM_OF_ETH_SRC,
+ flow->dl_src, cr->wc.dl_src_mask);
+ nxm_put_eth_masked(b, oxm ? OXM_OF_ETH_DST : NXM_OF_ETH_DST,
+ flow->dl_dst, cr->wc.dl_dst_mask);
if (!(wc & FWW_DL_TYPE)) {
- nxm_put_16(b, NXM_OF_ETH_TYPE,
+ nxm_put_16(b, oxm ? OXM_OF_ETH_TYPE : NXM_OF_ETH_TYPE,
ofputil_dl_type_to_openflow(flow->dl_type));
}
/* 802.1Q. */
- nxm_put_16m(b, NXM_OF_VLAN_TCI, flow->vlan_tci, cr->wc.vlan_tci_mask);
+ if (oxm) {
+ ovs_be16 vid = flow->vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI);
+ ovs_be16 mask = cr->wc.vlan_tci_mask & htons(VLAN_VID_MASK | VLAN_CFI);
+
+ if (mask == htons(VLAN_VID_MASK | VLAN_CFI)) {
+ nxm_put_16(b, OXM_OF_VLAN_VID, vid);
+ } else if (mask) {
+ nxm_put_16m(b, OXM_OF_VLAN_VID, vid, mask);
+ }
+
+ if (vid && vlan_tci_to_pcp(cr->wc.vlan_tci_mask)) {
+ nxm_put_8(b, OXM_OF_VLAN_PCP, vlan_tci_to_pcp(flow->vlan_tci));
+ }
+
+ } else {
+ nxm_put_16m(b, NXM_OF_VLAN_TCI, flow->vlan_tci, cr->wc.vlan_tci_mask);
+ }
/* L3. */
if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IP)) {
/* IP. */
- nxm_put_32m(b, NXM_OF_IP_SRC, flow->nw_src, cr->wc.nw_src_mask);
- nxm_put_32m(b, NXM_OF_IP_DST, flow->nw_dst, cr->wc.nw_dst_mask);
- nxm_put_ip(b, cr, IPPROTO_ICMP, NXM_OF_ICMP_TYPE, NXM_OF_ICMP_CODE);
+ nxm_put_32m(b, oxm ? OXM_OF_IPV4_SRC : NXM_OF_IP_SRC,
+ flow->nw_src, cr->wc.nw_src_mask);
+ nxm_put_32m(b, oxm ? OXM_OF_IPV4_DST : NXM_OF_IP_DST,
+ flow->nw_dst, cr->wc.nw_dst_mask);
+ nxm_put_ip(b, cr, IPPROTO_ICMP,
+ oxm ? OXM_OF_ICMPV4_TYPE : NXM_OF_ICMP_TYPE,
+ oxm ? OXM_OF_ICMPV4_CODE : NXM_OF_ICMP_CODE, oxm);
} else if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IPV6)) {
/* IPv6. */
- nxm_put_ipv6(b, NXM_NX_IPV6_SRC, &flow->ipv6_src,
- &cr->wc.ipv6_src_mask);
- nxm_put_ipv6(b, NXM_NX_IPV6_DST, &flow->ipv6_dst,
- &cr->wc.ipv6_dst_mask);
- nxm_put_ip(b, cr,
- IPPROTO_ICMPV6, NXM_NX_ICMPV6_TYPE, NXM_NX_ICMPV6_CODE);
-
- if (!(wc & FWW_IPV6_LABEL)) {
- nxm_put_32(b, NXM_NX_IPV6_LABEL, flow->ipv6_label);
- }
+ nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_SRC : NXM_NX_IPV6_SRC,
+ &flow->ipv6_src, &cr->wc.ipv6_src_mask);
+ nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_DST : NXM_NX_IPV6_DST,
+ &flow->ipv6_dst, &cr->wc.ipv6_dst_mask);
+ nxm_put_ip(b, cr, IPPROTO_ICMPV6,
+ oxm ? OXM_OF_ICMPV6_TYPE : NXM_NX_ICMPV6_TYPE,
+ oxm ? OXM_OF_ICMPV6_CODE : NXM_NX_ICMPV6_CODE, oxm);
+
+ nxm_put_32m(b, oxm ? OXM_OF_IPV6_FLABEL : NXM_NX_IPV6_LABEL,
+ flow->ipv6_label, cr->wc.ipv6_label_mask);
if (flow->nw_proto == IPPROTO_ICMPV6
&& (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
flow->tp_src == htons(ND_NEIGHBOR_ADVERT))) {
- nxm_put_ipv6(b, NXM_NX_ND_TARGET, &flow->nd_target,
- &cr->wc.nd_target_mask);
- if (!(wc & FWW_ARP_SHA)
- && flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
- nxm_put_eth(b, NXM_NX_ND_SLL, flow->arp_sha);
+ nxm_put_ipv6(b, oxm ? OXM_OF_IPV6_ND_TARGET : NXM_NX_ND_TARGET,
+ &flow->nd_target, &cr->wc.nd_target_mask);
+ if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
+ nxm_put_eth_masked(b, oxm ? OXM_OF_IPV6_ND_SLL : NXM_NX_ND_SLL,
+ flow->arp_sha, cr->wc.arp_sha_mask);
}
- if (!(wc & FWW_ARP_THA)
- && flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
- nxm_put_eth(b, NXM_NX_ND_TLL, flow->arp_tha);
+ if (flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+ nxm_put_eth_masked(b, oxm ? OXM_OF_IPV6_ND_TLL : NXM_NX_ND_TLL,
+ flow->arp_tha, cr->wc.arp_tha_mask);
}
}
} else if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_ARP)) {
/* ARP. */
if (!(wc & FWW_NW_PROTO)) {
- nxm_put_16(b, NXM_OF_ARP_OP, htons(flow->nw_proto));
- }
- nxm_put_32m(b, NXM_OF_ARP_SPA, flow->nw_src, cr->wc.nw_src_mask);
- nxm_put_32m(b, NXM_OF_ARP_TPA, flow->nw_dst, cr->wc.nw_dst_mask);
- if (!(wc & FWW_ARP_SHA)) {
- nxm_put_eth(b, NXM_NX_ARP_SHA, flow->arp_sha);
- }
- if (!(wc & FWW_ARP_THA)) {
- nxm_put_eth(b, NXM_NX_ARP_THA, flow->arp_tha);
+ nxm_put_16(b, oxm ? OXM_OF_ARP_OP : NXM_OF_ARP_OP,
+ htons(flow->nw_proto));
}
+ nxm_put_32m(b, oxm ? OXM_OF_ARP_SPA : NXM_OF_ARP_SPA,
+ flow->nw_src, cr->wc.nw_src_mask);
+ nxm_put_32m(b, oxm ? OXM_OF_ARP_TPA : NXM_OF_ARP_TPA,
+ flow->nw_dst, cr->wc.nw_dst_mask);
+ nxm_put_eth_masked(b, oxm ? OXM_OF_ARP_SHA : NXM_NX_ARP_SHA,
+ flow->arp_sha, cr->wc.arp_sha_mask);
+ nxm_put_eth_masked(b, oxm ? OXM_OF_ARP_THA : NXM_NX_ARP_THA,
+ flow->arp_tha, cr->wc.arp_tha_mask);
}
/* Tunnel ID. */
htonl(flow->regs[i]), htonl(cr->wc.reg_masks[i]));
}
+ /* OpenFlow 1.1+ Metadata. */
+ nxm_put_64m(b, OXM_OF_METADATA, flow->metadata, cr->wc.metadata_mask);
+
/* Cookie. */
nxm_put_64m(b, NXM_NX_COOKIE, cookie, cookie_mask);
match_len = b->size - start_len;
+ return match_len;
+}
+
+/* Appends to 'b' the nx_match format that expresses 'cr' (except for
+ * 'cr->priority', because priority is not part of nx_match), plus enough zero
+ * bytes to pad the nx_match out to a multiple of 8. For Flow Mod and Flow
+ * Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied.
+ * Otherwise, 'cookie_mask' should be zero.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b', excluding padding. The return
+ * value can be zero if it appended nothing at all to 'b' (which happens if
+ * 'cr' is a catch-all rule that matches every packet). */
+int
+nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
+ ovs_be64 cookie, ovs_be64 cookie_mask)
+{
+ int match_len = nx_put_raw(b, false, cr, cookie, cookie_mask);
+
+ ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
+ return match_len;
+}
+
+
+/* Appends to 'b' an struct ofp11_match_header followed by the oxm format that
+ * expresses 'cr' (except for 'cr->priority', because priority is not part of
+ * nx_match), plus enough zero bytes to pad the data appended out to a multiple
+ * of 8.
+ *
+ * This function can cause 'b''s data to be reallocated.
+ *
+ * Returns the number of bytes appended to 'b', excluding the padding. Never
+ * returns zero. */
+int
+oxm_put_match(struct ofpbuf *b, const struct cls_rule *cr)
+{
+ int match_len;
+ struct ofp11_match_header *omh;
+ size_t start_len = b->size;
+ ovs_be64 cookie = htonll(0), cookie_mask = htonll(0);
+
+ ofpbuf_put_uninit(b, sizeof *omh);
+ match_len = nx_put_raw(b, true, cr, cookie, cookie_mask) + sizeof *omh;
ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
+
+ omh = (struct ofp11_match_header *)((char *)b->data + start_len);
+ omh->type = htons(OFPMT_OXM);
+ omh->length = htons(match_len);
+
return match_len;
}
\f
return ds_steal_cstr(&s);
}
+char *
+oxm_match_to_string(const uint8_t *p, unsigned int match_len)
+{
+ const struct ofp11_match_header *omh = (struct ofp11_match_header *)p;
+ uint16_t match_len_;
+ struct ds s;
+
+ ds_init(&s);
+
+ if (match_len < sizeof *omh) {
+ ds_put_format(&s, "<match too short: %u>", match_len);
+ goto err;
+ }
+
+ if (omh->type != htons(OFPMT_OXM)) {
+ ds_put_format(&s, "<bad match type field: %u>", ntohs(omh->type));
+ goto err;
+ }
+
+ match_len_ = ntohs(omh->length);
+ if (match_len_ < sizeof *omh) {
+ ds_put_format(&s, "<match length field too short: %u>", match_len_);
+ goto err;
+ }
+
+ if (match_len_ != match_len) {
+ ds_put_format(&s, "<match length field incorrect: %u != %u>",
+ match_len_, match_len);
+ goto err;
+ }
+
+ return nx_match_to_string(p + sizeof *omh, match_len - sizeof *omh);
+
+err:
+ return ds_steal_cstr(&s);
+}
+
static void
format_nxm_field_name(struct ds *s, uint32_t header)
{
const struct mf_field *mf = mf_from_nxm_header(header);
if (mf) {
- ds_put_cstr(s, mf->nxm_name);
+ ds_put_cstr(s, IS_OXM_HEADER(header) ? mf->oxm_name : mf->nxm_name);
if (NXM_HASMASK(header)) {
ds_put_cstr(s, "_W");
}
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 {
\f
/* nx_match_from_string(). */
-int
-nx_match_from_string(const char *s, struct ofpbuf *b)
+static int
+nx_match_from_string_raw(const char *s, struct ofpbuf *b)
{
const char *full_s = s;
const size_t start_len = b->size;
- int match_len;
if (!strcmp(s, "<any>")) {
/* Ensure that 'b->data' isn't actually null. */
s++;
}
- match_len = b->size - start_len;
+ return b->size - start_len;
+}
+
+int
+nx_match_from_string(const char *s, struct ofpbuf *b)
+{
+ int match_len = nx_match_from_string_raw(s, b);
+ ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
+ return match_len;
+}
+
+int
+oxm_match_from_string(const char *s, struct ofpbuf *b)
+{
+ int match_len;
+ struct ofp11_match_header *omh;
+ size_t start_len = b->size;
+
+ ofpbuf_put_uninit(b, sizeof *omh);
+ match_len = nx_match_from_string_raw(s, b) + sizeof *omh;
ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
+
+ omh = (struct ofp11_match_header *)((char *)b->data + start_len);
+ omh->type = htons(OFPMT_OXM);
+ omh->length = htons(match_len);
+
return match_len;
}
\f
void
-nxm_parse_reg_move(struct nx_action_reg_move *move, const char *s)
+nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s)
{
const char *full_s = s;
- struct mf_subfield src, dst;
- s = mf_parse_subfield(&src, s);
+ s = mf_parse_subfield(&move->src, s);
if (strncmp(s, "->", 2)) {
ovs_fatal(0, "%s: missing `->' following source", full_s);
}
s += 2;
- s = mf_parse_subfield(&dst, s);
+ s = mf_parse_subfield(&move->dst, s);
if (*s != '\0') {
ovs_fatal(0, "%s: trailing garbage following destination", full_s);
}
- if (src.n_bits != dst.n_bits) {
+ if (move->src.n_bits != move->dst.n_bits) {
ovs_fatal(0, "%s: source field is %d bits wide but destination is "
- "%d bits wide", full_s, src.n_bits, dst.n_bits);
+ "%d bits wide", full_s,
+ move->src.n_bits, move->dst.n_bits);
}
-
- ofputil_init_NXAST_REG_MOVE(move);
- move->n_bits = htons(src.n_bits);
- move->src_ofs = htons(src.ofs);
- move->dst_ofs = htons(dst.ofs);
- move->src = htonl(src.field->nxm_header);
- move->dst = htonl(dst.field->nxm_header);
}
void
-nxm_parse_reg_load(struct nx_action_reg_load *load, const char *s)
+nxm_parse_reg_load(struct ofpact_reg_load *load, const char *s)
{
const char *full_s = s;
- struct mf_subfield dst;
- uint64_t value;
- value = strtoull(s, (char **) &s, 0);
+ load->value = strtoull(s, (char **) &s, 0);
if (strncmp(s, "->", 2)) {
ovs_fatal(0, "%s: missing `->' following value", full_s);
}
s += 2;
- s = mf_parse_subfield(&dst, s);
+ s = mf_parse_subfield(&load->dst, s);
if (*s != '\0') {
ovs_fatal(0, "%s: trailing garbage following destination", full_s);
}
- if (dst.n_bits < 64 && (value >> dst.n_bits) != 0) {
- ovs_fatal(0, "%s: value %"PRIu64" does not fit into %u bits",
- full_s, value, dst.n_bits);
+ if (load->dst.n_bits < 64 && (load->value >> load->dst.n_bits) != 0) {
+ ovs_fatal(0, "%s: value %"PRIu64" does not fit into %d bits",
+ full_s, load->value, load->dst.n_bits);
}
-
- ofputil_init_NXAST_REG_LOAD(load);
- load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
- load->dst = htonl(dst.field->nxm_header);
- load->value = htonll(value);
}
\f
/* nxm_format_reg_move(), nxm_format_reg_load(). */
void
-nxm_format_reg_move(const struct nx_action_reg_move *move, struct ds *s)
+nxm_format_reg_move(const struct ofpact_reg_move *move, struct ds *s)
{
- struct mf_subfield src, dst;
-
- nxm_decode_discrete(&src, move->src, move->src_ofs, move->n_bits);
- nxm_decode_discrete(&dst, move->dst, move->dst_ofs, move->n_bits);
-
ds_put_format(s, "move:");
- mf_format_subfield(&src, s);
+ mf_format_subfield(&move->src, s);
ds_put_cstr(s, "->");
- mf_format_subfield(&dst, s);
+ mf_format_subfield(&move->dst, s);
}
void
-nxm_format_reg_load(const struct nx_action_reg_load *load, struct ds *s)
+nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s)
{
- struct mf_subfield dst;
+ ds_put_format(s, "load:%#"PRIx64"->", load->value);
+ mf_format_subfield(&load->dst, s);
+}
+\f
+enum ofperr
+nxm_reg_move_from_openflow(const struct nx_action_reg_move *narm,
+ struct ofpbuf *ofpacts)
+{
+ struct ofpact_reg_move *move;
- ds_put_format(s, "load:%#"PRIx64"->", ntohll(load->value));
+ move = ofpact_put_REG_MOVE(ofpacts);
+ move->src.field = mf_from_nxm_header(ntohl(narm->src));
+ move->src.ofs = ntohs(narm->src_ofs);
+ move->src.n_bits = ntohs(narm->n_bits);
+ move->dst.field = mf_from_nxm_header(ntohl(narm->dst));
+ move->dst.ofs = ntohs(narm->dst_ofs);
+ move->dst.n_bits = ntohs(narm->n_bits);
- nxm_decode(&dst, load->dst, load->ofs_nbits);
- mf_format_subfield(&dst, s);
+ return nxm_reg_move_check(move, NULL);
}
-\f
-/* nxm_check_reg_move(), nxm_check_reg_load(). */
enum ofperr
-nxm_check_reg_move(const struct nx_action_reg_move *action,
- const struct flow *flow)
+nxm_reg_load_from_openflow(const struct nx_action_reg_load *narl,
+ struct ofpbuf *ofpacts)
{
- struct mf_subfield src;
- struct mf_subfield dst;
- int error;
+ struct ofpact_reg_load *load;
- nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits);
- error = mf_check_src(&src, flow);
- if (error) {
- return error;
+ load = ofpact_put_REG_LOAD(ofpacts);
+ load->dst.field = mf_from_nxm_header(ntohl(narl->dst));
+ load->dst.ofs = nxm_decode_ofs(narl->ofs_nbits);
+ load->dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits);
+ load->value = ntohll(narl->value);
+
+ /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in
+ * narl->value. */
+ if (load->dst.n_bits < 64 && load->value >> load->dst.n_bits) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
}
- nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits);
- return mf_check_dst(&dst, flow);
+ return nxm_reg_load_check(load, NULL);
}
-
+\f
enum ofperr
-nxm_check_reg_load(const struct nx_action_reg_load *action,
- const struct flow *flow)
+nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow)
{
- struct mf_subfield dst;
enum ofperr error;
- nxm_decode(&dst, action->dst, action->ofs_nbits);
- error = mf_check_dst(&dst, flow);
+ error = mf_check_src(&move->src, flow);
if (error) {
return error;
}
- /* Reject 'action' if a bit numbered 'n_bits' or higher is set to 1 in
- * action->value. */
- if (dst.n_bits < 64 && ntohll(action->value) >> dst.n_bits) {
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
+ return mf_check_dst(&move->dst, NULL);
+}
- return 0;
+enum ofperr
+nxm_reg_load_check(const struct ofpact_reg_load *load, const struct flow *flow)
+{
+ return mf_check_dst(&load->dst, flow);
}
\f
-/* nxm_execute_reg_move(), nxm_execute_reg_load(). */
-
void
-nxm_execute_reg_move(const struct nx_action_reg_move *action,
- struct flow *flow)
+nxm_reg_move_to_nxast(const struct ofpact_reg_move *move,
+ struct ofpbuf *openflow)
{
- struct mf_subfield src, dst;
- union mf_value src_value;
- union mf_value dst_value;
+ struct nx_action_reg_move *narm;
+
+ narm = ofputil_put_NXAST_REG_MOVE(openflow);
+ narm->n_bits = htons(move->dst.n_bits);
+ narm->src_ofs = htons(move->src.ofs);
+ narm->dst_ofs = htons(move->dst.ofs);
+ narm->src = htonl(move->src.field->nxm_header);
+ narm->dst = htonl(move->dst.field->nxm_header);
+}
- nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits);
- nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits);
+void
+nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
+ struct ofpbuf *openflow)
+{
+ struct nx_action_reg_load *narl;
- mf_get_value(dst.field, flow, &dst_value);
- mf_get_value(src.field, flow, &src_value);
- bitwise_copy(&src_value, src.field->n_bytes, src.ofs,
- &dst_value, dst.field->n_bytes, dst.ofs,
- src.n_bits);
- mf_set_flow_value(dst.field, &dst_value, flow);
+ narl = ofputil_put_NXAST_REG_LOAD(openflow);
+ narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits);
+ narl->dst = htonl(load->dst.field->nxm_header);
+ narl->value = htonll(load->value);
}
+\f
+/* nxm_execute_reg_move(), nxm_execute_reg_load(). */
void
-nxm_execute_reg_load(const struct nx_action_reg_load *action,
+nxm_execute_reg_move(const struct ofpact_reg_move *move,
struct flow *flow)
{
- struct mf_subfield dst;
+ union mf_value src_value;
+ union mf_value dst_value;
- nxm_decode(&dst, action->dst, action->ofs_nbits);
- mf_set_subfield_value(&dst, ntohll(action->value), flow);
+ mf_get_value(move->dst.field, flow, &dst_value);
+ mf_get_value(move->src.field, flow, &src_value);
+ bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs,
+ &dst_value, move->dst.field->n_bytes, move->dst.ofs,
+ move->src.n_bits);
+ mf_set_flow_value(move->dst.field, &dst_value, flow);
}
-/* Initializes 'sf->field' with the field corresponding to the given NXM
- * 'header' and 'sf->ofs' and 'sf->n_bits' decoded from 'ofs_nbits' with
- * nxm_decode_ofs() and nxm_decode_n_bits(), respectively.
- *
- * Afterward, 'sf' might be invalid in a few different ways:
- *
- * - 'sf->field' will be NULL if 'header' is unknown.
- *
- * - 'sf->ofs' and 'sf->n_bits' might exceed the width of sf->field.
- *
- * The caller should call mf_check_src() or mf_check_dst() to check for these
- * problems. */
void
-nxm_decode(struct mf_subfield *sf, ovs_be32 header, ovs_be16 ofs_nbits)
+nxm_execute_reg_load(const struct ofpact_reg_load *load, struct flow *flow)
{
- sf->field = mf_from_nxm_header(ntohl(header));
- sf->ofs = nxm_decode_ofs(ofs_nbits);
- sf->n_bits = nxm_decode_n_bits(ofs_nbits);
+ nxm_reg_load(&load->dst, load->value, flow);
}
-/* Initializes 'sf->field' with the field corresponding to the given NXM
- * 'header' and 'sf->ofs' and 'sf->n_bits' from 'ofs' and 'n_bits',
- * respectively.
- *
- * Afterward, 'sf' might be invalid in a few different ways:
- *
- * - 'sf->field' will be NULL if 'header' is unknown.
- *
- * - 'sf->ofs' and 'sf->n_bits' might exceed the width of sf->field.
- *
- * The caller should call mf_check_src() or mf_check_dst() to check for these
- * problems. */
void
-nxm_decode_discrete(struct mf_subfield *sf, ovs_be32 header,
- ovs_be16 ofs, ovs_be16 n_bits)
+nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
+ struct flow *flow)
{
- sf->field = mf_from_nxm_header(ntohl(header));
- sf->ofs = ntohs(ofs);
- sf->n_bits = ntohs(n_bits);
+ union mf_value dst_value;
+ union mf_value src_value;
+
+ mf_get_value(dst->field, flow, &dst_value);
+ src_value.be64 = htonll(src_data);
+ bitwise_copy(&src_value, sizeof src_value.be64, 0,
+ &dst_value, dst->field->n_bytes, dst->ofs,
+ dst->n_bits);
+ mf_set_flow_value(dst->field, &dst_value, flow);
}
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <sys/types.h>
#include <netinet/in.h>
#include "flow.h"
+#include "ofp-errors.h"
#include "openvswitch/types.h"
#include "ofp-errors.h"
struct ds;
struct flow;
struct mf_subfield;
+struct ofpact_reg_move;
+struct ofpact_reg_load;
struct ofpbuf;
struct nx_action_reg_load;
struct nx_action_reg_move;
uint16_t priority, struct cls_rule *,
ovs_be64 *cookie, ovs_be64 *cookie_mask);
enum ofperr nx_pull_match_loose(struct ofpbuf *, unsigned int match_len,
- uint16_t priority, struct cls_rule *,
- ovs_be64 *cookie, ovs_be64 *cookie_mask);
+ uint16_t priority,
+ struct cls_rule *, ovs_be64 *cookie,
+ ovs_be64 *cookie_mask);
+enum ofperr oxm_pull_match(struct ofpbuf *, uint16_t priority,
+ struct cls_rule *);
+enum ofperr oxm_pull_match_loose(struct ofpbuf *, uint16_t priority,
+ struct cls_rule *);
int nx_put_match(struct ofpbuf *, const struct cls_rule *,
ovs_be64 cookie, ovs_be64 cookie_mask);
+int oxm_put_match(struct ofpbuf *, const struct cls_rule *);
char *nx_match_to_string(const uint8_t *, unsigned int match_len);
+char *oxm_match_to_string(const uint8_t *, unsigned int match_len);
int nx_match_from_string(const char *, struct ofpbuf *);
+int oxm_match_from_string(const char *, struct ofpbuf *);
+
+void nxm_parse_reg_move(struct ofpact_reg_move *, const char *);
+void nxm_parse_reg_load(struct ofpact_reg_load *, const char *);
-void nxm_parse_reg_move(struct nx_action_reg_move *, const char *);
-void nxm_parse_reg_load(struct nx_action_reg_load *, const char *);
+void nxm_format_reg_move(const struct ofpact_reg_move *, struct ds *);
+void nxm_format_reg_load(const struct ofpact_reg_load *, struct ds *);
-void nxm_format_reg_move(const struct nx_action_reg_move *, struct ds *);
-void nxm_format_reg_load(const struct nx_action_reg_load *, struct ds *);
+enum ofperr nxm_reg_move_from_openflow(const struct nx_action_reg_move *,
+ struct ofpbuf *ofpacts);
+enum ofperr nxm_reg_load_from_openflow(const struct nx_action_reg_load *,
+ struct ofpbuf *ofpacts);
-enum ofperr nxm_check_reg_move(const struct nx_action_reg_move *,
+enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *,
const struct flow *);
-enum ofperr nxm_check_reg_load(const struct nx_action_reg_load *,
+enum ofperr nxm_reg_load_check(const struct ofpact_reg_load *,
const struct flow *);
-void nxm_execute_reg_move(const struct nx_action_reg_move *, struct flow *);
-void nxm_execute_reg_load(const struct nx_action_reg_load *, struct flow *);
+void nxm_reg_move_to_nxast(const struct ofpact_reg_move *,
+ struct ofpbuf *openflow);
+void nxm_reg_load_to_nxast(const struct ofpact_reg_load *,
+ struct ofpbuf *openflow);
+
+void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *);
+void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *);
+void nxm_reg_load(const struct mf_subfield *, uint64_t src_data,
+ struct flow *);
int nxm_field_bytes(uint32_t header);
int nxm_field_bits(uint32_t header);
{
return (ntohs(ofs_nbits) & 0x3f) + 1;
}
-
-void nxm_decode(struct mf_subfield *, ovs_be32 header, ovs_be16 ofs_nbits);
-void nxm_decode_discrete(struct mf_subfield *, ovs_be32 header,
- ovs_be16 ofs, ovs_be16 n_bits);
\f
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
-/* Upper bound on the length of an nx_match. The longest nx_match (an
- * IPV6 neighbor discovery message using 5 registers) would be:
- *
- * header value mask total
- * ------ ----- ---- -----
- * NXM_OF_IN_PORT 4 2 -- 6
- * NXM_OF_ETH_DST_W 4 6 6 16
- * NXM_OF_ETH_SRC 4 6 -- 10
- * NXM_OF_ETH_TYPE 4 2 -- 6
- * NXM_OF_VLAN_TCI 4 2 2 8
- * NXM_OF_IP_TOS 4 1 -- 5
- * NXM_NX_IP_ECN 4 1 -- 5
- * NXM_OF_IP_TTL 4 1 -- 5
- * NXM_NX_IP_FRAG 4 1 1 8
- * NXM_OF_IP_PROTO 4 2 -- 6
- * NXM_OF_IPV6_SRC_W 4 16 16 36
- * NXM_OF_IPV6_DST_W 4 16 16 36
- * NXM_OF_IPV6_LABEL 4 4 -- 8
- * NXM_OF_ICMP_TYPE 4 1 -- 5
- * NXM_OF_ICMP_CODE 4 1 -- 5
- * NXM_NX_ND_TARGET 4 16 16 36
- * NXM_NX_ND_SLL 4 6 -- 10
- * NXM_NX_REG_W(0) 4 4 4 12
- * NXM_NX_REG_W(1) 4 4 4 12
- * NXM_NX_REG_W(2) 4 4 4 12
- * NXM_NX_REG_W(3) 4 4 4 12
- * NXM_NX_REG_W(4) 4 4 4 12
- * NXM_NX_REG_W(5) 4 4 4 12
- * NXM_NX_REG_W(6) 4 4 4 12
- * NXM_NX_REG_W(7) 4 4 4 12
- * NXM_NX_TUN_ID_W 4 8 8 20
- * -------------------------------------------
- * total 327
- *
- * So this value is conservative.
- */
-#define NXM_MAX_LEN 400
-
/* This is my guess at the length of a "typical" nx_match, for use in
* predicting space requirements. */
#define NXM_TYPICAL_LEN 64
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#include <arpa/inet.h>
#include <config.h>
+#include <arpa/inet.h>
#include "odp-util.h"
#include <errno.h>
#include <inttypes.h>
#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"
* 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);
ds_put_format(ds, "))");
}
+static const char *
+slow_path_reason_to_string(enum slow_path_reason bit)
+{
+ switch (bit) {
+ case SLOW_CFM:
+ return "cfm";
+ case SLOW_LACP:
+ return "lacp";
+ case SLOW_STP:
+ return "stp";
+ case SLOW_IN_BAND:
+ return "in_band";
+ case SLOW_CONTROLLER:
+ return "controller";
+ case SLOW_MATCH:
+ return "match";
+ default:
+ return NULL;
+ }
+}
+
+static void
+format_slow_path_reason(struct ds *ds, uint32_t slow)
+{
+ uint32_t bad = 0;
+
+ while (slow) {
+ uint32_t bit = rightmost_1bit(slow);
+ const char *s;
+
+ s = slow_path_reason_to_string(bit);
+ if (s) {
+ ds_put_format(ds, "%s,", s);
+ } else {
+ bad |= bit;
+ }
+
+ slow &= ~bit;
+ }
+
+ if (bad) {
+ ds_put_format(ds, "0x%"PRIx32",", bad);
+ }
+ ds_chomp(ds, ',');
+}
+
static void
format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
{
if (a[OVS_USERSPACE_ATTR_USERDATA]) {
uint64_t userdata = nl_attr_get_u64(a[OVS_USERSPACE_ATTR_USERDATA]);
- struct user_action_cookie cookie;
+ union user_action_cookie cookie;
memcpy(&cookie, &userdata, sizeof cookie);
- if (cookie.type == USER_ACTION_COOKIE_SFLOW) {
- ds_put_format(ds, ",sFlow,n_output=%"PRIu8","
- "vid=%"PRIu16",pcp=%"PRIu8",ifindex=%"PRIu32,
- cookie.n_output, vlan_tci_to_vid(cookie.vlan_tci),
- vlan_tci_to_pcp(cookie.vlan_tci), cookie.data);
- } else {
+ switch (cookie.type) {
+ case USER_ACTION_COOKIE_SFLOW:
+ ds_put_format(ds, ",sFlow("
+ "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
+ vlan_tci_to_vid(cookie.sflow.vlan_tci),
+ vlan_tci_to_pcp(cookie.sflow.vlan_tci),
+ cookie.sflow.output);
+ break;
+
+ case USER_ACTION_COOKIE_SLOW_PATH:
+ ds_put_cstr(ds, ",slow_path(");
+ if (cookie.slow_path.reason) {
+ format_slow_path_reason(ds, cookie.slow_path.reason);
+ }
+ ds_put_char(ds, ')');
+ break;
+
+ case USER_ACTION_COOKIE_UNSPEC:
+ default:
ds_put_format(ds, ",userdata=0x%"PRIx64, userdata);
+ break;
}
}
}
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
if (port_names) {
int len = strcspn(s, delimiters);
- struct shash_node *node;
+ struct simap_node *node;
- node = shash_find_len(port_names, s, len);
+ node = simap_find_len(port_names, s, len);
if (node) {
- nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT,
- (uintptr_t) node->data);
+ nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data);
return len;
}
}
{
unsigned long long int pid;
- unsigned long long int ifindex;
+ unsigned long long int output;
char userdata_s[32];
- int n_output;
int vid, pcp;
int n = -1;
if (sscanf(s, "userspace(pid=%lli)%n", &pid, &n) > 0 && n > 0) {
odp_put_userspace_action(pid, NULL, actions);
return n;
- } else if (sscanf(s, "userspace(pid=%lli,sFlow,n_output=%i,vid=%i,"
- "pcp=%i,ifindex=%lli)%n", &pid, &n_output,
- &vid, &pcp, &ifindex, &n) > 0 && n > 0) {
- struct user_action_cookie cookie;
+ } else if (sscanf(s, "userspace(pid=%lli,sFlow(vid=%i,"
+ "pcp=%i,output=%lli))%n",
+ &pid, &vid, &pcp, &output, &n) > 0 && n > 0) {
+ union user_action_cookie cookie;
uint16_t tci;
tci = vid | (pcp << VLAN_PCP_SHIFT);
}
cookie.type = USER_ACTION_COOKIE_SFLOW;
- cookie.n_output = n_output;
- cookie.vlan_tci = htons(tci);
- cookie.data = ifindex;
+ cookie.sflow.vlan_tci = htons(tci);
+ cookie.sflow.output = output;
+ odp_put_userspace_action(pid, &cookie, actions);
+ return n;
+ } else if (sscanf(s, "userspace(pid=%lli,slow_path(%n", &pid, &n) > 0
+ && n > 0) {
+ union user_action_cookie cookie;
+
+ cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
+ cookie.slow_path.unused = 0;
+ cookie.slow_path.reason = 0;
+
+ while (s[n] != ')') {
+ uint32_t bit;
+
+ for (bit = 1; bit; bit <<= 1) {
+ const char *reason = slow_path_reason_to_string(bit);
+ size_t len = strlen(reason);
+
+ if (reason
+ && !strncmp(s + n, reason, len)
+ && (s[n + len] == ',' || s[n + len] == ')'))
+ {
+ cookie.slow_path.reason |= bit;
+ n += len + (s[n + len] == ',');
+ break;
+ }
+ }
+
+ if (!bit) {
+ return -EINVAL;
+ }
+ }
+ if (s[n + 1] != ')') {
+ return -EINVAL;
+ }
+ n += 2;
+
odp_put_userspace_action(pid, &cookie, actions);
return n;
} else if (sscanf(s, "userspace(pid=%lli,userdata="
"%31[x0123456789abcdefABCDEF])%n", &pid, userdata_s,
&n) > 0 && n > 0) {
- struct user_action_cookie cookie;
+ union user_action_cookie cookie;
uint64_t userdata;
userdata = strtoull(userdata_s, NULL, 0);
for (;;) {
int retval;
- s += strspn(s, delimiters);
+ n += strspn(s + n, delimiters);
if (s[n] == ')') {
break;
}
return retval;
}
n += retval;
-
}
nl_msg_end_nested(actions, actions_ofs);
nl_msg_end_nested(actions, sample_ofs);
* 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;
}
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
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;
}
}
* 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;
: 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)
{
nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tun_id);
}
- if (flow->in_port != OFPP_NONE) {
+ if (flow->in_port != OFPP_NONE && flow->in_port != OFPP_CONTROLLER) {
nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT,
ofp_port_to_odp_port(flow->in_port));
}
* the start of the cookie. (If 'cookie' is null, then the return value is not
* meaningful.) */
size_t
-odp_put_userspace_action(uint32_t pid, const struct user_action_cookie *cookie,
+odp_put_userspace_action(uint32_t pid, const union user_action_cookie *cookie,
struct ofpbuf *odp_actions)
{
size_t offset;
commit_set_port_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
{
- if (!base->tp_src || !base->tp_dst) {
+ if (!base->tp_src && !base->tp_dst) {
return;
}
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct flow;
struct nlattr;
struct ofpbuf;
-struct shash;
+struct simap;
-#define OVSP_NONE ((uint16_t) -1)
+#define OVSP_NONE UINT16_MAX
static inline uint16_t
ofp_port_to_odp_port(uint16_t ofp_port)
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
* ------ --- ------ -----
* 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
};
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 *);
struct flow *);
const char *odp_key_fitness_to_string(enum odp_key_fitness);
+void commit_odp_actions(const struct flow *, struct flow *base,
+ struct ofpbuf *odp_actions);
+\f
+/* ofproto-dpif interface.
+ *
+ * The following types and functions are logically part of ofproto-dpif.
+ * ofproto-dpif puts values of these types into the flows that it installs in
+ * the kernel datapath, though, so ovs-dpctl needs to interpret them so that
+ * it can print flows in a more human-readable manner. */
+
enum user_action_cookie_type {
USER_ACTION_COOKIE_UNSPEC,
USER_ACTION_COOKIE_SFLOW, /* Packet for sFlow sampling. */
+ USER_ACTION_COOKIE_SLOW_PATH /* Userspace must process this flow. */
};
/* user_action_cookie is passed as argument to OVS_ACTION_ATTR_USERSPACE.
- * Since is it passed to kernel as u64, its size has to be 8 bytes. */
-struct user_action_cookie {
- uint8_t type; /* enum user_action_cookie_type. */
- uint8_t n_output; /* No of output ports. used by sflow. */
- ovs_be16 vlan_tci; /* Used by sFlow */
- uint32_t data; /* Data is len for OFPP_CONTROLLER action.
- For sFlow it is port_ifindex. */
+ * Since it is passed to kernel as u64, its size has to be 8 bytes. */
+union user_action_cookie {
+ uint16_t type; /* enum user_action_cookie_type. */
+
+ struct {
+ uint16_t type; /* USER_ACTION_COOKIE_SFLOW. */
+ ovs_be16 vlan_tci; /* Destination VLAN TCI. */
+ uint32_t output; /* SFL_FLOW_SAMPLE_TYPE 'output' value. */
+ } sflow;
+
+ struct {
+ uint16_t type; /* USER_ACTION_COOKIE_SLOW_PATH. */
+ uint16_t unused;
+ uint32_t reason; /* enum slow_path_reason. */
+ } slow_path;
};
-
-BUILD_ASSERT_DECL(sizeof(struct user_action_cookie) == 8);
+BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 8);
size_t odp_put_userspace_action(uint32_t pid,
- const struct user_action_cookie *,
+ const union user_action_cookie *,
struct ofpbuf *odp_actions);
-void commit_odp_actions(const struct flow *, struct flow *base,
- struct ofpbuf *odp_actions);
+/* Reasons why a subfacet might not be fast-pathable. */
+enum slow_path_reason {
+ /* These reasons are mutually exclusive. */
+ SLOW_CFM = 1 << 0, /* CFM packets need per-packet processing. */
+ SLOW_LACP = 1 << 1, /* LACP packets need per-packet processing. */
+ SLOW_STP = 1 << 2, /* STP packets need per-packet processing. */
+ SLOW_IN_BAND = 1 << 3, /* In-band control needs every packet. */
+
+ /* Mutually exclusive with SLOW_CFM, SLOW_LACP, SLOW_STP.
+ * Could possibly appear with SLOW_IN_BAND. */
+ SLOW_CONTROLLER = 1 << 4, /* Packets must go to OpenFlow controller. */
+
+ /* This can appear on its own, or, theoretically at least, along with any
+ * other combination of reasons. */
+ SLOW_MATCH = 1 << 5, /* Datapath can't match specifically enough. */
+};
+
#endif /* odp-util.h */
--- /dev/null
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "ofp-actions.h"
+#include "autopath.h"
+#include "bundle.h"
+#include "byte-order.h"
+#include "compiler.h"
+#include "dynamic-string.h"
+#include "learn.h"
+#include "meta-flow.h"
+#include "multipath.h"
+#include "nx-match.h"
+#include "ofp-util.h"
+#include "ofpbuf.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ofp_actions);
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+\f
+/* Converting OpenFlow 1.0 to ofpacts. */
+
+static enum ofperr
+output_from_openflow10(const struct ofp10_action_output *oao,
+ struct ofpbuf *out)
+{
+ struct ofpact_output *output;
+
+ output = ofpact_put_OUTPUT(out);
+ output->port = ntohs(oao->port);
+ output->max_len = ntohs(oao->max_len);
+
+ return ofputil_check_output_port(output->port, OFPP_MAX);
+}
+
+static enum ofperr
+enqueue_from_openflow10(const struct ofp_action_enqueue *oae,
+ struct ofpbuf *out)
+{
+ struct ofpact_enqueue *enqueue;
+
+ enqueue = ofpact_put_ENQUEUE(out);
+ enqueue->port = ntohs(oae->port);
+ enqueue->queue = ntohl(oae->queue_id);
+ if (enqueue->port >= OFPP_MAX && enqueue->port != OFPP_IN_PORT
+ && enqueue->port != OFPP_LOCAL) {
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
+ }
+ return 0;
+}
+
+static void
+resubmit_from_openflow(const struct nx_action_resubmit *nar,
+ struct ofpbuf *out)
+{
+ struct ofpact_resubmit *resubmit;
+
+ resubmit = ofpact_put_RESUBMIT(out);
+ resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT;
+ resubmit->in_port = ntohs(nar->in_port);
+ resubmit->table_id = 0xff;
+}
+
+static enum ofperr
+resubmit_table_from_openflow(const struct nx_action_resubmit *nar,
+ struct ofpbuf *out)
+{
+ struct ofpact_resubmit *resubmit;
+
+ if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ resubmit = ofpact_put_RESUBMIT(out);
+ resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT_TABLE;
+ resubmit->in_port = ntohs(nar->in_port);
+ resubmit->table_id = nar->table;
+ return 0;
+}
+
+static enum ofperr
+output_reg_from_openflow(const struct nx_action_output_reg *naor,
+ struct ofpbuf *out)
+{
+ struct ofpact_output_reg *output_reg;
+
+ if (!is_all_zeros(naor->zero, sizeof naor->zero)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ output_reg = ofpact_put_OUTPUT_REG(out);
+ output_reg->src.field = mf_from_nxm_header(ntohl(naor->src));
+ output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits);
+ output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits);
+ output_reg->max_len = ntohs(naor->max_len);
+
+ return mf_check_src(&output_reg->src, NULL);
+}
+
+static void
+fin_timeout_from_openflow(const struct nx_action_fin_timeout *naft,
+ struct ofpbuf *out)
+{
+ struct ofpact_fin_timeout *oft;
+
+ oft = ofpact_put_FIN_TIMEOUT(out);
+ oft->fin_idle_timeout = ntohs(naft->fin_idle_timeout);
+ oft->fin_hard_timeout = ntohs(naft->fin_hard_timeout);
+}
+
+static void
+controller_from_openflow(const struct nx_action_controller *nac,
+ struct ofpbuf *out)
+{
+ struct ofpact_controller *oc;
+
+ oc = ofpact_put_CONTROLLER(out);
+ oc->max_len = ntohs(nac->max_len);
+ oc->controller_id = ntohs(nac->controller_id);
+ oc->reason = nac->reason;
+}
+
+static void
+note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out)
+{
+ struct ofpact_note *note;
+ unsigned int length;
+
+ length = ntohs(nan->len) - offsetof(struct nx_action_note, note);
+ note = ofpact_put(out, OFPACT_NOTE,
+ offsetof(struct ofpact_note, data) + length);
+ note->length = length;
+ memcpy(note->data, nan->note, length);
+}
+
+static enum ofperr
+dec_ttl_from_openflow(struct ofpbuf *out)
+{
+ uint16_t id = 0;
+ struct ofpact_cnt_ids *ids;
+ enum ofperr error = 0;
+
+ ids = ofpact_put_DEC_TTL(out);
+ ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
+ ids->n_controllers = 1;
+ ofpbuf_put(out, &id, sizeof id);
+ ids = out->l2;
+ ofpact_update_len(out, &ids->ofpact);
+ return error;
+}
+
+static enum ofperr
+dec_ttl_cnt_ids_from_openflow(const struct nx_action_cnt_ids *nac_ids,
+ struct ofpbuf *out)
+{
+ struct ofpact_cnt_ids *ids;
+ size_t ids_size;
+ int i;
+
+ ids = ofpact_put_DEC_TTL(out);
+ ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS;
+ ids->n_controllers = ntohs(nac_ids->n_controllers);
+ ids_size = ntohs(nac_ids->len) - sizeof *nac_ids;
+
+ if (!is_all_zeros(nac_ids->zeros, sizeof nac_ids->zeros)) {
+ return OFPERR_NXBRC_MUST_BE_ZERO;
+ }
+
+ if (ids_size < ids->n_controllers * sizeof(ovs_be16)) {
+ VLOG_WARN_RL(&rl, "Nicira action dec_ttl_cnt_ids only has %zu bytes "
+ "allocated for controller ids. %zu bytes are required for "
+ "%"PRIu16" controllers.", ids_size,
+ ids->n_controllers * sizeof(ovs_be16), ids->n_controllers);
+ return OFPERR_OFPBAC_BAD_LEN;
+ }
+
+ for (i = 0; i < ids->n_controllers; i++) {
+ uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]);
+ ofpbuf_put(out, &id, sizeof id);
+ }
+
+ ids = out->l2;
+ ofpact_update_len(out, &ids->ofpact);
+
+ return 0;
+}
+
+static enum ofperr
+decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code)
+{
+ const struct nx_action_header *nah = (const struct nx_action_header *) a;
+ uint16_t len = ntohs(a->header.len);
+
+ if (len < sizeof(struct nx_action_header)) {
+ return OFPERR_OFPBAC_BAD_LEN;
+ } else if (a->vendor.vendor != CONSTANT_HTONL(NX_VENDOR_ID)) {
+ return OFPERR_OFPBAC_BAD_VENDOR;
+ }
+
+ switch (nah->subtype) {
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ case CONSTANT_HTONS(ENUM): \
+ if (EXTENSIBLE \
+ ? len >= sizeof(struct STRUCT) \
+ : len == sizeof(struct STRUCT)) { \
+ *code = OFPUTIL_##ENUM; \
+ return 0; \
+ } else { \
+ return OFPERR_OFPBAC_BAD_LEN; \
+ } \
+ NOT_REACHED();
+#include "ofp-util.def"
+
+ case CONSTANT_HTONS(NXAST_SNAT__OBSOLETE):
+ case CONSTANT_HTONS(NXAST_DROP_SPOOFED_ARP__OBSOLETE):
+ default:
+ return OFPERR_OFPBAC_BAD_TYPE;
+ }
+}
+
+/* Parses 'a' to determine its type. On success stores the correct type into
+ * '*code' and returns 0. On failure returns an OFPERR_* error code and
+ * '*code' is indeterminate.
+ *
+ * The caller must have already verified that 'a''s length is potentially
+ * correct (that is, a->header.len is nonzero and a multiple of sizeof(union
+ * ofp_action) and no longer than the amount of space allocated to 'a').
+ *
+ * This function verifies that 'a''s length is correct for the type of action
+ * that it represents. */
+static enum ofperr
+decode_openflow10_action(const union ofp_action *a,
+ enum ofputil_action_code *code)
+{
+ switch (a->type) {
+ case CONSTANT_HTONS(OFPAT10_VENDOR):
+ return decode_nxast_action(a, code);
+
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
+ case CONSTANT_HTONS(ENUM): \
+ if (a->header.len == htons(sizeof(struct STRUCT))) { \
+ *code = OFPUTIL_##ENUM; \
+ return 0; \
+ } else { \
+ return OFPERR_OFPBAC_BAD_LEN; \
+ } \
+ break;
+#include "ofp-util.def"
+
+ default:
+ return OFPERR_OFPBAC_BAD_TYPE;
+ }
+}
+
+static enum ofperr
+ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
+ struct ofpbuf *out)
+{
+ const struct nx_action_resubmit *nar;
+ const struct nx_action_set_tunnel *nast;
+ const struct nx_action_set_queue *nasq;
+ const struct nx_action_note *nan;
+ const struct nx_action_set_tunnel64 *nast64;
+ struct ofpact_tunnel *tunnel;
+ enum ofperr error = 0;
+
+ switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+ NOT_REACHED();
+
+ case OFPUTIL_NXAST_RESUBMIT:
+ resubmit_from_openflow((const struct nx_action_resubmit *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_SET_TUNNEL:
+ nast = (const struct nx_action_set_tunnel *) a;
+ tunnel = ofpact_put_SET_TUNNEL(out);
+ tunnel->ofpact.compat = code;
+ tunnel->tun_id = ntohl(nast->tun_id);
+ break;
+
+ case OFPUTIL_NXAST_SET_QUEUE:
+ nasq = (const struct nx_action_set_queue *) a;
+ ofpact_put_SET_QUEUE(out)->queue_id = ntohl(nasq->queue_id);
+ break;
+
+ case OFPUTIL_NXAST_POP_QUEUE:
+ ofpact_put_POP_QUEUE(out);
+ break;
+
+ case OFPUTIL_NXAST_REG_MOVE:
+ error = nxm_reg_move_from_openflow(
+ (const struct nx_action_reg_move *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_REG_LOAD:
+ error = nxm_reg_load_from_openflow(
+ (const struct nx_action_reg_load *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_NOTE:
+ nan = (const struct nx_action_note *) a;
+ note_from_openflow(nan, out);
+ break;
+
+ case OFPUTIL_NXAST_SET_TUNNEL64:
+ nast64 = (const struct nx_action_set_tunnel64 *) a;
+ tunnel = ofpact_put_SET_TUNNEL(out);
+ tunnel->ofpact.compat = code;
+ tunnel->tun_id = ntohll(nast64->tun_id);
+ break;
+
+ case OFPUTIL_NXAST_MULTIPATH:
+ error = multipath_from_openflow((const struct nx_action_multipath *) a,
+ ofpact_put_MULTIPATH(out));
+ break;
+
+ case OFPUTIL_NXAST_AUTOPATH__DEPRECATED:
+ error = autopath_from_openflow((const struct nx_action_autopath *) a,
+ ofpact_put_AUTOPATH(out));
+ break;
+
+ case OFPUTIL_NXAST_BUNDLE:
+ case OFPUTIL_NXAST_BUNDLE_LOAD:
+ error = bundle_from_openflow((const struct nx_action_bundle *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_OUTPUT_REG:
+ error = output_reg_from_openflow(
+ (const struct nx_action_output_reg *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_RESUBMIT_TABLE:
+ nar = (const struct nx_action_resubmit *) a;
+ error = resubmit_table_from_openflow(nar, out);
+ break;
+
+ case OFPUTIL_NXAST_LEARN:
+ error = learn_from_openflow((const struct nx_action_learn *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_EXIT:
+ ofpact_put_EXIT(out);
+ break;
+
+ case OFPUTIL_NXAST_DEC_TTL:
+ error = dec_ttl_from_openflow(out);
+ break;
+
+ case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
+ error = dec_ttl_cnt_ids_from_openflow(
+ (const struct nx_action_cnt_ids *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_FIN_TIMEOUT:
+ fin_timeout_from_openflow(
+ (const struct nx_action_fin_timeout *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_CONTROLLER:
+ controller_from_openflow((const struct nx_action_controller *) a, out);
+ break;
+ }
+
+ return error;
+}
+
+static enum ofperr
+ofpact_from_openflow10(const union ofp_action *a, struct ofpbuf *out)
+{
+ enum ofputil_action_code code;
+ enum ofperr error;
+
+ error = decode_openflow10_action(a, &code);
+ if (error) {
+ return error;
+ }
+
+ switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+ NOT_REACHED();
+
+ case OFPUTIL_OFPAT10_OUTPUT:
+ return output_from_openflow10(&a->output10, out);
+
+ case OFPUTIL_OFPAT10_SET_VLAN_VID:
+ if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_VLAN_PCP:
+ if (a->vlan_pcp.vlan_pcp & ~7) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
+ break;
+
+ case OFPUTIL_OFPAT10_STRIP_VLAN:
+ ofpact_put_STRIP_VLAN(out);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_DL_SRC:
+ memcpy(ofpact_put_SET_ETH_SRC(out)->mac,
+ ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_DL_DST:
+ memcpy(ofpact_put_SET_ETH_DST(out)->mac,
+ ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_NW_SRC:
+ ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT10_SET_NW_DST:
+ ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT10_SET_NW_TOS:
+ if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_IPV4_DSCP(out)->dscp = a->nw_tos.nw_tos;
+ break;
+
+ case OFPUTIL_OFPAT10_SET_TP_SRC:
+ ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port);
+ break;
+
+ case OFPUTIL_OFPAT10_SET_TP_DST:
+ ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port);
+
+ break;
+
+ case OFPUTIL_OFPAT10_ENQUEUE:
+ error = enqueue_from_openflow10((const struct ofp_action_enqueue *) a,
+ out);
+ break;
+
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+ return ofpact_from_nxast(a, code, out);
+ }
+
+ return error;
+}
+
+static inline union ofp_action *
+action_next(const union ofp_action *a)
+{
+ return ((union ofp_action *) (void *)
+ ((uint8_t *) a + ntohs(a->header.len)));
+}
+
+static inline bool
+action_is_valid(const union ofp_action *a, size_t n_actions)
+{
+ uint16_t len = ntohs(a->header.len);
+ return (!(len % OFP_ACTION_ALIGN)
+ && len >= sizeof *a
+ && len / sizeof *a <= n_actions);
+}
+
+/* This macro is careful to check for actions with bad lengths. */
+#define ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS) \
+ for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS); \
+ (LEFT) > 0 && action_is_valid(ITER, LEFT); \
+ ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
+ (ITER) = action_next(ITER)))
+
+static void
+log_bad_action(const union ofp_action *actions, size_t n_actions, size_t ofs,
+ enum ofperr error)
+{
+ if (!VLOG_DROP_WARN(&rl)) {
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_hex_dump(&s, actions, n_actions * sizeof *actions, 0, false);
+ VLOG_WARN("bad action at offset %#zx (%s):\n%s",
+ ofs * sizeof *actions, ofperr_get_name(error), ds_cstr(&s));
+ ds_destroy(&s);
+ }
+}
+
+static enum ofperr
+ofpacts_from_openflow(const union ofp_action *in, size_t n_in,
+ struct ofpbuf *out,
+ enum ofperr (*ofpact_from_openflow)(
+ const union ofp_action *a, struct ofpbuf *out))
+{
+ const union ofp_action *a;
+ size_t left;
+
+ ACTION_FOR_EACH (a, left, in, n_in) {
+ enum ofperr error = ofpact_from_openflow(a, out);
+ if (error) {
+ log_bad_action(in, n_in, a - in, error);
+ return error;
+ }
+ }
+ if (left) {
+ enum ofperr error = OFPERR_OFPBAC_BAD_LEN;
+ log_bad_action(in, n_in, n_in - left, error);
+ return error;
+ }
+
+ ofpact_pad(out);
+ return 0;
+}
+
+static enum ofperr
+ofpacts_from_openflow10(const union ofp_action *in, size_t n_in,
+ struct ofpbuf *out)
+{
+ return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow10);
+}
+
+static enum ofperr
+ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len,
+ struct ofpbuf *ofpacts,
+ enum ofperr (*translate)(const union ofp_action *actions,
+ size_t n_actions,
+ struct ofpbuf *ofpacts))
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const union ofp_action *actions;
+ enum ofperr error;
+
+ ofpbuf_clear(ofpacts);
+
+ if (actions_len % OFP_ACTION_ALIGN != 0) {
+ VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a "
+ "multiple of %d", actions_len, OFP_ACTION_ALIGN);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ actions = ofpbuf_try_pull(openflow, actions_len);
+ if (actions == NULL) {
+ VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds "
+ "remaining message length (%zu)",
+ actions_len, openflow->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ error = translate(actions, actions_len / OFP_ACTION_ALIGN, ofpacts);
+ if (error) {
+ ofpbuf_clear(ofpacts);
+ }
+ return error;
+}
+
+/* Attempts to convert 'actions_len' bytes of OpenFlow 1.0 actions from the
+ * front of 'openflow' into ofpacts. On success, replaces any existing content
+ * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
+ * Returns 0 if successful, otherwise an OpenFlow error.
+ *
+ * The parsed actions are valid generically, but they may not be valid in a
+ * specific context. For example, port numbers up to OFPP_MAX are valid
+ * generically, but specific datapaths may only support port numbers in a
+ * smaller range. Use ofpacts_check() to additional check whether actions are
+ * valid in a specific context. */
+enum ofperr
+ofpacts_pull_openflow10(struct ofpbuf *openflow, unsigned int actions_len,
+ struct ofpbuf *ofpacts)
+{
+ return ofpacts_pull_actions(openflow, actions_len, ofpacts,
+ ofpacts_from_openflow10);
+}
+\f
+/* OpenFlow 1.1 actions. */
+
+/* Parses 'a' to determine its type. On success stores the correct type into
+ * '*code' and returns 0. On failure returns an OFPERR_* error code and
+ * '*code' is indeterminate.
+ *
+ * The caller must have already verified that 'a''s length is potentially
+ * correct (that is, a->header.len is nonzero and a multiple of sizeof(union
+ * ofp_action) and no longer than the amount of space allocated to 'a').
+ *
+ * This function verifies that 'a''s length is correct for the type of action
+ * that it represents. */
+static enum ofperr
+decode_openflow11_action(const union ofp_action *a,
+ enum ofputil_action_code *code)
+{
+ switch (a->type) {
+ case CONSTANT_HTONS(OFPAT11_EXPERIMENTER):
+ return decode_nxast_action(a, code);
+
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) \
+ case CONSTANT_HTONS(ENUM): \
+ if (a->header.len == htons(sizeof(struct STRUCT))) { \
+ *code = OFPUTIL_##ENUM; \
+ return 0; \
+ } else { \
+ return OFPERR_OFPBAC_BAD_LEN; \
+ } \
+ break;
+#include "ofp-util.def"
+
+ default:
+ return OFPERR_OFPBAC_BAD_TYPE;
+ }
+}
+
+static enum ofperr
+output_from_openflow11(const struct ofp11_action_output *oao,
+ struct ofpbuf *out)
+{
+ struct ofpact_output *output;
+ enum ofperr error;
+
+ output = ofpact_put_OUTPUT(out);
+ output->max_len = ntohs(oao->max_len);
+
+ error = ofputil_port_from_ofp11(oao->port, &output->port);
+ if (error) {
+ return error;
+ }
+
+ return ofputil_check_output_port(output->port, OFPP_MAX);
+}
+
+static enum ofperr
+ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
+{
+ enum ofputil_action_code code;
+ enum ofperr error;
+
+ error = decode_openflow11_action(a, &code);
+ if (error) {
+ return error;
+ }
+
+ switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+ NOT_REACHED();
+
+ case OFPUTIL_OFPAT11_OUTPUT:
+ return output_from_openflow11((const struct ofp11_action_output *) a,
+ out);
+
+ case OFPUTIL_OFPAT11_SET_VLAN_VID:
+ if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_VLAN_PCP:
+ if (a->vlan_pcp.vlan_pcp & ~7) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_DL_SRC:
+ memcpy(ofpact_put_SET_ETH_SRC(out)->mac,
+ ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_DL_DST:
+ memcpy(ofpact_put_SET_ETH_DST(out)->mac,
+ ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_SRC:
+ ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_DST:
+ ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_TOS:
+ if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_IPV4_DSCP(out)->dscp = a->nw_tos.nw_tos;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_TP_SRC:
+ ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_TP_DST:
+ ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port);
+ break;
+
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+ return ofpact_from_nxast(a, code, out);
+ }
+
+ return error;
+}
+
+static enum ofperr
+ofpacts_from_openflow11(const union ofp_action *in, size_t n_in,
+ struct ofpbuf *out)
+{
+ return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow11);
+}
+\f
+/* OpenFlow 1.1 instructions. */
+
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ static inline const struct STRUCT * \
+ instruction_get_##ENUM(const struct ofp11_instruction *inst)\
+ { \
+ assert(inst->type == htons(ENUM)); \
+ return (struct STRUCT *)inst; \
+ } \
+ \
+ static inline void \
+ instruction_init_##ENUM(struct STRUCT *s) \
+ { \
+ memset(s, 0, sizeof *s); \
+ s->type = htons(ENUM); \
+ s->len = htons(sizeof *s); \
+ } \
+ \
+ static inline struct STRUCT * \
+ instruction_put_##ENUM(struct ofpbuf *buf) \
+ { \
+ struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
+ instruction_init_##ENUM(s); \
+ return s; \
+ }
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+
+struct instruction_type_info {
+ enum ovs_instruction_type type;
+ const char *name;
+};
+
+static const struct instruction_type_info inst_info[] = {
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) {OVSINST_##ENUM, NAME},
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+};
+
+const char *
+ofpact_instruction_name_from_type(enum ovs_instruction_type type)
+{
+ return inst_info[type].name;
+}
+
+int
+ofpact_instruction_type_from_name(const char *name)
+{
+ const struct instruction_type_info *p;
+ for (p = inst_info; p < &inst_info[ARRAY_SIZE(inst_info)]; p++) {
+ if (!strcasecmp(name, p->name)) {
+ return p->type;
+ }
+ }
+ return -1;
+}
+
+static inline struct ofp11_instruction *
+instruction_next(const struct ofp11_instruction *inst)
+{
+ return ((struct ofp11_instruction *) (void *)
+ ((uint8_t *) inst + ntohs(inst->len)));
+}
+
+static inline bool
+instruction_is_valid(const struct ofp11_instruction *inst,
+ size_t n_instructions)
+{
+ uint16_t len = ntohs(inst->len);
+ return (!(len % OFP11_INSTRUCTION_ALIGN)
+ && len >= sizeof *inst
+ && len / sizeof *inst <= n_instructions);
+}
+
+/* This macro is careful to check for instructions with bad lengths. */
+#define INSTRUCTION_FOR_EACH(ITER, LEFT, INSTRUCTIONS, N_INSTRUCTIONS) \
+ for ((ITER) = (INSTRUCTIONS), (LEFT) = (N_INSTRUCTIONS); \
+ (LEFT) > 0 && instruction_is_valid(ITER, LEFT); \
+ ((LEFT) -= (ntohs((ITER)->len) \
+ / sizeof(struct ofp11_instruction)), \
+ (ITER) = instruction_next(ITER)))
+
+static enum ofperr
+decode_openflow11_instruction(const struct ofp11_instruction *inst,
+ enum ovs_instruction_type *type)
+{
+ uint16_t len = ntohs(inst->len);
+
+ switch (inst->type) {
+ case CONSTANT_HTONS(OFPIT11_EXPERIMENTER):
+ return OFPERR_OFPBIC_BAD_EXPERIMENTER;
+
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ case CONSTANT_HTONS(ENUM): \
+ if (EXTENSIBLE \
+ ? len >= sizeof(struct STRUCT) \
+ : len == sizeof(struct STRUCT)) { \
+ *type = OVSINST_##ENUM; \
+ return 0; \
+ } else { \
+ return OFPERR_OFPBIC_BAD_LEN; \
+ }
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+
+ default:
+ return OFPERR_OFPBIC_UNKNOWN_INST;
+ }
+}
+
+static enum ofperr
+decode_openflow11_instructions(const struct ofp11_instruction insts[],
+ size_t n_insts,
+ const struct ofp11_instruction *out[])
+{
+ const struct ofp11_instruction *inst;
+ size_t left;
+
+ memset(out, 0, N_OVS_INSTRUCTIONS * sizeof *out);
+ INSTRUCTION_FOR_EACH (inst, left, insts, n_insts) {
+ enum ovs_instruction_type type;
+ enum ofperr error;
+
+ error = decode_openflow11_instruction(inst, &type);
+ if (error) {
+ return error;
+ }
+
+ if (out[type]) {
+ return OFPERR_NXBIC_DUP_TYPE;
+ }
+ out[type] = inst;
+ }
+
+ if (left) {
+ VLOG_WARN_RL(&rl, "bad instruction format at offset %zu",
+ (n_insts - left) * sizeof *inst);
+ return OFPERR_OFPBIC_BAD_LEN;
+ }
+ return 0;
+}
+
+static void
+get_actions_from_instruction(const struct ofp11_instruction *inst,
+ const union ofp_action **actions,
+ size_t *n_actions)
+{
+ *actions = (const union ofp_action *) (inst + 1);
+ *n_actions = (ntohs(inst->len) - sizeof *inst) / OFP11_INSTRUCTION_ALIGN;
+}
+
+/* Attempts to convert 'actions_len' bytes of OpenFlow 1.1 actions from the
+ * front of 'openflow' into ofpacts. On success, replaces any existing content
+ * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
+ * Returns 0 if successful, otherwise an OpenFlow error.
+ *
+ * In most places in OpenFlow 1.1 and 1.2, actions appear encapsulated in
+ * instructions, so you should call ofpacts_pull_openflow11_instructions()
+ * instead of this function.
+ *
+ * The parsed actions are valid generically, but they may not be valid in a
+ * specific context. For example, port numbers up to OFPP_MAX are valid
+ * generically, but specific datapaths may only support port numbers in a
+ * smaller range. Use ofpacts_check() to additional check whether actions are
+ * valid in a specific context. */
+enum ofperr
+ofpacts_pull_openflow11_actions(struct ofpbuf *openflow,
+ unsigned int actions_len,
+ struct ofpbuf *ofpacts)
+{
+ return ofpacts_pull_actions(openflow, actions_len, ofpacts,
+ ofpacts_from_openflow11);
+}
+
+enum ofperr
+ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
+ unsigned int instructions_len,
+ struct ofpbuf *ofpacts)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ const struct ofp11_instruction *instructions;
+ const struct ofp11_instruction *insts[N_OVS_INSTRUCTIONS];
+ enum ofperr error;
+
+ ofpbuf_clear(ofpacts);
+
+ if (instructions_len % OFP11_INSTRUCTION_ALIGN != 0) {
+ VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u is not a "
+ "multiple of %d",
+ instructions_len, OFP11_INSTRUCTION_ALIGN);
+ error = OFPERR_OFPBIC_BAD_LEN;
+ goto exit;
+ }
+
+ instructions = ofpbuf_try_pull(openflow, instructions_len);
+ if (instructions == NULL) {
+ VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u exceeds "
+ "remaining message length (%zu)",
+ instructions_len, openflow->size);
+ error = OFPERR_OFPBIC_BAD_LEN;
+ goto exit;
+ }
+
+ error = decode_openflow11_instructions(
+ instructions, instructions_len / OFP11_INSTRUCTION_ALIGN,
+ insts);
+ if (error) {
+ goto exit;
+ }
+
+ if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) {
+ const union ofp_action *actions;
+ size_t n_actions;
+
+ get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS],
+ &actions, &n_actions);
+ error = ofpacts_from_openflow11(actions, n_actions, ofpacts);
+ if (error) {
+ goto exit;
+ }
+ }
+
+ if (insts[OVSINST_OFPIT11_GOTO_TABLE] ||
+ insts[OVSINST_OFPIT11_WRITE_METADATA] ||
+ insts[OVSINST_OFPIT11_WRITE_ACTIONS] ||
+ insts[OVSINST_OFPIT11_CLEAR_ACTIONS]) {
+ error = OFPERR_OFPBIC_UNSUP_INST;
+ goto exit;
+ }
+
+exit:
+ if (error) {
+ ofpbuf_clear(ofpacts);
+ }
+ return error;
+}
+\f
+static enum ofperr
+ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports)
+{
+ const struct ofpact_enqueue *enqueue;
+
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ return ofputil_check_output_port(ofpact_get_OUTPUT(a)->port,
+ max_ports);
+
+ case OFPACT_CONTROLLER:
+ return 0;
+
+ case OFPACT_ENQUEUE:
+ enqueue = ofpact_get_ENQUEUE(a);
+ if (enqueue->port >= max_ports && enqueue->port != OFPP_IN_PORT
+ && enqueue->port != OFPP_LOCAL) {
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
+ }
+ return 0;
+
+ case OFPACT_OUTPUT_REG:
+ return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow);
+
+ case OFPACT_BUNDLE:
+ return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow);
+
+ case OFPACT_SET_VLAN_VID:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IPV4_DSCP:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_L4_DST_PORT:
+ return 0;
+
+ case OFPACT_REG_MOVE:
+ return nxm_reg_move_check(ofpact_get_REG_MOVE(a), flow);
+
+ case OFPACT_REG_LOAD:
+ return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow);
+
+ case OFPACT_DEC_TTL:
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_POP_QUEUE:
+ case OFPACT_FIN_TIMEOUT:
+ case OFPACT_RESUBMIT:
+ return 0;
+
+ case OFPACT_LEARN:
+ return learn_check(ofpact_get_LEARN(a), flow);
+
+ case OFPACT_MULTIPATH:
+ return multipath_check(ofpact_get_MULTIPATH(a), flow);
+
+ case OFPACT_AUTOPATH:
+ return autopath_check(ofpact_get_AUTOPATH(a), flow);
+
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ return 0;
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are
+ * appropriate for a packet with the prerequisites satisfied by 'flow' in a
+ * switch with no more than 'max_ports' ports. */
+enum ofperr
+ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len,
+ const struct flow *flow, int max_ports)
+{
+ const struct ofpact *a;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ enum ofperr error = ofpact_check__(a, flow, max_ports);
+ if (error) {
+ return error;
+ }
+ }
+
+ return 0;
+}
+\f
+/* Converting ofpacts to Nicira OpenFlow extensions. */
+
+static void
+ofpact_output_reg_to_nxast(const struct ofpact_output_reg *output_reg,
+ struct ofpbuf *out)
+{
+ struct nx_action_output_reg *naor = ofputil_put_NXAST_OUTPUT_REG(out);
+
+ naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs,
+ output_reg->src.n_bits);
+ naor->src = htonl(output_reg->src.field->nxm_header);
+ naor->max_len = htons(output_reg->max_len);
+}
+
+static void
+ofpact_resubmit_to_nxast(const struct ofpact_resubmit *resubmit,
+ struct ofpbuf *out)
+{
+ struct nx_action_resubmit *nar;
+
+ if (resubmit->table_id == 0xff
+ && resubmit->ofpact.compat != OFPUTIL_NXAST_RESUBMIT_TABLE) {
+ nar = ofputil_put_NXAST_RESUBMIT(out);
+ } else {
+ nar = ofputil_put_NXAST_RESUBMIT_TABLE(out);
+ nar->table = resubmit->table_id;
+ }
+ nar->in_port = htons(resubmit->in_port);
+}
+
+static void
+ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel,
+ struct ofpbuf *out)
+{
+ uint64_t tun_id = tunnel->tun_id;
+
+ if (tun_id <= UINT32_MAX
+ && tunnel->ofpact.compat != OFPUTIL_NXAST_SET_TUNNEL64) {
+ ofputil_put_NXAST_SET_TUNNEL(out)->tun_id = htonl(tun_id);
+ } else {
+ ofputil_put_NXAST_SET_TUNNEL64(out)->tun_id = htonll(tun_id);
+ }
+}
+
+static void
+ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out)
+{
+ size_t start_ofs = out->size;
+ struct nx_action_note *nan;
+ unsigned int remainder;
+ unsigned int len;
+
+ nan = ofputil_put_NXAST_NOTE(out);
+ out->size -= sizeof nan->note;
+
+ ofpbuf_put(out, note->data, note->length);
+
+ len = out->size - start_ofs;
+ remainder = len % OFP_ACTION_ALIGN;
+ if (remainder) {
+ ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder);
+ }
+ nan = (struct nx_action_note *)((char *)out->data + start_ofs);
+ nan->len = htons(out->size - start_ofs);
+}
+
+static void
+ofpact_controller_to_nxast(const struct ofpact_controller *oc,
+ struct ofpbuf *out)
+{
+ struct nx_action_controller *nac;
+
+ nac = ofputil_put_NXAST_CONTROLLER(out);
+ nac->max_len = htons(oc->max_len);
+ nac->controller_id = htons(oc->controller_id);
+ nac->reason = oc->reason;
+}
+
+static void
+ofpact_dec_ttl_to_nxast(const struct ofpact_cnt_ids *oc_ids,
+ struct ofpbuf *out)
+{
+ if (oc_ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL) {
+ ofputil_put_NXAST_DEC_TTL(out);
+ } else {
+ struct nx_action_cnt_ids *nac_ids =
+ ofputil_put_NXAST_DEC_TTL_CNT_IDS(out);
+ int ids_len = ROUND_UP(2 * oc_ids->n_controllers, OFP_ACTION_ALIGN);
+ ovs_be16 *ids;
+ size_t i;
+
+ nac_ids->len = htons(ntohs(nac_ids->len) + ids_len);
+ nac_ids->n_controllers = htons(oc_ids->n_controllers);
+
+ ids = ofpbuf_put_zeros(out, ids_len);
+ for (i = 0; i < oc_ids->n_controllers; i++) {
+ ids[i] = htons(oc_ids->cnt_ids[i]);
+ }
+ }
+}
+
+static void
+ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout,
+ struct ofpbuf *out)
+{
+ struct nx_action_fin_timeout *naft = ofputil_put_NXAST_FIN_TIMEOUT(out);
+ naft->fin_idle_timeout = htons(fin_timeout->fin_idle_timeout);
+ naft->fin_hard_timeout = htons(fin_timeout->fin_hard_timeout);
+}
+
+static void
+ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
+{
+ switch (a->type) {
+ case OFPACT_CONTROLLER:
+ ofpact_controller_to_nxast(ofpact_get_CONTROLLER(a), out);
+ break;
+
+ case OFPACT_OUTPUT_REG:
+ ofpact_output_reg_to_nxast(ofpact_get_OUTPUT_REG(a), out);
+ break;
+
+ case OFPACT_BUNDLE:
+ bundle_to_nxast(ofpact_get_BUNDLE(a), out);
+ break;
+
+ case OFPACT_REG_MOVE:
+ nxm_reg_move_to_nxast(ofpact_get_REG_MOVE(a), out);
+ break;
+
+ case OFPACT_REG_LOAD:
+ nxm_reg_load_to_nxast(ofpact_get_REG_LOAD(a), out);
+ break;
+
+ case OFPACT_DEC_TTL:
+ ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out);
+ break;
+
+ case OFPACT_SET_TUNNEL:
+ ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out);
+ break;
+
+ case OFPACT_SET_QUEUE:
+ ofputil_put_NXAST_SET_QUEUE(out)->queue_id
+ = htonl(ofpact_get_SET_QUEUE(a)->queue_id);
+ break;
+
+ case OFPACT_POP_QUEUE:
+ ofputil_put_NXAST_POP_QUEUE(out);
+ break;
+
+ case OFPACT_FIN_TIMEOUT:
+ ofpact_fin_timeout_to_nxast(ofpact_get_FIN_TIMEOUT(a), out);
+ break;
+
+ case OFPACT_RESUBMIT:
+ ofpact_resubmit_to_nxast(ofpact_get_RESUBMIT(a), out);
+ break;
+
+ case OFPACT_LEARN:
+ learn_to_nxast(ofpact_get_LEARN(a), out);
+ break;
+
+ case OFPACT_MULTIPATH:
+ multipath_to_nxast(ofpact_get_MULTIPATH(a), out);
+ break;
+
+ case OFPACT_AUTOPATH:
+ autopath_to_nxast(ofpact_get_AUTOPATH(a), out);
+ break;
+
+ case OFPACT_NOTE:
+ ofpact_note_to_nxast(ofpact_get_NOTE(a), out);
+ break;
+
+ case OFPACT_EXIT:
+ ofputil_put_NXAST_EXIT(out);
+ break;
+
+ case OFPACT_OUTPUT:
+ case OFPACT_ENQUEUE:
+ case OFPACT_SET_VLAN_VID:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IPV4_DSCP:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_L4_DST_PORT:
+ NOT_REACHED();
+ }
+}
+\f
+/* Converting ofpacts to OpenFlow 1.0. */
+
+static void
+ofpact_output_to_openflow10(const struct ofpact_output *output,
+ struct ofpbuf *out)
+{
+ struct ofp10_action_output *oao;
+
+ oao = ofputil_put_OFPAT10_OUTPUT(out);
+ oao->port = htons(output->port);
+ oao->max_len = htons(output->max_len);
+}
+
+static void
+ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue,
+ struct ofpbuf *out)
+{
+ struct ofp_action_enqueue *oae;
+
+ oae = ofputil_put_OFPAT10_ENQUEUE(out);
+ oae->port = htons(enqueue->port);
+ oae->queue_id = htonl(enqueue->queue);
+}
+
+static void
+ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
+{
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ ofpact_output_to_openflow10(ofpact_get_OUTPUT(a), out);
+ break;
+
+ case OFPACT_ENQUEUE:
+ ofpact_enqueue_to_openflow10(ofpact_get_ENQUEUE(a), out);
+ break;
+
+ case OFPACT_SET_VLAN_VID:
+ ofputil_put_OFPAT10_SET_VLAN_VID(out)->vlan_vid
+ = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid);
+ break;
+
+ case OFPACT_SET_VLAN_PCP:
+ ofputil_put_OFPAT10_SET_VLAN_PCP(out)->vlan_pcp
+ = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp;
+ break;
+
+ case OFPACT_STRIP_VLAN:
+ ofputil_put_OFPAT10_STRIP_VLAN(out);
+ break;
+
+ case OFPACT_SET_ETH_SRC:
+ memcpy(ofputil_put_OFPAT10_SET_DL_SRC(out)->dl_addr,
+ ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
+ break;
+
+ case OFPACT_SET_ETH_DST:
+ memcpy(ofputil_put_OFPAT10_SET_DL_DST(out)->dl_addr,
+ ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN);
+ break;
+
+ case OFPACT_SET_IPV4_SRC:
+ ofputil_put_OFPAT10_SET_NW_SRC(out)->nw_addr
+ = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+ break;
+
+ case OFPACT_SET_IPV4_DST:
+ ofputil_put_OFPAT10_SET_NW_DST(out)->nw_addr
+ = ofpact_get_SET_IPV4_DST(a)->ipv4;
+ break;
+
+ case OFPACT_SET_IPV4_DSCP:
+ ofputil_put_OFPAT10_SET_NW_TOS(out)->nw_tos
+ = ofpact_get_SET_IPV4_DSCP(a)->dscp;
+ break;
+
+ case OFPACT_SET_L4_SRC_PORT:
+ ofputil_put_OFPAT10_SET_TP_SRC(out)->tp_port
+ = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
+ break;
+
+ case OFPACT_SET_L4_DST_PORT:
+ ofputil_put_OFPAT10_SET_TP_DST(out)->tp_port
+ = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
+ break;
+
+ case OFPACT_CONTROLLER:
+ case OFPACT_OUTPUT_REG:
+ case OFPACT_BUNDLE:
+ case OFPACT_REG_MOVE:
+ case OFPACT_REG_LOAD:
+ case OFPACT_DEC_TTL:
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_POP_QUEUE:
+ case OFPACT_FIN_TIMEOUT:
+ case OFPACT_RESUBMIT:
+ case OFPACT_LEARN:
+ case OFPACT_MULTIPATH:
+ case OFPACT_AUTOPATH:
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ ofpact_to_nxast(a, out);
+ break;
+ }
+}
+
+/* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow 1.0
+ * actions in 'openflow', appending the actions to any existing data in
+ * 'openflow'. */
+void
+ofpacts_put_openflow10(const struct ofpact ofpacts[], size_t ofpacts_len,
+ struct ofpbuf *openflow)
+{
+ const struct ofpact *a;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ ofpact_to_openflow10(a, openflow);
+ }
+}
+\f
+/* Converting ofpacts to OpenFlow 1.1. */
+
+static void
+ofpact_output_to_openflow11(const struct ofpact_output *output,
+ struct ofpbuf *out)
+{
+ struct ofp11_action_output *oao;
+
+ oao = ofputil_put_OFPAT11_OUTPUT(out);
+ oao->port = ofputil_port_to_ofp11(output->port);
+ oao->max_len = htons(output->max_len);
+}
+
+static void
+ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
+{
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ return ofpact_output_to_openflow11(ofpact_get_OUTPUT(a), out);
+
+ case OFPACT_ENQUEUE:
+ /* XXX */
+ break;
+
+ case OFPACT_SET_VLAN_VID:
+ ofputil_put_OFPAT11_SET_VLAN_VID(out)->vlan_vid
+ = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid);
+ break;
+
+ case OFPACT_SET_VLAN_PCP:
+ ofputil_put_OFPAT11_SET_VLAN_PCP(out)->vlan_pcp
+ = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp;
+ break;
+
+ case OFPACT_STRIP_VLAN:
+ /* XXX */
+ break;
+
+ case OFPACT_SET_ETH_SRC:
+ memcpy(ofputil_put_OFPAT11_SET_DL_SRC(out)->dl_addr,
+ ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
+ break;
+
+ case OFPACT_SET_ETH_DST:
+ memcpy(ofputil_put_OFPAT11_SET_DL_DST(out)->dl_addr,
+ ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN);
+ break;
+
+ case OFPACT_SET_IPV4_SRC:
+ ofputil_put_OFPAT11_SET_NW_SRC(out)->nw_addr
+ = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+ break;
+
+ case OFPACT_SET_IPV4_DST:
+ ofputil_put_OFPAT11_SET_NW_DST(out)->nw_addr
+ = ofpact_get_SET_IPV4_DST(a)->ipv4;
+ break;
+
+ case OFPACT_SET_IPV4_DSCP:
+ ofputil_put_OFPAT11_SET_NW_TOS(out)->nw_tos
+ = ofpact_get_SET_IPV4_DSCP(a)->dscp;
+ break;
+
+ case OFPACT_SET_L4_SRC_PORT:
+ ofputil_put_OFPAT11_SET_TP_SRC(out)->tp_port
+ = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
+ break;
+
+ case OFPACT_SET_L4_DST_PORT:
+ ofputil_put_OFPAT11_SET_TP_DST(out)->tp_port
+ = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
+ break;
+
+ case OFPACT_CONTROLLER:
+ case OFPACT_OUTPUT_REG:
+ case OFPACT_BUNDLE:
+ case OFPACT_REG_MOVE:
+ case OFPACT_REG_LOAD:
+ case OFPACT_DEC_TTL:
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_POP_QUEUE:
+ case OFPACT_FIN_TIMEOUT:
+ case OFPACT_RESUBMIT:
+ case OFPACT_LEARN:
+ case OFPACT_MULTIPATH:
+ case OFPACT_AUTOPATH:
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ ofpact_to_nxast(a, out);
+ break;
+ }
+}
+
+/* Converts the ofpacts in 'ofpacts' (terminated by OFPACT_END) into OpenFlow
+ * 1.1 actions in 'openflow', appending the actions to any existing data in
+ * 'openflow'. */
+size_t
+ofpacts_put_openflow11_actions(const struct ofpact ofpacts[],
+ size_t ofpacts_len, struct ofpbuf *openflow)
+{
+ const struct ofpact *a;
+ size_t start_size = openflow->size;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ ofpact_to_openflow11(a, openflow);
+ }
+
+ return openflow->size - start_size;
+}
+
+void
+ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
+ size_t ofpacts_len,
+ struct ofpbuf *openflow)
+{
+ struct ofp11_instruction_actions *oia;
+ size_t ofs;
+
+ /* Put an OFPIT11_APPLY_ACTIONS instruction and fill it in. */
+ ofs = openflow->size;
+ instruction_put_OFPIT11_APPLY_ACTIONS(openflow);
+ ofpacts_put_openflow11_actions(ofpacts, ofpacts_len, openflow);
+
+ /* Update the instruction's length (or, if it's empty, delete it). */
+ oia = ofpbuf_at_assert(openflow, ofs, sizeof *oia);
+ if (openflow->size > ofs + sizeof *oia) {
+ oia->len = htons(openflow->size - ofs);
+ } else {
+ openflow->size = ofs;
+ }
+}
+\f
+/* Returns true if 'action' outputs to 'port', false otherwise. */
+static bool
+ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
+{
+ switch (ofpact->type) {
+ case OFPACT_OUTPUT:
+ return ofpact_get_OUTPUT(ofpact)->port == port;
+ case OFPACT_ENQUEUE:
+ return ofpact_get_ENQUEUE(ofpact)->port == port;
+ case OFPACT_CONTROLLER:
+ return port == OFPP_CONTROLLER;
+
+ case OFPACT_OUTPUT_REG:
+ case OFPACT_BUNDLE:
+ case OFPACT_SET_VLAN_VID:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IPV4_DSCP:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_L4_DST_PORT:
+ case OFPACT_REG_MOVE:
+ case OFPACT_REG_LOAD:
+ case OFPACT_DEC_TTL:
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_POP_QUEUE:
+ case OFPACT_FIN_TIMEOUT:
+ case OFPACT_RESUBMIT:
+ case OFPACT_LEARN:
+ case OFPACT_MULTIPATH:
+ case OFPACT_AUTOPATH:
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ default:
+ return false;
+ }
+}
+
+/* Returns true if any action in the 'ofpacts_len' bytes of 'ofpacts' outputs
+ * to 'port', false otherwise. */
+bool
+ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len,
+ uint16_t port)
+{
+ const struct ofpact *a;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ if (ofpact_outputs_to_port(a, port)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+ofpacts_equal(const struct ofpact *a, size_t a_len,
+ const struct ofpact *b, size_t b_len)
+{
+ return a_len == b_len && !memcmp(a, b, a_len);
+}
+\f
+/* Formatting ofpacts. */
+
+static void
+print_note(const struct ofpact_note *note, struct ds *string)
+{
+ size_t i;
+
+ ds_put_cstr(string, "note:");
+ for (i = 0; i < note->length; i++) {
+ if (i) {
+ ds_put_char(string, '.');
+ }
+ ds_put_format(string, "%02"PRIx8, note->data[i]);
+ }
+}
+
+static void
+print_dec_ttl(const struct ofpact_cnt_ids *ids,
+ struct ds *s)
+{
+ size_t i;
+
+ ds_put_cstr(s, "dec_ttl");
+ if (ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL_CNT_IDS) {
+ ds_put_cstr(s, "(");
+ for (i = 0; i < ids->n_controllers; i++) {
+ if (i) {
+ ds_put_cstr(s, ",");
+ }
+ ds_put_format(s, "%"PRIu16, ids->cnt_ids[i]);
+ }
+ ds_put_cstr(s, ")");
+ }
+}
+
+static void
+print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout,
+ struct ds *s)
+{
+ ds_put_cstr(s, "fin_timeout(");
+ if (fin_timeout->fin_idle_timeout) {
+ ds_put_format(s, "idle_timeout=%"PRIu16",",
+ fin_timeout->fin_idle_timeout);
+ }
+ if (fin_timeout->fin_hard_timeout) {
+ ds_put_format(s, "hard_timeout=%"PRIu16",",
+ fin_timeout->fin_hard_timeout);
+ }
+ ds_chomp(s, ',');
+ ds_put_char(s, ')');
+}
+
+static void
+ofpact_format(const struct ofpact *a, struct ds *s)
+{
+ const struct ofpact_enqueue *enqueue;
+ const struct ofpact_resubmit *resubmit;
+ const struct ofpact_autopath *autopath;
+ const struct ofpact_controller *controller;
+ const struct ofpact_tunnel *tunnel;
+ uint16_t port;
+
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ port = ofpact_get_OUTPUT(a)->port;
+ if (port < OFPP_MAX) {
+ ds_put_format(s, "output:%"PRIu16, port);
+ } else {
+ ofputil_format_port(port, s);
+ if (port == OFPP_CONTROLLER) {
+ ds_put_format(s, ":%"PRIu16, ofpact_get_OUTPUT(a)->max_len);
+ }
+ }
+ break;
+
+ case OFPACT_CONTROLLER:
+ controller = ofpact_get_CONTROLLER(a);
+ if (controller->reason == OFPR_ACTION &&
+ controller->controller_id == 0) {
+ ds_put_format(s, "CONTROLLER:%"PRIu16,
+ ofpact_get_CONTROLLER(a)->max_len);
+ } else {
+ enum ofp_packet_in_reason reason = controller->reason;
+
+ ds_put_cstr(s, "controller(");
+ if (reason != OFPR_ACTION) {
+ ds_put_format(s, "reason=%s,",
+ ofputil_packet_in_reason_to_string(reason));
+ }
+ if (controller->max_len != UINT16_MAX) {
+ ds_put_format(s, "max_len=%"PRIu16",", controller->max_len);
+ }
+ if (controller->controller_id != 0) {
+ ds_put_format(s, "id=%"PRIu16",", controller->controller_id);
+ }
+ ds_chomp(s, ',');
+ ds_put_char(s, ')');
+ }
+ break;
+
+ case OFPACT_ENQUEUE:
+ enqueue = ofpact_get_ENQUEUE(a);
+ ds_put_format(s, "enqueue:");
+ ofputil_format_port(enqueue->port, s);
+ ds_put_format(s, "q%"PRIu32, enqueue->queue);
+ break;
+
+ case OFPACT_OUTPUT_REG:
+ ds_put_cstr(s, "output:");
+ mf_format_subfield(&ofpact_get_OUTPUT_REG(a)->src, s);
+ break;
+
+ case OFPACT_BUNDLE:
+ bundle_format(ofpact_get_BUNDLE(a), s);
+ break;
+
+ case OFPACT_SET_VLAN_VID:
+ ds_put_format(s, "mod_vlan_vid:%"PRIu16,
+ ofpact_get_SET_VLAN_VID(a)->vlan_vid);
+ break;
+
+ case OFPACT_SET_VLAN_PCP:
+ ds_put_format(s, "mod_vlan_pcp:%"PRIu8,
+ ofpact_get_SET_VLAN_PCP(a)->vlan_pcp);
+ break;
+
+ case OFPACT_STRIP_VLAN:
+ ds_put_cstr(s, "strip_vlan");
+ break;
+
+ case OFPACT_SET_ETH_SRC:
+ ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(ofpact_get_SET_ETH_SRC(a)->mac));
+ break;
+
+ case OFPACT_SET_ETH_DST:
+ ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(ofpact_get_SET_ETH_DST(a)->mac));
+ break;
+
+ case OFPACT_SET_IPV4_SRC:
+ ds_put_format(s, "mod_nw_src:"IP_FMT,
+ IP_ARGS(&ofpact_get_SET_IPV4_SRC(a)->ipv4));
+ break;
+
+ case OFPACT_SET_IPV4_DST:
+ ds_put_format(s, "mod_nw_dst:"IP_FMT,
+ IP_ARGS(&ofpact_get_SET_IPV4_DST(a)->ipv4));
+ break;
+
+ case OFPACT_SET_IPV4_DSCP:
+ ds_put_format(s, "mod_nw_tos:%d", ofpact_get_SET_IPV4_DSCP(a)->dscp);
+ break;
+
+ case OFPACT_SET_L4_SRC_PORT:
+ ds_put_format(s, "mod_tp_src:%d", ofpact_get_SET_L4_SRC_PORT(a)->port);
+ break;
+
+ case OFPACT_SET_L4_DST_PORT:
+ ds_put_format(s, "mod_tp_dst:%d", ofpact_get_SET_L4_DST_PORT(a)->port);
+ break;
+
+ case OFPACT_REG_MOVE:
+ nxm_format_reg_move(ofpact_get_REG_MOVE(a), s);
+ break;
+
+ case OFPACT_REG_LOAD:
+ nxm_format_reg_load(ofpact_get_REG_LOAD(a), s);
+ break;
+
+ case OFPACT_DEC_TTL:
+ print_dec_ttl(ofpact_get_DEC_TTL(a), s);
+ break;
+
+ case OFPACT_SET_TUNNEL:
+ tunnel = ofpact_get_SET_TUNNEL(a);
+ ds_put_format(s, "set_tunnel%s:%#"PRIx64,
+ (tunnel->tun_id > UINT32_MAX
+ || a->compat == OFPUTIL_NXAST_SET_TUNNEL64 ? "64" : ""),
+ tunnel->tun_id);
+ break;
+
+ case OFPACT_SET_QUEUE:
+ ds_put_format(s, "set_queue:%"PRIu32,
+ ofpact_get_SET_QUEUE(a)->queue_id);
+ break;
+
+ case OFPACT_POP_QUEUE:
+ ds_put_cstr(s, "pop_queue");
+ break;
+
+ case OFPACT_FIN_TIMEOUT:
+ print_fin_timeout(ofpact_get_FIN_TIMEOUT(a), s);
+ break;
+
+ case OFPACT_RESUBMIT:
+ resubmit = ofpact_get_RESUBMIT(a);
+ if (resubmit->in_port != OFPP_IN_PORT && resubmit->table_id == 255) {
+ ds_put_format(s, "resubmit:%"PRIu16, resubmit->in_port);
+ } else {
+ ds_put_format(s, "resubmit(");
+ if (resubmit->in_port != OFPP_IN_PORT) {
+ ofputil_format_port(resubmit->in_port, s);
+ }
+ ds_put_char(s, ',');
+ if (resubmit->table_id != 255) {
+ ds_put_format(s, "%"PRIu8, resubmit->table_id);
+ }
+ ds_put_char(s, ')');
+ }
+ break;
+
+ case OFPACT_LEARN:
+ learn_format(ofpact_get_LEARN(a), s);
+ break;
+
+ case OFPACT_MULTIPATH:
+ multipath_format(ofpact_get_MULTIPATH(a), s);
+ break;
+
+ case OFPACT_AUTOPATH:
+ autopath = ofpact_get_AUTOPATH(a);
+ ds_put_format(s, "autopath(%u,", autopath->port);
+ mf_format_subfield(&autopath->dst, s);
+ ds_put_char(s, ')');
+ break;
+
+ case OFPACT_NOTE:
+ print_note(ofpact_get_NOTE(a), s);
+ break;
+
+ case OFPACT_EXIT:
+ ds_put_cstr(s, "exit");
+ break;
+ }
+}
+
+/* Appends a string representing the 'ofpacts_len' bytes of ofpacts in
+ * 'ofpacts' to 'string'. */
+void
+ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
+ struct ds *string)
+{
+ ds_put_cstr(string, "actions=");
+ if (!ofpacts_len) {
+ ds_put_cstr(string, "drop");
+ } else {
+ const struct ofpact *a;
+
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ if (a != ofpacts) {
+ ds_put_cstr(string, ",");
+ }
+ ofpact_format(a, string);
+ }
+ }
+}
+\f
+/* Internal use by helpers. */
+
+void *
+ofpact_put(struct ofpbuf *ofpacts, enum ofpact_type type, size_t len)
+{
+ struct ofpact *ofpact;
+
+ ofpact_pad(ofpacts);
+ ofpact = ofpacts->l2 = ofpbuf_put_uninit(ofpacts, len);
+ ofpact_init(ofpact, type, len);
+ return ofpact;
+}
+
+void
+ofpact_init(struct ofpact *ofpact, enum ofpact_type type, size_t len)
+{
+ memset(ofpact, 0, len);
+ ofpact->type = type;
+ ofpact->compat = OFPUTIL_ACTION_INVALID;
+ ofpact->len = len;
+}
+\f
+/* Updates 'ofpact->len' to the number of bytes in the tail of 'ofpacts'
+ * starting at 'ofpact'.
+ *
+ * This is the correct way to update a variable-length ofpact's length after
+ * adding the variable-length part of the payload. (See the large comment
+ * near the end of ofp-actions.h for more information.) */
+void
+ofpact_update_len(struct ofpbuf *ofpacts, struct ofpact *ofpact)
+{
+ assert(ofpact == ofpacts->l2);
+ ofpact->len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact;
+}
+
+/* Pads out 'ofpacts' to a multiple of OFPACT_ALIGNTO bytes in length. Each
+ * ofpact_put_<ENUM>() calls this function automatically beforehand, but the
+ * client must call this itself after adding the final ofpact to an array of
+ * them.
+ *
+ * (The consequences of failing to call this function are probably not dire.
+ * OFPACT_FOR_EACH will calculate a pointer beyond the end of the ofpacts, but
+ * not dereference it. That's undefined behavior, technically, but it will not
+ * cause a real problem on common systems. Still, it seems better to call
+ * it.) */
+void
+ofpact_pad(struct ofpbuf *ofpacts)
+{
+ unsigned int rem = ofpacts->size % OFPACT_ALIGNTO;
+ if (rem) {
+ ofpbuf_put_zeros(ofpacts, OFPACT_ALIGNTO - rem);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFP_ACTIONS_H
+#define OFP_ACTIONS_H 1
+
+#include <stdint.h>
+#include "meta-flow.h"
+#include "ofp-errors.h"
+#include "ofp-util.h"
+#include "openflow/openflow.h"
+#include "openflow/nicira-ext.h"
+#include "openvswitch/types.h"
+
+/* List of OVS abstracted actions.
+ *
+ * This macro is used directly only internally by this header, but the list is
+ * still of interest to developers.
+ *
+ * Each DEFINE_OFPACT invocation has the following parameters:
+ *
+ * 1. <ENUM>, used below in the enum definition of OFPACT_<ENUM>, and
+ * elsewhere.
+ *
+ * 2. <STRUCT> corresponding to a structure "struct <STRUCT>", that must be
+ * defined below. This structure must be an abstract definition of the
+ * action. Its first member must have type "struct ofpact" and name
+ * "ofpact". It may be fixed length or end with a flexible array member
+ * (e.g. "int member[];").
+ *
+ * 3. <MEMBER>, which has one of two possible values:
+ *
+ * - If "struct <STRUCT>" is fixed-length, it must be "ofpact".
+ *
+ * - If "struct <STRUCT>" is variable-length, it must be the name of the
+ * flexible array member.
+ */
+#define OFPACTS \
+ /* Output. */ \
+ DEFINE_OFPACT(OUTPUT, ofpact_output, ofpact) \
+ DEFINE_OFPACT(CONTROLLER, ofpact_controller, ofpact) \
+ DEFINE_OFPACT(ENQUEUE, ofpact_enqueue, ofpact) \
+ DEFINE_OFPACT(OUTPUT_REG, ofpact_output_reg, ofpact) \
+ DEFINE_OFPACT(BUNDLE, ofpact_bundle, slaves) \
+ \
+ /* Header changes. */ \
+ DEFINE_OFPACT(SET_VLAN_VID, ofpact_vlan_vid, ofpact) \
+ DEFINE_OFPACT(SET_VLAN_PCP, ofpact_vlan_pcp, ofpact) \
+ DEFINE_OFPACT(STRIP_VLAN, ofpact_null, ofpact) \
+ DEFINE_OFPACT(SET_ETH_SRC, ofpact_mac, ofpact) \
+ DEFINE_OFPACT(SET_ETH_DST, ofpact_mac, ofpact) \
+ DEFINE_OFPACT(SET_IPV4_SRC, ofpact_ipv4, ofpact) \
+ DEFINE_OFPACT(SET_IPV4_DST, ofpact_ipv4, ofpact) \
+ DEFINE_OFPACT(SET_IPV4_DSCP, ofpact_dscp, ofpact) \
+ DEFINE_OFPACT(SET_L4_SRC_PORT, ofpact_l4_port, ofpact) \
+ DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port, ofpact) \
+ DEFINE_OFPACT(REG_MOVE, ofpact_reg_move, ofpact) \
+ DEFINE_OFPACT(REG_LOAD, ofpact_reg_load, ofpact) \
+ DEFINE_OFPACT(DEC_TTL, ofpact_cnt_ids, cnt_ids) \
+ \
+ /* Metadata. */ \
+ DEFINE_OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact) \
+ DEFINE_OFPACT(SET_QUEUE, ofpact_queue, ofpact) \
+ DEFINE_OFPACT(POP_QUEUE, ofpact_null, ofpact) \
+ DEFINE_OFPACT(FIN_TIMEOUT, ofpact_fin_timeout, ofpact) \
+ \
+ /* Flow table interaction. */ \
+ DEFINE_OFPACT(RESUBMIT, ofpact_resubmit, ofpact) \
+ DEFINE_OFPACT(LEARN, ofpact_learn, specs) \
+ \
+ /* Arithmetic. */ \
+ DEFINE_OFPACT(MULTIPATH, ofpact_multipath, ofpact) \
+ DEFINE_OFPACT(AUTOPATH, ofpact_autopath, ofpact) \
+ \
+ /* Other. */ \
+ DEFINE_OFPACT(NOTE, ofpact_note, data) \
+ DEFINE_OFPACT(EXIT, ofpact_null, ofpact)
+
+/* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */
+enum OVS_PACKED_ENUM ofpact_type {
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) OFPACT_##ENUM,
+ OFPACTS
+#undef DEFINE_OFPACT
+};
+
+/* N_OFPACTS, the number of values of "enum ofpact_type". */
+enum {
+ N_OFPACTS =
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) + 1
+ OFPACTS
+#undef DEFINE_OFPACT
+};
+
+/* Header for an action.
+ *
+ * Each action is a structure "struct ofpact_*" that begins with "struct
+ * ofpact", usually followed by other data that describes the action. Actions
+ * are padded out to a multiple of OFPACT_ALIGNTO bytes in length. */
+struct ofpact {
+ enum ofpact_type type; /* OFPACT_*. */
+ enum ofputil_action_code compat; /* Original type when added, if any. */
+ uint16_t len; /* Length of the action, in bytes, including
+ * struct ofpact, excluding padding. */
+};
+
+#ifdef __GNUC__
+/* Make sure that OVS_PACKED_ENUM really worked. */
+BUILD_ASSERT_DECL(sizeof(struct ofpact) == 4);
+#endif
+
+/* Alignment. */
+#define OFPACT_ALIGNTO 8
+#define OFPACT_ALIGN(SIZE) ROUND_UP(SIZE, OFPACT_ALIGNTO)
+
+static inline struct ofpact *
+ofpact_next(const struct ofpact *ofpact)
+{
+ return (void *) ((uint8_t *) ofpact + OFPACT_ALIGN(ofpact->len));
+}
+
+static inline struct ofpact *
+ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len)
+{
+ return (void *) ((uint8_t *) ofpacts + ofpacts_len);
+}
+
+/* Assigns POS to each ofpact, in turn, in the OFPACTS_LEN bytes of ofpacts
+ * starting at OFPACTS. */
+#define OFPACT_FOR_EACH(POS, OFPACTS, OFPACTS_LEN) \
+ for ((POS) = (OFPACTS); (POS) < ofpact_end(OFPACTS, OFPACTS_LEN); \
+ (POS) = ofpact_next(POS))
+\f
+/* Action structure for each OFPACT_*. */
+
+/* OFPACT_STRIP_VLAN, OFPACT_POP_QUEUE, OFPACT_EXIT.
+ *
+ * Used for OFPAT10_STRIP_VLAN, NXAST_DEC_TTL, NXAST_POP_QUEUE, NXAST_EXIT.
+ *
+ * Action structure for actions that do not have any extra data beyond the
+ * action type. */
+struct ofpact_null {
+ struct ofpact ofpact;
+};
+
+/* OFPACT_OUTPUT.
+ *
+ * Used for OFPAT10_OUTPUT. */
+struct ofpact_output {
+ struct ofpact ofpact;
+ uint16_t port; /* Output port. */
+ uint16_t max_len; /* Max send len, for port OFPP_CONTROLLER. */
+};
+
+/* OFPACT_CONTROLLER.
+ *
+ * Used for NXAST_CONTROLLER. */
+struct ofpact_controller {
+ struct ofpact ofpact;
+ uint16_t max_len; /* Maximum length to send to controller. */
+ uint16_t controller_id; /* Controller ID to send packet-in. */
+ enum ofp_packet_in_reason reason; /* Reason to put in packet-in. */
+};
+
+/* OFPACT_ENQUEUE.
+ *
+ * Used for OFPAT10_ENQUEUE. */
+struct ofpact_enqueue {
+ struct ofpact ofpact;
+ uint16_t port;
+ uint32_t queue;
+};
+
+/* OFPACT_OUTPUT_REG.
+ *
+ * Used for NXAST_OUTPUT_REG. */
+struct ofpact_output_reg {
+ struct ofpact ofpact;
+ struct mf_subfield src;
+ uint16_t max_len;
+};
+
+/* OFPACT_BUNDLE.
+ *
+ * Used for NXAST_BUNDLE. */
+struct ofpact_bundle {
+ struct ofpact ofpact;
+
+ /* Slave choice algorithm to apply to hash value. */
+ enum nx_bd_algorithm algorithm;
+
+ /* What fields to hash and how. */
+ enum nx_hash_fields fields;
+ uint16_t basis; /* Universal hash parameter. */
+
+ struct mf_subfield dst;
+
+ /* Slaves for output. */
+ unsigned int n_slaves;
+ uint16_t slaves[];
+};
+
+/* OFPACT_SET_VLAN_VID.
+ *
+ * Used for OFPAT10_SET_VLAN_VID. */
+struct ofpact_vlan_vid {
+ struct ofpact ofpact;
+ uint16_t vlan_vid; /* VLAN VID in low 12 bits, 0 in other bits. */
+};
+
+/* OFPACT_SET_VLAN_PCP.
+ *
+ * Used for OFPAT10_SET_VLAN_PCP. */
+struct ofpact_vlan_pcp {
+ struct ofpact ofpact;
+ uint8_t vlan_pcp; /* VLAN PCP in low 3 bits, 0 in other bits. */
+};
+
+/* OFPACT_SET_ETH_SRC, OFPACT_SET_ETH_DST.
+ *
+ * Used for OFPAT10_SET_DL_SRC, OFPAT10_SET_DL_DST. */
+struct ofpact_mac {
+ struct ofpact ofpact;
+ uint8_t mac[ETH_ADDR_LEN];
+};
+
+/* OFPACT_SET_IPV4_SRC, OFPACT_SET_IPV4_DST.
+ *
+ * Used for OFPAT10_SET_NW_SRC, OFPAT10_SET_NW_DST. */
+struct ofpact_ipv4 {
+ struct ofpact ofpact;
+ ovs_be32 ipv4;
+};
+
+/* OFPACT_SET_IPV4_DSCP.
+ *
+ * Used for OFPAT10_SET_NW_TOS. */
+struct ofpact_dscp {
+ struct ofpact ofpact;
+ uint8_t dscp; /* DSCP in high 6 bits, rest ignored. */
+};
+
+/* OFPACT_SET_L4_SRC_PORT, OFPACT_SET_L4_DST_PORT.
+ *
+ * Used for OFPAT10_SET_TP_SRC, OFPAT10_SET_TP_DST. */
+struct ofpact_l4_port {
+ struct ofpact ofpact;
+ uint16_t port; /* TCP or UDP port number. */
+};
+
+/* OFPACT_REG_MOVE.
+ *
+ * Used for NXAST_REG_MOVE. */
+struct ofpact_reg_move {
+ struct ofpact ofpact;
+ struct mf_subfield src;
+ struct mf_subfield dst;
+};
+
+/* OFPACT_REG_LOAD.
+ *
+ * Used for NXAST_REG_LOAD. */
+struct ofpact_reg_load {
+ struct ofpact ofpact;
+ struct mf_subfield dst;
+ uint64_t value;
+};
+
+/* OFPACT_SET_TUNNEL.
+ *
+ * Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */
+struct ofpact_tunnel {
+ struct ofpact ofpact;
+ uint64_t tun_id;
+};
+
+/* OFPACT_SET_QUEUE.
+ *
+ * Used for NXAST_SET_QUEUE. */
+struct ofpact_queue {
+ struct ofpact ofpact;
+ uint32_t queue_id;
+};
+
+/* OFPACT_FIN_TIMEOUT.
+ *
+ * Used for NXAST_FIN_TIMEOUT. */
+struct ofpact_fin_timeout {
+ struct ofpact ofpact;
+ uint16_t fin_idle_timeout;
+ uint16_t fin_hard_timeout;
+};
+
+/* OFPACT_RESUBMIT.
+ *
+ * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */
+struct ofpact_resubmit {
+ struct ofpact ofpact;
+ uint16_t in_port;
+ uint8_t table_id;
+};
+
+/* Part of struct ofpact_learn, below. */
+struct ofpact_learn_spec {
+ int n_bits;
+
+ int src_type;
+ struct mf_subfield src;
+ union mf_subvalue src_imm;
+
+ int dst_type;
+ struct mf_subfield dst;
+};
+
+/* OFPACT_LEARN.
+ *
+ * Used for NXAST_LEARN. */
+struct ofpact_learn {
+ struct ofpact ofpact;
+
+ uint16_t idle_timeout; /* Idle time before discarding (seconds). */
+ uint16_t hard_timeout; /* Max time before discarding (seconds). */
+ uint16_t priority; /* Priority level of flow entry. */
+ uint64_t cookie; /* Cookie for new flow. */
+ uint16_t flags; /* Either 0 or OFPFF_SEND_FLOW_REM. */
+ uint8_t table_id; /* Table to insert flow entry. */
+ uint16_t fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */
+ uint16_t fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */
+
+ unsigned int n_specs;
+ struct ofpact_learn_spec specs[];
+};
+
+/* OFPACT_MULTIPATH.
+ *
+ * Used for NXAST_MULTIPATH. */
+struct ofpact_multipath {
+ struct ofpact ofpact;
+
+ /* What fields to hash and how. */
+ enum nx_hash_fields fields;
+ uint16_t basis; /* Universal hash parameter. */
+
+ /* Multipath link choice algorithm to apply to hash value. */
+ enum nx_mp_algorithm algorithm;
+ uint16_t max_link; /* Number of output links, minus 1. */
+ uint32_t arg; /* Algorithm-specific argument. */
+
+ /* Where to store the result. */
+ struct mf_subfield dst;
+};
+
+/* OFPACT_AUTOPATH.
+ *
+ * Used for NXAST_AUTOPATH. */
+struct ofpact_autopath {
+ struct ofpact ofpact;
+ struct mf_subfield dst;
+ uint32_t port;
+};
+
+/* OFPACT_NOTE.
+ *
+ * Used for NXAST_NOTE. */
+struct ofpact_note {
+ struct ofpact ofpact;
+ size_t length;
+ uint8_t data[];
+};
+
+/* OFPACT_DEC_TTL.
+ *
+ * Used for NXAST_DEC_TTL and NXAST_DEC_TTL_CNT_IDS. */
+struct ofpact_cnt_ids {
+ struct ofpact ofpact;
+
+ /* Controller ids. */
+ unsigned int n_controllers;
+ uint16_t cnt_ids[];
+
+};
+
+/* Converting OpenFlow to ofpacts. */
+enum ofperr ofpacts_pull_openflow10(struct ofpbuf *openflow,
+ unsigned int actions_len,
+ struct ofpbuf *ofpacts);
+enum ofperr ofpacts_pull_openflow11_actions(struct ofpbuf *openflow,
+ unsigned int actions_len,
+ struct ofpbuf *ofpacts);
+enum ofperr ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
+ unsigned int instructions_len,
+ struct ofpbuf *ofpacts);
+enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len,
+ const struct flow *, int max_ports);
+
+/* Converting ofpacts to OpenFlow. */
+void ofpacts_put_openflow10(const struct ofpact[], size_t ofpacts_len,
+ struct ofpbuf *openflow);
+size_t ofpacts_put_openflow11_actions(const struct ofpact[], size_t ofpacts_len,
+ struct ofpbuf *openflow);
+void ofpacts_put_openflow11_instructions(const struct ofpact[],
+ size_t ofpacts_len,
+ struct ofpbuf *openflow);
+
+/* Working with ofpacts. */
+bool ofpacts_output_to_port(const struct ofpact[], size_t ofpacts_len,
+ uint16_t port);
+bool ofpacts_equal(const struct ofpact a[], size_t a_len,
+ const struct ofpact b[], size_t b_len);
+
+/* Formatting ofpacts.
+ *
+ * (For parsing ofpacts, see ofp-parse.h.) */
+void ofpacts_format(const struct ofpact[], size_t ofpacts_len, struct ds *);
+
+/* Internal use by the helpers below. */
+void ofpact_init(struct ofpact *, enum ofpact_type, size_t len);
+void *ofpact_put(struct ofpbuf *, enum ofpact_type, size_t len);
+
+/* For each OFPACT_<ENUM> with a corresponding struct <STRUCT>, this defines
+ * the following commonly useful functions:
+ *
+ * struct <STRUCT> *ofpact_put_<ENUM>(struct ofpbuf *ofpacts);
+ *
+ * Appends a new 'ofpact', of length OFPACT_<ENUM>_RAW_SIZE, to 'ofpacts',
+ * initializes it with ofpact_init_<ENUM>(), and returns it. Also sets
+ * 'ofpacts->l2' to the returned action.
+ *
+ * After using this function to add a variable-length action, add the
+ * elements of the flexible array (e.g. with ofpbuf_put()), then use
+ * ofpact_update_len() to update the length embedded into the action.
+ * (Keep in mind the need to refresh the structure from 'ofpacts->l2' after
+ * adding data to 'ofpacts'.)
+ *
+ * struct <STRUCT> *ofpact_get_<ENUM>(const struct ofpact *ofpact);
+ *
+ * Returns 'ofpact' cast to "struct <STRUCT> *". 'ofpact->type' must be
+ * OFPACT_<ENUM>.
+ *
+ * as well as the following more rarely useful definitions:
+ *
+ * void ofpact_init_<ENUM>(struct <STRUCT> *ofpact);
+ *
+ * Initializes the parts of 'ofpact' that identify it as having type
+ * OFPACT_<ENUM> and length OFPACT_<ENUM>_RAW_SIZE and zeros the rest.
+ *
+ * <ENUM>_RAW_SIZE
+ *
+ * The size of the action structure. For a fixed-length action, this is
+ * sizeof(struct <STRUCT>). For a variable-length action, this is the
+ * offset to the variable-length part.
+ *
+ * <ENUM>_SIZE
+ *
+ * An integer constant, the value of OFPACT_<ENUM>_RAW_SIZE rounded up to a
+ * multiple of OFPACT_ALIGNTO.
+ */
+#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) \
+ BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0); \
+ \
+ enum { OFPACT_##ENUM##_RAW_SIZE \
+ = (offsetof(struct STRUCT, MEMBER) \
+ ? offsetof(struct STRUCT, MEMBER) \
+ : sizeof(struct STRUCT)) }; \
+ \
+ enum { OFPACT_##ENUM##_SIZE \
+ = ROUND_UP(OFPACT_##ENUM##_RAW_SIZE, OFPACT_ALIGNTO) }; \
+ \
+ static inline struct STRUCT * \
+ ofpact_get_##ENUM(const struct ofpact *ofpact) \
+ { \
+ assert(ofpact->type == OFPACT_##ENUM); \
+ return (struct STRUCT *) ofpact; \
+ } \
+ \
+ static inline struct STRUCT * \
+ ofpact_put_##ENUM(struct ofpbuf *ofpacts) \
+ { \
+ return ofpact_put(ofpacts, OFPACT_##ENUM, \
+ OFPACT_##ENUM##_RAW_SIZE); \
+ } \
+ \
+ static inline void \
+ ofpact_init_##ENUM(struct STRUCT *ofpact) \
+ { \
+ ofpact_init(&ofpact->ofpact, OFPACT_##ENUM, \
+ OFPACT_##ENUM##_RAW_SIZE); \
+ }
+OFPACTS
+#undef DEFINE_OFPACT
+
+/* Functions to use after adding ofpacts to a buffer. */
+void ofpact_update_len(struct ofpbuf *, struct ofpact *);
+void ofpact_pad(struct ofpbuf *);
+
+/* OpenFlow 1.1 instructions.
+ * The order is sorted in execution order. Not in the value of OFPIT11_xxx.
+ * It is enforced on parser from text string.
+ */
+#define OVS_INSTRUCTIONS \
+ DEFINE_INST(OFPIT11_APPLY_ACTIONS, \
+ ofp11_instruction_actions, true, \
+ "apply_actions") \
+ \
+ DEFINE_INST(OFPIT11_CLEAR_ACTIONS, \
+ ofp11_instruction, false, \
+ "clear_actions") \
+ \
+ DEFINE_INST(OFPIT11_WRITE_ACTIONS, \
+ ofp11_instruction_actions, true, \
+ "write_actions") \
+ \
+ DEFINE_INST(OFPIT11_WRITE_METADATA, \
+ ofp11_instruction_write_metadata, false, \
+ "write_metadata") \
+ \
+ DEFINE_INST(OFPIT11_GOTO_TABLE, \
+ ofp11_instruction_goto_table, false, \
+ "goto_table")
+
+enum ovs_instruction_type {
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) OVSINST_##ENUM,
+ OVS_INSTRUCTIONS
+#undef DEFINE_INST
+};
+
+enum {
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) + 1
+ N_OVS_INSTRUCTIONS = OVS_INSTRUCTIONS
+#undef DEFINE_INST
+};
+
+const char *ofpact_instruction_name_from_type(enum ovs_instruction_type type);
+int ofpact_instruction_type_from_name(const char *name);
+
+#endif /* ofp-actions.h */
#include <errno.h>
#include "byte-order.h"
#include "dynamic-string.h"
+#include "ofp-msgs.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
* 'version' (one of the possible values of struct ofp_header's 'version'
* member). Returns NULL if the version isn't defined or isn't understood by
* OVS. */
-const struct ofperr_domain *
-ofperr_domain_from_version(uint8_t version)
+static const struct ofperr_domain *
+ofperr_domain_from_version(enum ofp_version version)
{
- return (version == ofperr_of10.version ? &ofperr_of10
- : version == ofperr_of11.version ? &ofperr_of11
- : version == ofperr_of12.version ? &ofperr_of12
- : NULL);
+ switch (version) {
+ case OFP10_VERSION:
+ return &ofperr_of10;
+ case OFP11_VERSION:
+ return &ofperr_of11;
+ case OFP12_VERSION:
+ return &ofperr_of12;
+ default:
+ return NULL;
+ }
}
-/* Returns the name (e.g. "OpenFlow 1.0") of OpenFlow error domain 'domain'. */
+/* Returns the name (e.g. "OpenFlow 1.0") of OpenFlow version 'version'. */
const char *
-ofperr_domain_get_name(const struct ofperr_domain *domain)
+ofperr_domain_get_name(enum ofp_version version)
{
- return domain->name;
+ const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+ return domain ? domain->name : NULL;
}
/* Returns true if 'error' is a valid OFPERR_* value, false otherwise. */
* A given error may not be encodable in some domains because each OpenFlow
* version tends to introduce new errors and retire some old ones. */
bool
-ofperr_is_encodable(enum ofperr error, const struct ofperr_domain *domain)
+ofperr_is_encodable(enum ofperr error, enum ofp_version version)
{
+ const struct ofperr_domain *domain = ofperr_domain_from_version(version);
return (ofperr_is_valid(error)
- && domain->errors[error - OFPERR_OFS].code >= 0);
+ && domain && domain->errors[error - OFPERR_OFS].code >= 0);
}
/* Returns the OFPERR_* value that corresponds to 'type' and 'code' within
- * 'domain', or 0 if no such OFPERR_* value exists. */
+ * 'version', or 0 if either no such OFPERR_* value exists or 'version' is
+ * unknown. */
enum ofperr
-ofperr_decode(const struct ofperr_domain *domain, uint16_t type, uint16_t code)
+ofperr_decode(enum ofp_version version, uint16_t type, uint16_t code)
{
- return domain->decode(type, code);
+ const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+ return domain ? domain->decode(type, code) : 0;
}
/* Returns the OFPERR_* value that corresponds to the category 'type' within
- * 'domain', or 0 if no such OFPERR_* value exists. */
+ * 'version', or 0 if either no such OFPERR_* value exists or 'version' is
+ * unknown. */
enum ofperr
-ofperr_decode_type(const struct ofperr_domain *domain, uint16_t type)
+ofperr_decode_type(enum ofp_version version, uint16_t type)
{
- return domain->decode_type(type);
+ const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+ return domain ? domain->decode_type(type) : 0;
}
/* Returns the name of 'error', e.g. "OFPBRC_BAD_TYPE" if 'error' is
}
static struct ofpbuf *
-ofperr_encode_msg__(enum ofperr error, const struct ofperr_domain *domain,
+ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version,
ovs_be32 xid, const void *data, size_t data_len)
{
struct ofp_error_msg *oem;
const struct pair *pair;
struct ofpbuf *buf;
+ const struct ofperr_domain *domain;
+ domain = ofperr_domain_from_version(ofp_version);
if (!domain) {
return NULL;
}
- if (!ofperr_is_encodable(error, domain)) {
+ if (!ofperr_is_encodable(error, ofp_version)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
if (!ofperr_is_valid(error)) {
pair = ofperr_get_pair__(error, domain);
if (!ofperr_is_nx_extension(error)) {
- oem = make_openflow_xid(data_len + sizeof *oem, OFPT_ERROR, xid, &buf);
+ buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
+ sizeof *oem + data_len);
+
+ oem = ofpbuf_put_uninit(buf, sizeof *oem);
oem->type = htons(pair->type);
oem->code = htons(pair->code);
} else {
struct nx_vendor_error *nve;
- oem = make_openflow_xid(data_len + sizeof *oem + sizeof *nve,
- OFPT_ERROR, xid, &buf);
+ buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid,
+ sizeof *oem + sizeof *nve + data_len);
+
+ oem = ofpbuf_put_uninit(buf, sizeof *oem);
oem->type = htons(NXET_VENDOR);
oem->code = htons(NXVC_VENDOR_ERROR);
- nve = (struct nx_vendor_error *) oem->data;
+ nve = ofpbuf_put_uninit(buf, sizeof *nve);
nve->vendor = htonl(NX_VENDOR_ID);
nve->type = htons(pair->type);
nve->code = htons(pair->code);
}
- oem->header.version = domain->version;
- buf->size -= data_len;
ofpbuf_put(buf, data, data_len);
return buf;
struct ofpbuf *
ofperr_encode_reply(enum ofperr error, const struct ofp_header *oh)
{
- const struct ofperr_domain *domain;
uint16_t len = ntohs(oh->length);
- domain = ofperr_domain_from_version(oh->version);
- return ofperr_encode_msg__(error, domain, oh->xid, oh, MIN(len, 64));
+ return ofperr_encode_msg__(error, oh->version, oh->xid, oh, MIN(len, 64));
}
/* Creates and returns an OpenFlow message of type OFPT_ERROR that conveys the
* given 'error', in the error domain 'domain'. The error message will include
* the additional null-terminated text string 's'.
*
- * If 'domain' is NULL, uses the OpenFlow 1.0 error domain. OFPET_HELLO_FAILED
- * error messages are supposed to be backward-compatible, so in theory this
- * should work.
+ * If 'version' is an unknown version then OFP10_VERSION is used.
+ * OFPET_HELLO_FAILED error messages are supposed to be backward-compatible,
+ * so in theory this should work.
*
* Returns NULL if 'error' is not an OpenFlow error code or if 'error' cannot
* be encoded in 'domain'. */
struct ofpbuf *
-ofperr_encode_hello(enum ofperr error, const struct ofperr_domain *domain,
+ofperr_encode_hello(enum ofperr error, enum ofp_version ofp_version,
const char *s)
{
- if (!domain) {
- domain = &ofperr_of10;
+ switch (ofp_version) {
+ case OFP10_VERSION:
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ break;
+
+ default:
+ ofp_version = OFP10_VERSION;
}
- return ofperr_encode_msg__(error, domain, htonl(0), s, strlen(s));
+
+ return ofperr_encode_msg__(error, ofp_version, htonl(0), s, strlen(s));
}
/* Returns the value that would go into an OFPT_ERROR message's 'type' for
* encoding 'error' in 'domain'. Returns -1 if 'error' is not encodable in
- * 'domain'.
+ * 'version' or 'version' is unknown.
*
* 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */
int
-ofperr_get_type(enum ofperr error, const struct ofperr_domain *domain)
+ofperr_get_type(enum ofperr error, enum ofp_version version)
{
- return ofperr_get_pair__(error, domain)->type;
+ const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+ return domain ? ofperr_get_pair__(error, domain)->type : -1;
}
/* Returns the value that would go into an OFPT_ERROR message's 'code' for
* encoding 'error' in 'domain'. Returns -1 if 'error' is not encodable in
- * 'domain' or if 'error' represents a category rather than a specific error.
+ * 'version', 'version' is unknown or if 'error' represents a category
+ * rather than a specific error.
+ *
*
* 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */
int
-ofperr_get_code(enum ofperr error, const struct ofperr_domain *domain)
+ofperr_get_code(enum ofperr error, enum ofp_version version)
{
- return ofperr_get_pair__(error, domain)->code;
+ const struct ofperr_domain *domain = ofperr_domain_from_version(version);
+ return domain ? ofperr_get_pair__(error, domain)->code : -1;
}
/* Tries to decodes 'oh', which should be an OpenFlow OFPT_ERROR message.
* Returns an OFPERR_* constant on success, 0 on failure.
*
- * If 'payload_ofs' is nonnull, on success '*payload_ofs' is set to the offset
- * to the payload starting from 'oh' and on failure it is set to 0. */
+ * If 'payload' is nonnull, on success '*payload' is initialized to the
+ * error's payload, and on failure it is cleared. */
enum ofperr
-ofperr_decode_msg(const struct ofp_header *oh, size_t *payload_ofs)
+ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- const struct ofperr_domain *domain;
const struct ofp_error_msg *oem;
+ enum ofpraw raw;
uint16_t type, code;
enum ofperr error;
struct ofpbuf b;
- if (payload_ofs) {
- *payload_ofs = 0;
+ if (payload) {
+ memset(payload, 0, sizeof *payload);
}
/* Pull off the error message. */
ofpbuf_use_const(&b, oh, ntohs(oh->length));
- oem = ofpbuf_try_pull(&b, sizeof *oem);
- if (!oem) {
- return 0;
- }
-
- /* Check message type and version. */
- if (oh->type != OFPT_ERROR) {
- return 0;
- }
- domain = ofperr_domain_from_version(oh->version);
- if (!domain) {
+ error = ofpraw_pull(&raw, &b);
+ if (error) {
return 0;
}
+ oem = ofpbuf_pull(&b, sizeof *oem);
/* Get the error type and code. */
type = ntohs(oem->type);
/* Translate the error type and code into an ofperr.
* If we don't know the error type and code, at least try for the type. */
- error = ofperr_decode(domain, type, code);
+ error = ofperr_decode(oh->version, type, code);
if (!error) {
- error = ofperr_decode_type(domain, type);
+ error = ofperr_decode_type(oh->version, type);
}
- if (error && payload_ofs) {
- *payload_ofs = (uint8_t *) b.data - (uint8_t *) oh;
+ if (error && payload) {
+ ofpbuf_use_const(payload, b.data, b.size);
}
return error;
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stddef.h>
#include <stdint.h>
+#include "openflow/openflow.h"
+
struct ds;
-struct ofp_header;
+struct ofpbuf;
/* Error codes.
*
* valid. */
OFPERR_NXBRC_BAD_REASON,
+ /* NX1.0+(1,517). The 'id' in an NXST_FLOW_MONITOR request is the same as
+ * an existing monitor id (or two monitors in the same NXST_FLOW_MONITOR
+ * request have the same 'id'). */
+ OFPERR_NXBRC_FM_DUPLICATE_ID,
+
+ /* NX1.0+(1,518). The 'flags' in an NXST_FLOW_MONITOR request either does
+ * not specify at least one of the NXFMF_ADD, NXFMF_DELETE, or NXFMF_MODIFY
+ * flags, or specifies a flag bit that is not defined. */
+ OFPERR_NXBRC_FM_BAD_FLAGS,
+
+ /* NX1.0+(1,519). The 'id' in an NXT_FLOW_MONITOR_CANCEL request is not
+ * the id of any existing monitor. */
+ OFPERR_NXBRC_FM_BAD_ID,
+
/* ## ---------------- ## */
/* ## OFPET_BAD_ACTION ## */
/* ## ---------------- ## */
/* ## --------------------- ## */
/* OF1.1+(3). Error in instruction list. */
- OFPERR_OFPET_BAD_INSTRUCTION,
+ OFPERR_OFPIT_BAD_INSTRUCTION,
/* OF1.1+(3,0). Unknown instruction. */
OFPERR_OFPBIC_UNKNOWN_INST,
/* OF1.2+(3,8). Permissions error. */
OFPERR_OFPBIC_EPERM,
+ /* NX1.1+(3,256). Duplicate instruction type in set of instructions. */
+ OFPERR_NXBIC_DUP_TYPE,
+
/* ## --------------- ## */
/* ## OFPET_BAD_MATCH ## */
/* ## --------------- ## */
* extension is enabled. */
OFPERR_NXFMFC_BAD_TABLE_ID,
+ /* NX1.0+(3,258). 'out_group' specified but groups not yet supported. */
+ OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED,
+
/* ## ---------------------- ## */
/* ## OFPET_GROUP_MOD_FAILED ## */
/* ## ---------------------- ## */
/* NX1.0(1,513), NX1.1(1,513), OF1.2+(11,2). Invalid role. */
OFPERR_OFPRRFC_BAD_ROLE,
-
/* ## ------------------ ## */
/* ## OFPET_EXPERIMENTER ## */
/* ## ------------------ ## */
OFPERR_OFPET_EXPERIMENTER,
};
-extern const struct ofperr_domain ofperr_of10;
-extern const struct ofperr_domain ofperr_of11;
-extern const struct ofperr_domain ofperr_of12;
-
-const struct ofperr_domain *ofperr_domain_from_version(uint8_t version);
-const char *ofperr_domain_get_name(const struct ofperr_domain *);
+const char *ofperr_domain_get_name(enum ofp_version);
bool ofperr_is_valid(enum ofperr);
bool ofperr_is_category(enum ofperr);
bool ofperr_is_nx_extension(enum ofperr);
-bool ofperr_is_encodable(enum ofperr, const struct ofperr_domain *);
+bool ofperr_is_encodable(enum ofperr, enum ofp_version);
-enum ofperr ofperr_decode(const struct ofperr_domain *,
- uint16_t type, uint16_t code);
-enum ofperr ofperr_decode_type(const struct ofperr_domain *, uint16_t type);
+enum ofperr ofperr_decode(enum ofp_version, uint16_t type, uint16_t code);
+enum ofperr ofperr_decode_type(enum ofp_version, uint16_t type);
enum ofperr ofperr_from_name(const char *);
-enum ofperr ofperr_decode_msg(const struct ofp_header *, size_t *payload_ofs);
+enum ofperr ofperr_decode_msg(const struct ofp_header *,
+ struct ofpbuf *payload);
struct ofpbuf *ofperr_encode_reply(enum ofperr, const struct ofp_header *);
-struct ofpbuf *ofperr_encode_hello(enum ofperr, const struct ofperr_domain *,
+struct ofpbuf *ofperr_encode_hello(enum ofperr, enum ofp_version ofp_version,
const char *);
-int ofperr_get_type(enum ofperr, const struct ofperr_domain *);
-int ofperr_get_code(enum ofperr, const struct ofperr_domain *);
+int ofperr_get_type(enum ofperr, enum ofp_version);
+int ofperr_get_code(enum ofperr, enum ofp_version);
const char *ofperr_get_name(enum ofperr);
const char *ofperr_get_description(enum ofperr);
--- /dev/null
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "ofp-msgs.h"
+#include <assert.h>
+#include "byte-order.h"
+#include "dynamic-string.h"
+#include "hash.h"
+#include "hmap.h"
+#include "ofpbuf.h"
+#include "openflow/nicira-ext.h"
+#include "openflow/openflow.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ofp_msgs);
+
+#define OFPT_VENDOR 4
+#define OFPT10_STATS_REQUEST 16
+#define OFPT10_STATS_REPLY 17
+#define OFPT11_STATS_REQUEST 18
+#define OFPT11_STATS_REPLY 19
+#define OFPST_VENDOR 0xffff
+
+/* A thin abstraction of OpenFlow headers:
+ *
+ * - 'version' and 'type' come straight from struct ofp_header, so these are
+ * always present and meaningful.
+ *
+ * - 'stat' comes from the 'type' member in statistics messages only. It is
+ * meaningful, therefore, only if 'version' and 'type' taken together
+ * specify a statistics request or reply. Otherwise it is 0.
+ *
+ * - 'vendor' is meaningful only for vendor messages, that is, if 'version'
+ * and 'type' specify a vendor message or if 'version' and 'type' specify
+ * a statistics message and 'stat' specifies a vendor statistic type.
+ * Otherwise it is 0.
+ *
+ * - 'subtype' is meaningful only for vendor messages and otherwise 0. It
+ * specifies a vendor-defined subtype. There is no standard format for
+ * these but 32 bits seems like it should be enough. */
+struct ofphdrs {
+ uint8_t version; /* From ofp_header. */
+ uint8_t type; /* From ofp_header. */
+ uint16_t stat; /* From ofp10_stats_msg or ofp11_stats_msg. */
+ uint32_t vendor; /* From ofp_vendor_header,
+ * ofp10_vendor_stats_msg, or
+ * ofp11_vendor_stats_msg. */
+ uint32_t subtype; /* From nicira_header, nicira10_stats_msg, or
+ * nicira11_stats_msg. */
+};
+BUILD_ASSERT_DECL(sizeof(struct ofphdrs) == 12);
+
+/* A mapping from OpenFlow headers to OFPRAW_*. */
+struct raw_instance {
+ struct hmap_node hmap_node; /* In 'raw_instance_map'. */
+ struct ofphdrs hdrs; /* Key. */
+ enum ofpraw raw; /* Value. */
+ unsigned int hdrs_len; /* ofphdrs_len(hdrs). */
+};
+
+/* Information about a particular 'enum ofpraw'. */
+struct raw_info {
+ /* All possible instantiations of this OFPRAW_* into OpenFlow headers. */
+ struct raw_instance *instances; /* min_version - max_version + 1 elems. */
+ uint8_t min_version;
+ uint8_t max_version;
+
+ unsigned int min_body;
+ unsigned int extra_multiple;
+ enum ofptype type;
+ const char *name;
+};
+
+/* All understood OpenFlow message types, indexed by their 'struct ofphdrs'. */
+static struct hmap raw_instance_map;
+#include "ofp-msgs.inc"
+
+static ovs_be32 alloc_xid(void);
+
+/* ofphdrs functions. */
+static uint32_t ofphdrs_hash(const struct ofphdrs *);
+static bool ofphdrs_equal(const struct ofphdrs *a, const struct ofphdrs *b);
+static enum ofperr ofphdrs_decode(struct ofphdrs *,
+ const struct ofp_header *oh, size_t length);
+static void ofphdrs_decode_assert(struct ofphdrs *,
+ const struct ofp_header *oh, size_t length);
+size_t ofphdrs_len(const struct ofphdrs *);
+
+static const struct raw_info *raw_info_get(enum ofpraw);
+static struct raw_instance *raw_instance_get(const struct raw_info *,
+ uint8_t version);
+
+static enum ofperr ofpraw_from_ofphdrs(enum ofpraw *, const struct ofphdrs *);
+\f
+/* Returns a transaction ID to use for an outgoing OpenFlow message. */
+static ovs_be32
+alloc_xid(void)
+{
+ static uint32_t next_xid = 1;
+ return htonl(next_xid++);
+}
+\f
+static uint32_t
+ofphdrs_hash(const struct ofphdrs *hdrs)
+{
+ BUILD_ASSERT_DECL(sizeof *hdrs == 12);
+ return hash_words((const uint32_t *) hdrs, 3, 0);
+}
+
+static bool
+ofphdrs_equal(const struct ofphdrs *a, const struct ofphdrs *b)
+{
+ return !memcmp(a, b, sizeof *a);
+}
+
+static void
+log_bad_vendor(uint32_t vendor)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+ VLOG_WARN_RL(&rl, "OpenFlow message has unknown vendor %#"PRIx32, vendor);
+}
+
+static enum ofperr
+ofphdrs_decode(struct ofphdrs *hdrs,
+ const struct ofp_header *oh, size_t length)
+{
+ memset(hdrs, 0, sizeof *hdrs);
+ if (length < sizeof *oh) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ /* Get base message version and type (OFPT_*). */
+ hdrs->version = oh->version;
+ hdrs->type = oh->type;
+
+ if (hdrs->type == OFPT_VENDOR) {
+ /* Get vendor. */
+ const struct ofp_vendor_header *ovh;
+
+ if (length < sizeof *ovh) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ ovh = (const struct ofp_vendor_header *) oh;
+ hdrs->vendor = ntohl(ovh->vendor);
+ if (hdrs->vendor == NX_VENDOR_ID) {
+ /* Get Nicira message subtype (NXT_*). */
+ const struct nicira_header *nh;
+
+ if (length < sizeof *nh) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ nh = (const struct nicira_header *) oh;
+ hdrs->subtype = ntohl(nh->subtype);
+ } else {
+ log_bad_vendor(hdrs->vendor);
+ return OFPERR_OFPBRC_BAD_VENDOR;
+ }
+ } else if (hdrs->version == OFP10_VERSION
+ && (hdrs->type == OFPT10_STATS_REQUEST ||
+ hdrs->type == OFPT10_STATS_REPLY)) {
+ const struct ofp10_stats_msg *osm;
+
+ /* Get statistic type (OFPST_*). */
+ if (length < sizeof *osm) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ osm = (const struct ofp10_stats_msg *) oh;
+ hdrs->stat = ntohs(osm->type);
+
+ if (hdrs->stat == OFPST_VENDOR) {
+ /* Get vendor. */
+ const struct ofp10_vendor_stats_msg *ovsm;
+
+ if (length < sizeof *ovsm) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ ovsm = (const struct ofp10_vendor_stats_msg *) oh;
+ hdrs->vendor = ntohl(ovsm->vendor);
+ if (hdrs->vendor == NX_VENDOR_ID) {
+ /* Get Nicira statistic type (NXST_*). */
+ const struct nicira10_stats_msg *nsm;
+
+ if (length < sizeof *nsm) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ nsm = (const struct nicira10_stats_msg *) oh;
+ hdrs->subtype = ntohl(nsm->subtype);
+ } else {
+ log_bad_vendor(hdrs->vendor);
+ return OFPERR_OFPBRC_BAD_VENDOR;
+ }
+ }
+ } else if (hdrs->version != OFP10_VERSION
+ && (hdrs->type == OFPT11_STATS_REQUEST ||
+ hdrs->type == OFPT11_STATS_REPLY)) {
+ const struct ofp11_stats_msg *osm;
+
+ /* Get statistic type (OFPST_*). */
+ if (length < sizeof *osm) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ osm = (const struct ofp11_stats_msg *) oh;
+ hdrs->stat = ntohs(osm->type);
+
+ if (hdrs->stat == OFPST_VENDOR) {
+ /* Get vendor. */
+ const struct ofp11_vendor_stats_msg *ovsm;
+
+ if (length < sizeof *ovsm) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ ovsm = (const struct ofp11_vendor_stats_msg *) oh;
+ hdrs->vendor = ntohl(ovsm->vendor);
+ if (hdrs->vendor == NX_VENDOR_ID) {
+ /* Get Nicira statistic type (NXST_*). */
+ const struct nicira11_stats_msg *nsm;
+
+ if (length < sizeof *nsm) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ nsm = (const struct nicira11_stats_msg *) oh;
+ hdrs->subtype = ntohl(nsm->subtype);
+ } else {
+ log_bad_vendor(hdrs->vendor);
+ return OFPERR_OFPBRC_BAD_VENDOR;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void
+ofphdrs_decode_assert(struct ofphdrs *hdrs,
+ const struct ofp_header *oh, size_t length)
+{
+ enum ofperr error = ofphdrs_decode(hdrs, oh, length);
+ assert(!error);
+}
+
+static bool
+ofphdrs_is_stat(const struct ofphdrs *hdrs)
+{
+ switch ((enum ofp_version) hdrs->version) {
+ case OFP10_VERSION:
+ return (hdrs->type == OFPT10_STATS_REQUEST ||
+ hdrs->type == OFPT10_STATS_REPLY);
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ return (hdrs->type == OFPT11_STATS_REQUEST ||
+ hdrs->type == OFPT11_STATS_REPLY);
+ }
+
+ return false;
+}
+
+size_t
+ofphdrs_len(const struct ofphdrs *hdrs)
+{
+ if (hdrs->type == OFPT_VENDOR) {
+ return sizeof(struct nicira_header);
+ }
+
+ switch ((enum ofp_version) hdrs->version) {
+ case OFP10_VERSION:
+ if (hdrs->type == OFPT10_STATS_REQUEST ||
+ hdrs->type == OFPT10_STATS_REPLY) {
+ return (hdrs->stat == OFPST_VENDOR
+ ? sizeof(struct nicira10_stats_msg)
+ : sizeof(struct ofp10_stats_msg));
+ }
+ break;
+
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ if (hdrs->type == OFPT11_STATS_REQUEST ||
+ hdrs->type == OFPT11_STATS_REPLY) {
+ return (hdrs->stat == OFPST_VENDOR
+ ? sizeof(struct nicira11_stats_msg)
+ : sizeof(struct ofp11_stats_msg));
+ }
+ break;
+ }
+
+ return sizeof(struct ofp_header);
+}
+\f
+/* Determines the OFPRAW_* type of the OpenFlow message at 'oh', which has
+ * length 'oh->length'. (The caller must ensure that 'oh->length' bytes of
+ * data are readable at 'oh'.) On success, returns 0 and stores the type into
+ * '*raw'. On failure, returns an OFPERR_* error code and zeros '*raw'.
+ *
+ * This function checks that 'oh' is a valid length for its particular type of
+ * message, and returns an error if not. */
+enum ofperr
+ofpraw_decode(enum ofpraw *raw, const struct ofp_header *oh)
+{
+ struct ofpbuf msg;
+
+ ofpbuf_use_const(&msg, oh, ntohs(oh->length));
+ return ofpraw_pull(raw, &msg);
+}
+
+/* Determines the OFPRAW_* type of the OpenFlow message in 'msg', which starts
+ * at 'msg->data' and has length 'msg->size' bytes. On success, returns 0 and
+ * stores the type into '*rawp'. On failure, returns an OFPERR_* error code
+ * and zeros '*rawp'.
+ *
+ * This function checks that the message has a valid length for its particular
+ * type of message, and returns an error if not.
+ *
+ * In addition to setting '*rawp', this function pulls off the OpenFlow header
+ * (including the stats headers, vendor header, and any subtype header) with
+ * ofpbuf_pull(). It also sets 'msg->l2' to the start of the OpenFlow header
+ * and 'msg->l3' just beyond the headers (that is, to the final value of
+ * msg->data). */
+enum ofperr
+ofpraw_pull(enum ofpraw *rawp, struct ofpbuf *msg)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+ const struct raw_instance *instance;
+ const struct raw_info *info;
+ struct ofphdrs hdrs;
+
+ unsigned int min_len;
+ unsigned int len;
+
+ enum ofperr error;
+ enum ofpraw raw;
+
+ /* Set default outputs. */
+ msg->l2 = msg->l3 = msg->data;
+ *rawp = 0;
+
+ len = msg->size;
+ error = ofphdrs_decode(&hdrs, msg->data, len);
+ if (error) {
+ return error;
+ }
+
+ error = ofpraw_from_ofphdrs(&raw, &hdrs);
+ if (error) {
+ return error;
+ }
+
+ info = raw_info_get(raw);
+ instance = raw_instance_get(info, hdrs.version);
+ msg->l2 = ofpbuf_pull(msg, instance->hdrs_len);
+ msg->l3 = msg->data;
+
+ min_len = instance->hdrs_len + info->min_body;
+ switch (info->extra_multiple) {
+ case 0:
+ if (len != min_len) {
+ VLOG_WARN_RL(&rl, "received %s with incorrect length %u (expected "
+ "length %u)", info->name, len, min_len);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ break;
+
+ case 1:
+ if (len < min_len) {
+ VLOG_WARN_RL(&rl, "received %s with incorrect length %u (expected "
+ "length at least %u bytes)",
+ info->name, len, min_len);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ break;
+
+ default:
+ if (len < min_len || (len - min_len) % info->extra_multiple) {
+ VLOG_WARN_RL(&rl, "received %s with incorrect length %u (must be "
+ "exactly %u bytes or longer by an integer multiple "
+ "of %u bytes)",
+ info->name, len, min_len, info->extra_multiple);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ break;
+ }
+
+ *rawp = raw;
+ return 0;
+}
+
+/* Does the same job as ofpraw_pull(), except that it assert-fails if
+ * ofpbuf_pull() would have reported an error. Thus, it's able to use the
+ * return value for the OFPRAW_* message type instead of an error code.
+ *
+ * (It only makes sense to use this function if you previously called
+ * ofpbuf_decode() on the message and thus know that it's OK.) */
+enum ofpraw
+ofpraw_pull_assert(struct ofpbuf *msg)
+{
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = ofpraw_pull(&raw, msg);
+ assert(!error);
+ return raw;
+}
+
+/* Determines the OFPRAW_* type of the OpenFlow message that starts at 'oh' and
+ * has length 'length' bytes. On success, returns 0 and stores the type into
+ * '*rawp'. On failure, returns an OFPERR_* error code and zeros '*rawp'.
+ *
+ * Unlike other functions for decoding message types, this one is not picky
+ * about message length. For example, it will successfully decode a message
+ * whose body is shorter than the minimum length for a message of its type.
+ * Thus, this is the correct function to use for decoding the type of a message
+ * that might have been truncated, such as the payload of an OpenFlow error
+ * message (which is allowed to be truncated to 64 bytes). */
+enum ofperr
+ofpraw_decode_partial(enum ofpraw *raw,
+ const struct ofp_header *oh, size_t length)
+{
+ struct ofphdrs hdrs;
+ enum ofperr error;
+
+ error = ofphdrs_decode(&hdrs, oh, length);
+ if (!error) {
+ error = ofpraw_from_ofphdrs(raw, &hdrs);
+ }
+
+ if (error) {
+ *raw = 0;
+ }
+ return error;
+}
+\f
+/* Encoding messages using OFPRAW_* values. */
+
+static void ofpraw_put__(enum ofpraw, uint8_t version, ovs_be32 xid,
+ size_t extra_tailroom, struct ofpbuf *);
+
+/* Allocates and returns a new ofpbuf that contains an OpenFlow header for
+ * 'raw' with OpenFlow version 'version' and a fresh OpenFlow transaction ID.
+ * The ofpbuf has enough tailroom for the minimum body length of 'raw', plus
+ * 'extra_tailroom' additional bytes.
+ *
+ * Each 'raw' value is valid only for certain OpenFlow versions. The caller
+ * must specify a valid (raw, version) pair.
+ *
+ * In the returned ofpbuf, 'l2' points to the beginning of the OpenFlow header
+ * and 'l3' points just after it, to where the message's body will start. The
+ * caller must actually allocate the body into the space reserved for it,
+ * e.g. with ofpbuf_put_uninit().
+ *
+ * The caller owns the returned ofpbuf and must free it when it is no longer
+ * needed, e.g. with ofpbuf_delete(). */
+struct ofpbuf *
+ofpraw_alloc(enum ofpraw raw, uint8_t version, size_t extra_tailroom)
+{
+ return ofpraw_alloc_xid(raw, version, alloc_xid(), extra_tailroom);
+}
+
+/* Same as ofpraw_alloc() but the caller provides the transaction ID. */
+struct ofpbuf *
+ofpraw_alloc_xid(enum ofpraw raw, uint8_t version, ovs_be32 xid,
+ size_t extra_tailroom)
+{
+ struct ofpbuf *buf = ofpbuf_new(0);
+ ofpraw_put__(raw, version, xid, extra_tailroom, buf);
+ return buf;
+}
+
+/* Same as ofpraw_alloc(), but obtains the OpenFlow version and transaction ID
+ * from 'request->version' and 'request->xid', respectively.
+ *
+ * Even though the version comes from 'request->version', the caller must still
+ * know what it is doing, by specifying a valid pairing of 'raw' and
+ * 'request->version', just like ofpraw_alloc(). */
+struct ofpbuf *
+ofpraw_alloc_reply(enum ofpraw raw, const struct ofp_header *request,
+ size_t extra_tailroom)
+{
+ return ofpraw_alloc_xid(raw, request->version, request->xid,
+ extra_tailroom);
+}
+
+/* Allocates and returns a new ofpbuf that contains an OpenFlow header that is
+ * a stats reply to the stats request in 'request', using the same OpenFlow
+ * version and transaction ID as 'request'. The ofpbuf has enough tailroom for
+ * the stats reply's minimum body length, plus 'extra_tailroom' additional
+ * bytes.
+ *
+ * 'request' must be a stats request, that is, an OFPRAW_OFPST* or OFPRAW_NXST*
+ * value. Every stats request has a corresponding reply, so the (raw, version)
+ * pairing pitfalls of the other ofpraw_alloc_*() functions don't apply here.
+ *
+ * In the returned ofpbuf, 'l2' points to the beginning of the OpenFlow header
+ * and 'l3' points just after it, to where the message's body will start. The
+ * caller must actually allocate the body into the space reserved for it,
+ * e.g. with ofpbuf_put_uninit().
+ *
+ * The caller owns the returned ofpbuf and must free it when it is no longer
+ * needed, e.g. with ofpbuf_delete(). */
+struct ofpbuf *
+ofpraw_alloc_stats_reply(const struct ofp_header *request,
+ size_t extra_tailroom)
+{
+ enum ofpraw request_raw;
+ enum ofpraw reply_raw;
+ enum ofperr error;
+
+ error = ofpraw_decode_partial(&request_raw, request,
+ ntohs(request->length));
+ assert(!error);
+
+ reply_raw = ofpraw_stats_request_to_reply(request_raw, request->version);
+ assert(reply_raw);
+
+ return ofpraw_alloc_reply(reply_raw, request, extra_tailroom);
+}
+
+/* Appends to 'buf' an OpenFlow header for 'raw' with OpenFlow version
+ * 'version' and a fresh OpenFlow transaction ID. Preallocates enough tailroom
+ * in 'buf' for the minimum body length of 'raw', plus 'extra_tailroom'
+ * additional bytes.
+ *
+ * Each 'raw' value is valid only for certain OpenFlow versions. The caller
+ * must specify a valid (raw, version) pair.
+ *
+ * Upon return, 'buf->l2' points to the beginning of the OpenFlow header and
+ * 'buf->l3' points just after it, to where the message's body will start. The
+ * caller must actually allocating the body into the space reserved for it,
+ * e.g. with ofpbuf_put_uninit(). */
+void
+ofpraw_put(enum ofpraw raw, uint8_t version, struct ofpbuf *buf)
+{
+ ofpraw_put__(raw, version, alloc_xid(), 0, buf);
+}
+
+/* Same as ofpraw_put() but the caller provides the transaction ID. */
+void
+ofpraw_put_xid(enum ofpraw raw, uint8_t version, ovs_be32 xid,
+ struct ofpbuf *buf)
+{
+ ofpraw_put__(raw, version, xid, 0, buf);
+}
+
+/* Same as ofpraw_put(), but obtains the OpenFlow version and transaction ID
+ * from 'request->version' and 'request->xid', respectively.
+ *
+ * Even though the version comes from 'request->version', the caller must still
+ * know what it is doing, by specifying a valid pairing of 'raw' and
+ * 'request->version', just like ofpraw_put(). */
+void
+ofpraw_put_reply(enum ofpraw raw, const struct ofp_header *request,
+ struct ofpbuf *buf)
+{
+ ofpraw_put__(raw, request->version, request->xid, 0, buf);
+}
+
+/* Appends to 'buf' an OpenFlow header that is a stats reply to the stats
+ * request in 'request', using the same OpenFlow version and transaction ID as
+ * 'request'. Preallocate enough tailroom in 'buf for the stats reply's
+ * minimum body length, plus 'extra_tailroom' additional bytes.
+ *
+ * 'request' must be a stats request, that is, an OFPRAW_OFPST* or OFPRAW_NXST*
+ * value. Every stats request has a corresponding reply, so the (raw, version)
+ * pairing pitfalls of the other ofpraw_alloc_*() functions don't apply here.
+ *
+ * In the returned ofpbuf, 'l2' points to the beginning of the OpenFlow header
+ * and 'l3' points just after it, to where the message's body will start. The
+ * caller must actually allocate the body into the space reserved for it,
+ * e.g. with ofpbuf_put_uninit().
+ *
+ * The caller owns the returned ofpbuf and must free it when it is no longer
+ * needed, e.g. with ofpbuf_delete(). */
+void
+ofpraw_put_stats_reply(const struct ofp_header *request, struct ofpbuf *buf)
+{
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = ofpraw_decode_partial(&raw, request, ntohs(request->length));
+ assert(!error);
+
+ raw = ofpraw_stats_request_to_reply(raw, request->version);
+ assert(raw);
+
+ ofpraw_put__(raw, request->version, request->xid, 0, buf);
+}
+
+static void
+ofpraw_put__(enum ofpraw raw, uint8_t version, ovs_be32 xid,
+ size_t extra_tailroom, struct ofpbuf *buf)
+{
+ const struct raw_info *info = raw_info_get(raw);
+ const struct raw_instance *instance = raw_instance_get(info, version);
+ const struct ofphdrs *hdrs = &instance->hdrs;
+ struct ofp_header *oh;
+
+ ofpbuf_prealloc_tailroom(buf, (instance->hdrs_len + info->min_body
+ + extra_tailroom));
+ buf->l2 = ofpbuf_put_uninit(buf, instance->hdrs_len);
+ buf->l3 = ofpbuf_tail(buf);
+
+ oh = buf->l2;
+ oh->version = version;
+ oh->type = hdrs->type;
+ oh->length = htons(buf->size);
+ oh->xid = xid;
+
+ if (hdrs->type == OFPT_VENDOR) {
+ struct nicira_header *nh = buf->l2;
+
+ assert(hdrs->vendor == NX_VENDOR_ID);
+ nh->vendor = htonl(hdrs->vendor);
+ nh->subtype = htonl(hdrs->subtype);
+ } else if (version == OFP10_VERSION
+ && (hdrs->type == OFPT10_STATS_REQUEST ||
+ hdrs->type == OFPT10_STATS_REPLY)) {
+ struct ofp10_stats_msg *osm = buf->l2;
+
+ osm->type = htons(hdrs->stat);
+ osm->flags = htons(0);
+
+ if (hdrs->stat == OFPST_VENDOR) {
+ struct ofp10_vendor_stats_msg *ovsm = buf->l2;
+
+ ovsm->vendor = htonl(hdrs->vendor);
+ if (hdrs->vendor == NX_VENDOR_ID) {
+ struct nicira10_stats_msg *nsm = buf->l2;
+
+ nsm->subtype = htonl(hdrs->subtype);
+ memset(nsm->pad, 0, sizeof nsm->pad);
+ } else {
+ NOT_REACHED();
+ }
+ }
+ } else if (version != OFP10_VERSION
+ && (hdrs->type == OFPT11_STATS_REQUEST ||
+ hdrs->type == OFPT11_STATS_REPLY)) {
+ struct ofp11_stats_msg *osm = buf->l2;
+
+ osm->type = htons(hdrs->stat);
+ osm->flags = htons(0);
+ memset(osm->pad, 0, sizeof osm->pad);
+
+ if (hdrs->stat == OFPST_VENDOR) {
+ struct ofp11_vendor_stats_msg *ovsm = buf->l2;
+
+ ovsm->vendor = htonl(hdrs->vendor);
+ if (hdrs->vendor == NX_VENDOR_ID) {
+ struct nicira11_stats_msg *nsm = buf->l2;
+
+ nsm->subtype = htonl(hdrs->subtype);
+ } else {
+ NOT_REACHED();
+ }
+ }
+ }
+}
+\f
+/* Returns 'raw''s name.
+ *
+ * The name is the name used for 'raw' in the OpenFlow specification. For
+ * example, ofpraw_get_name(OFPRAW_OFPT10_FEATURES_REPLY) is
+ * "OFPT_FEATURES_REPLY".
+ *
+ * The caller must not modify or free the returned string. */
+const char *
+ofpraw_get_name(enum ofpraw raw)
+{
+ return raw_info_get(raw)->name;
+}
+
+/* Returns the stats reply that corresponds to 'raw' in the given OpenFlow
+ * 'version'. */
+enum ofpraw
+ofpraw_stats_request_to_reply(enum ofpraw raw, uint8_t version)
+{
+ const struct raw_info *info = raw_info_get(raw);
+ const struct raw_instance *instance = raw_instance_get(info, version);
+ enum ofpraw reply_raw;
+ struct ofphdrs hdrs;
+ enum ofperr error;
+
+ hdrs = instance->hdrs;
+ switch ((enum ofp_version)hdrs.version) {
+ case OFP10_VERSION:
+ assert(hdrs.type == OFPT10_STATS_REQUEST);
+ hdrs.type = OFPT10_STATS_REPLY;
+ break;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ assert(hdrs.type == OFPT11_STATS_REQUEST);
+ hdrs.type = OFPT11_STATS_REPLY;
+ break;
+ default:
+ NOT_REACHED();
+ }
+
+ error = ofpraw_from_ofphdrs(&reply_raw, &hdrs);
+ assert(!error);
+
+ return reply_raw;
+}
+\f
+/* Determines the OFPTYPE_* type of the OpenFlow message at 'oh', which has
+ * length 'oh->length'. (The caller must ensure that 'oh->length' bytes of
+ * data are readable at 'oh'.) On success, returns 0 and stores the type into
+ * '*typep'. On failure, returns an OFPERR_* error code and zeros '*typep'.
+ *
+ * This function checks that 'oh' is a valid length for its particular type of
+ * message, and returns an error if not. */
+enum ofperr
+ofptype_decode(enum ofptype *typep, const struct ofp_header *oh)
+{
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = ofpraw_decode(&raw, oh);
+ *typep = error ? 0 : ofptype_from_ofpraw(raw);
+ return error;
+}
+
+/* Determines the OFPTYPE_* type of the OpenFlow message in 'msg', which starts
+ * at 'msg->data' and has length 'msg->size' bytes. On success, returns 0 and
+ * stores the type into '*typep'. On failure, returns an OFPERR_* error code
+ * and zeros '*typep'.
+ *
+ * This function checks that the message has a valid length for its particular
+ * type of message, and returns an error if not.
+ *
+ * In addition to setting '*typep', this function pulls off the OpenFlow header
+ * (including the stats headers, vendor header, and any subtype header) with
+ * ofpbuf_pull(). It also sets 'msg->l2' to the start of the OpenFlow header
+ * and 'msg->l3' just beyond the headers (that is, to the final value of
+ * msg->data). */
+enum ofperr
+ofptype_pull(enum ofptype *typep, struct ofpbuf *buf)
+{
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = ofpraw_pull(&raw, buf);
+ *typep = error ? 0 : ofptype_from_ofpraw(raw);
+ return error;
+}
+
+/* Returns the OFPTYPE_* type that corresponds to 'raw'.
+ *
+ * (This is a one-way trip, because the mapping from ofpraw to ofptype is
+ * many-to-one.) */
+enum ofptype
+ofptype_from_ofpraw(enum ofpraw raw)
+{
+ return raw_info_get(raw)->type;
+}
+\f
+/* Updates the 'length' field of the OpenFlow message in 'buf' to
+ * 'buf->size'. */
+void
+ofpmsg_update_length(struct ofpbuf *buf)
+{
+ struct ofp_header *oh = ofpbuf_at_assert(buf, 0, sizeof *oh);
+ oh->length = htons(buf->size);
+}
+
+/* Returns just past the Openflow header (including the stats headers, vendor
+ * header, and any subtype header) in 'oh'. */
+const void *
+ofpmsg_body(const struct ofp_header *oh)
+{
+ struct ofphdrs hdrs;
+
+ ofphdrs_decode_assert(&hdrs, oh, ntohs(oh->length));
+ return (const uint8_t *) oh + ofphdrs_len(&hdrs);
+}
+\f
+static ovs_be16 *ofpmp_flags__(const struct ofp_header *);
+
+/* Initializes 'replies' as a new list of stats messages that reply to
+ * 'request', which must be a stats request message. Initially the list will
+ * consist of only a single reply part without any body. The caller should
+ * use calls to the other ofpmp_*() functions to add to the body and split the
+ * message into multiple parts, if necessary. */
+void
+ofpmp_init(struct list *replies, const struct ofp_header *request)
+{
+ struct ofpbuf *msg;
+
+ list_init(replies);
+
+ msg = ofpraw_alloc_stats_reply(request, 1000);
+ list_push_back(replies, &msg->list_node);
+}
+
+/* Prepares to append up to 'len' bytes to the series of statistics replies in
+ * 'replies', which should have been initialized with ofpmp_init(), if
+ * necessary adding a new reply to the list.
+ *
+ * Returns an ofpbuf with at least 'len' bytes of tailroom. The 'len' bytes
+ * have not actually been allocated, so the caller must do so with
+ * e.g. ofpbuf_put_uninit(). */
+struct ofpbuf *
+ofpmp_reserve(struct list *replies, size_t len)
+{
+ struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+
+ if (msg->size + len <= UINT16_MAX) {
+ ofpbuf_prealloc_tailroom(msg, len);
+ return msg;
+ } else {
+ unsigned int hdrs_len;
+ struct ofpbuf *next;
+ struct ofphdrs hdrs;
+
+ ofphdrs_decode_assert(&hdrs, msg->data, msg->size);
+ hdrs_len = ofphdrs_len(&hdrs);
+
+ next = ofpbuf_new(MAX(1024, hdrs_len + len));
+ ofpbuf_put(next, msg->data, hdrs_len);
+ list_push_back(replies, &next->list_node);
+
+ *ofpmp_flags__(msg->data) |= htons(OFPSF_REPLY_MORE);
+
+ return next;
+ }
+}
+
+/* Appends 'len' bytes to the series of statistics replies in 'replies', and
+ * returns the first byte. */
+void *
+ofpmp_append(struct list *replies, size_t len)
+{
+ return ofpbuf_put_uninit(ofpmp_reserve(replies, len), len);
+}
+
+/* Sometimes, when composing stats replies, it's difficult to predict how long
+ * an individual reply chunk will be before actually encoding it into the reply
+ * buffer. This function allows easy handling of this case: just encode the
+ * reply, then use this function to break the message into two pieces if it
+ * exceeds the OpenFlow message limit.
+ *
+ * In detail, if the final stats message in 'replies' is too long for OpenFlow,
+ * this function breaks it into two separate stats replies, the first one with
+ * the first 'start_ofs' bytes, the second one containing the bytes from that
+ * offset onward. */
+void
+ofpmp_postappend(struct list *replies, size_t start_ofs)
+{
+ struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+
+ assert(start_ofs <= UINT16_MAX);
+ if (msg->size > UINT16_MAX) {
+ size_t len = msg->size - start_ofs;
+ memcpy(ofpmp_append(replies, len),
+ (const uint8_t *) msg->data + start_ofs, len);
+ msg->size = start_ofs;
+ }
+}
+
+static ovs_be16 *
+ofpmp_flags__(const struct ofp_header *oh)
+{
+ switch ((enum ofp_version)oh->version) {
+ case OFP10_VERSION:
+ return &((struct ofp10_stats_msg *) oh)->flags;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ return &((struct ofp11_stats_msg *) oh)->flags;
+ default:
+ NOT_REACHED();
+ }
+}
+
+/* Returns the OFPSF_* flags found in the OpenFlow stats header of 'oh', which
+ * must be an OpenFlow stats request or reply.
+ *
+ * (OFPSF_REPLY_MORE is the only defined flag.) */
+uint16_t
+ofpmp_flags(const struct ofp_header *oh)
+{
+ return ntohs(*ofpmp_flags__(oh));
+}
+
+/* Returns true if the OFPSF_REPLY_MORE flag is set in the OpenFlow stats
+ * header of 'oh', which must be an OpenFlow stats request or reply, false if
+ * it is not set. */
+bool
+ofpmp_more(const struct ofp_header *oh)
+{
+ return (ofpmp_flags(oh) & OFPSF_REPLY_MORE) != 0;
+}
+\f
+static void ofpmsgs_init(void);
+
+static const struct raw_info *
+raw_info_get(enum ofpraw raw)
+{
+ ofpmsgs_init();
+
+ assert(raw < ARRAY_SIZE(raw_infos));
+ return &raw_infos[raw];
+}
+
+static struct raw_instance *
+raw_instance_get(const struct raw_info *info, uint8_t version)
+{
+ assert(version >= info->min_version && version <= info->max_version);
+ return &info->instances[version - info->min_version];
+}
+
+static enum ofperr
+ofpraw_from_ofphdrs(enum ofpraw *raw, const struct ofphdrs *hdrs)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+ struct raw_instance *raw_hdrs;
+ uint32_t hash;
+
+ ofpmsgs_init();
+
+ hash = ofphdrs_hash(hdrs);
+ HMAP_FOR_EACH_WITH_HASH (raw_hdrs, hmap_node, hash, &raw_instance_map) {
+ if (ofphdrs_equal(hdrs, &raw_hdrs->hdrs)) {
+ *raw = raw_hdrs->raw;
+ return 0;
+ }
+ }
+
+ if (!VLOG_DROP_WARN(&rl)) {
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_format(&s, "version %"PRIu8", type %"PRIu8,
+ hdrs->version, hdrs->type);
+ if (ofphdrs_is_stat(hdrs)) {
+ ds_put_format(&s, ", stat %"PRIu16, hdrs->stat);
+ }
+ if (hdrs->vendor) {
+ ds_put_format(&s, ", vendor 0x%"PRIx32", subtype %"PRIu32,
+ hdrs->vendor, hdrs->subtype);
+ }
+ VLOG_WARN("unknown OpenFlow message (%s)", ds_cstr(&s));
+ ds_destroy(&s);
+ }
+
+ return (hdrs->vendor ? OFPERR_OFPBRC_BAD_SUBTYPE
+ : ofphdrs_is_stat(hdrs) ? OFPERR_OFPBRC_BAD_STAT
+ : OFPERR_OFPBRC_BAD_TYPE);
+}
+
+static void
+ofpmsgs_init(void)
+{
+ const struct raw_info *info;
+
+ if (raw_instance_map.buckets) {
+ return;
+ }
+
+ hmap_init(&raw_instance_map);
+ for (info = raw_infos; info < &raw_infos[ARRAY_SIZE(raw_infos)]; info++)
+ {
+ int n_instances = info->max_version - info->min_version + 1;
+ struct raw_instance *inst;
+
+ for (inst = info->instances;
+ inst < &info->instances[n_instances];
+ inst++) {
+ inst->hdrs_len = ofphdrs_len(&inst->hdrs);
+ hmap_insert(&raw_instance_map, &inst->hmap_node,
+ ofphdrs_hash(&inst->hdrs));
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFP_MSGS_H
+#define OFP_MSGS_H 1
+
+/* OpenFlow message headers abstraction.
+ *
+ * OpenFlow headers are unnecessarily complicated:
+ *
+ * - Some messages with the same meaning were renumbered between 1.0 and 1.1.
+ *
+ * - "Statistics" (aka multipart) messages have a different format from other
+ * messages.
+ *
+ * - The 1.0 header for statistics messages is an odd number of 32-bit words
+ * long, leaving 64-bit quantities in the body misaligned. The 1.1 header
+ * for statistics added a padding word to fix this misalignment, although
+ * many statistic message bodies did not change.
+ *
+ * - Vendor-defined messages have an additional header but no standard way to
+ * distinguish individual types of message within a given vendor.
+ *
+ * This file attempts to abstract out the differences between the various forms
+ * of headers.
+ */
+
+#include "openvswitch/types.h"
+#include "ofp-errors.h"
+#include "util.h"
+
+struct list;
+\f
+/* Raw identifiers for OpenFlow messages.
+ *
+ * Some OpenFlow messages with similar meanings have multiple variants across
+ * OpenFlow versions or vendor extensions. Each variant has a different
+ * OFPRAW_* enumeration constant. More specifically, if two messages have
+ * different types, different numbers, or different arguments, then they must
+ * have different OFPRAW_* values.
+ *
+ * The comments here must follow a stylized form because the "extract-ofp-msgs"
+ * program parses them at build time to generate data tables. The syntax of
+ * each comment is:
+ *
+ * type versions (number): arguments.
+ *
+ * where the syntax of each part is:
+ *
+ * - type: One of OFPT (standard OpenFlow message), OFPST (standard OpenFlow
+ * statistics message), NXT (Nicira extension message), or NXST (Nicira
+ * extension statistics message).
+ *
+ * As new vendors implement extensions it will make sense to expand the
+ * dictionary of possible types.
+ *
+ * - versions: The OpenFlow version or versions in which this message is
+ * supported, e.g. "1.0" or "1.1" or "1.0+".
+ *
+ * - number:
+ * For OFPT, the 'type' in struct ofp_header.
+ * For OFPST, the 'type' in struct ofp_stats_msg or ofp11_stats_msg.
+ * For NXT, the 'subtype' in struct nicira_header.
+ * For NXST, the 'subtype' in struct nicira10_stats_msg or
+ * nicira11_stats_msg.
+ *
+ * - arguments: The types of data that follow the OpenFlow headers (the
+ * message "body"). This can be "void" if the message has no body.
+ * Otherwise, it should be a comma-separated sequence of C types. The
+ * last type in the sequence can end with [] if the body ends in a
+ * variable-length sequence.
+ *
+ * The arguments are used to validate the lengths of messages when a
+ * header is parsed. Any message whose length isn't valid as a length of
+ * the specified types will be rejected with OFPERR_OFPBRC_BAD_LEN.
+ *
+ * A few OpenFlow messages, such as OFPT_PACKET_IN, intentionally end with
+ * only part of a structure, up to some specified member. The syntax "up
+ * to <member>" indicates this, e.g. "struct ofp11_packet_in up to data".
+ */
+enum ofpraw {
+/* Standard messages. */
+
+ /* OFPT 1.0+ (0): uint8_t[]. */
+ OFPRAW_OFPT_HELLO,
+
+ /* OFPT 1.0+ (1): struct ofp_error_msg, uint8_t[]. */
+ OFPRAW_OFPT_ERROR,
+
+ /* OFPT 1.0+ (2): uint8_t[]. */
+ OFPRAW_OFPT_ECHO_REQUEST,
+
+ /* OFPT 1.0+ (3): uint8_t[]. */
+ OFPRAW_OFPT_ECHO_REPLY,
+
+ /* OFPT 1.0+ (5): void. */
+ OFPRAW_OFPT_FEATURES_REQUEST,
+
+ /* OFPT 1.0 (6): struct ofp_switch_features, struct ofp10_phy_port[]. */
+ OFPRAW_OFPT10_FEATURES_REPLY,
+ /* OFPT 1.1+ (6): struct ofp_switch_features, struct ofp11_port[]. */
+ OFPRAW_OFPT11_FEATURES_REPLY,
+
+ /* OFPT 1.0+ (7): void. */
+ OFPRAW_OFPT_GET_CONFIG_REQUEST,
+
+ /* OFPT 1.0+ (8): struct ofp_switch_config. */
+ OFPRAW_OFPT_GET_CONFIG_REPLY,
+
+ /* OFPT 1.0+ (9): struct ofp_switch_config. */
+ OFPRAW_OFPT_SET_CONFIG,
+
+ /* OFPT 1.0 (10): struct ofp_packet_in up to data, uint8_t[]. */
+ OFPRAW_OFPT10_PACKET_IN,
+ /* OFPT 1.1 (10): struct ofp11_packet_in up to data, uint8_t[]. */
+ OFPRAW_OFPT11_PACKET_IN,
+ /* OFPT 1.2 (10): struct ofp12_packet_in, uint8_t[]. */
+ OFPRAW_OFPT12_PACKET_IN,
+ /* NXT 1.0+ (17): struct nx_packet_in, uint8_t[]. */
+ OFPRAW_NXT_PACKET_IN,
+
+ /* OFPT 1.0 (11): struct ofp_flow_removed. */
+ OFPRAW_OFPT10_FLOW_REMOVED,
+ /* OFPT 1.1+ (11): struct ofp11_flow_removed, uint8_t[8][]. */
+ OFPRAW_OFPT11_FLOW_REMOVED,
+ /* NXT 1.0+ (14): struct nx_flow_removed, uint8_t[8][]. */
+ OFPRAW_NXT_FLOW_REMOVED,
+
+ /* OFPT 1.0 (12): struct ofp_port_status, struct ofp10_phy_port. */
+ OFPRAW_OFPT10_PORT_STATUS,
+ /* OFPT 1.1+ (12): struct ofp_port_status, struct ofp11_port. */
+ OFPRAW_OFPT11_PORT_STATUS,
+
+ /* OFPT 1.0 (13): struct ofp_packet_out, uint8_t[]. */
+ OFPRAW_OFPT10_PACKET_OUT,
+ /* OFPT 1.1+ (13): struct ofp11_packet_out, uint8_t[]. */
+ OFPRAW_OFPT11_PACKET_OUT,
+
+ /* OFPT 1.0 (14): struct ofp10_flow_mod, struct ofp_action_header[]. */
+ OFPRAW_OFPT10_FLOW_MOD,
+ /* OFPT 1.1+ (14): struct ofp11_flow_mod, struct ofp11_instruction[]. */
+ OFPRAW_OFPT11_FLOW_MOD,
+ /* NXT 1.0+ (13): struct nx_flow_mod, uint8_t[8][]. */
+ OFPRAW_NXT_FLOW_MOD,
+
+ /* OFPT 1.0 (15): struct ofp10_port_mod. */
+ OFPRAW_OFPT10_PORT_MOD,
+ /* OFPT 1.1+ (16): struct ofp11_port_mod. */
+ OFPRAW_OFPT11_PORT_MOD,
+
+ /* OFPT 1.0 (18): void. */
+ OFPRAW_OFPT10_BARRIER_REQUEST,
+ /* OFPT 1.1+ (20): void. */
+ OFPRAW_OFPT11_BARRIER_REQUEST,
+
+ /* OFPT 1.0 (19): void. */
+ OFPRAW_OFPT10_BARRIER_REPLY,
+ /* OFPT 1.1+ (21): void. */
+ OFPRAW_OFPT11_BARRIER_REPLY,
+
+/* Standard statistics. */
+
+ /* OFPST 1.0+ (0): void. */
+ OFPRAW_OFPST_DESC_REQUEST,
+
+ /* OFPST 1.0+ (0): struct ofp_desc_stats. */
+ OFPRAW_OFPST_DESC_REPLY,
+
+ /* OFPST 1.0 (1): struct ofp10_flow_stats_request. */
+ OFPRAW_OFPST10_FLOW_REQUEST,
+ /* OFPST 1.1+ (1): struct ofp11_flow_stats_request, uint8_t[8][]. */
+ OFPRAW_OFPST11_FLOW_REQUEST,
+ /* NXST 1.0 (0): struct nx_flow_stats_request, uint8_t[8][]. */
+ OFPRAW_NXST_FLOW_REQUEST,
+
+ /* OFPST 1.0 (1): uint8_t[]. */
+ OFPRAW_OFPST10_FLOW_REPLY,
+ /* OFPST 1.1+ (1): uint8_t[]. */
+ OFPRAW_OFPST11_FLOW_REPLY,
+ /* NXST 1.0 (0): uint8_t[]. */
+ OFPRAW_NXST_FLOW_REPLY,
+
+ /* OFPST 1.0 (2): struct ofp10_flow_stats_request. */
+ OFPRAW_OFPST10_AGGREGATE_REQUEST,
+ /* OFPST 1.1+ (2): struct ofp11_flow_stats_request, uint8_t[8][]. */
+ OFPRAW_OFPST11_AGGREGATE_REQUEST,
+ /* NXST 1.0 (1): struct nx_flow_stats_request, uint8_t[8][]. */
+ OFPRAW_NXST_AGGREGATE_REQUEST,
+
+ /* OFPST 1.0+ (2): struct ofp_aggregate_stats_reply. */
+ OFPRAW_OFPST_AGGREGATE_REPLY,
+ /* NXST 1.0 (1): struct ofp_aggregate_stats_reply. */
+ OFPRAW_NXST_AGGREGATE_REPLY,
+
+ /* OFPST 1.0-1.2 (3): void. */
+ OFPRAW_OFPST_TABLE_REQUEST,
+
+ /* OFPST 1.0 (3): struct ofp10_table_stats[]. */
+ OFPRAW_OFPST10_TABLE_REPLY,
+ /* OFPST 1.1 (3): struct ofp11_table_stats[]. */
+ OFPRAW_OFPST11_TABLE_REPLY,
+ /* OFPST 1.2 (3): struct ofp12_table_stats[]. */
+ OFPRAW_OFPST12_TABLE_REPLY,
+
+ /* OFPST 1.0 (4): struct ofp10_port_stats_request. */
+ OFPRAW_OFPST_PORT_REQUEST,
+
+ /* OFPST 1.0 (4): struct ofp10_port_stats[]. */
+ OFPRAW_OFPST_PORT_REPLY,
+
+ /* OFPST 1.0 (5): struct ofp10_queue_stats_request. */
+ OFPRAW_OFPST_QUEUE_REQUEST,
+
+ /* OFPST 1.0 (5): struct ofp10_queue_stats[]. */
+ OFPRAW_OFPST_QUEUE_REPLY,
+
+ /* OFPST 1.0 (13): void. */
+ OFPRAW_OFPST_PORT_DESC_REQUEST,
+
+ /* OFPST 1.0 (13): struct ofp10_phy_port[]. */
+ OFPRAW_OFPST_PORT_DESC_REPLY,
+
+/* Nicira extension messages.
+ *
+ * Nicira extensions that correspond to standard OpenFlow messages are listed
+ * alongside the standard versions above. */
+
+ /* NXT 1.0+ (10): struct nx_role_request. */
+ OFPRAW_NXT_ROLE_REQUEST,
+
+ /* NXT 1.0+ (11): struct nx_role_request. */
+ OFPRAW_NXT_ROLE_REPLY,
+
+ /* NXT 1.0+ (12): struct nx_set_flow_format. */
+ OFPRAW_NXT_SET_FLOW_FORMAT,
+
+ /* NXT 1.0+ (15): struct nx_flow_mod_table_id. */
+ OFPRAW_NXT_FLOW_MOD_TABLE_ID,
+
+ /* NXT 1.0+ (16): struct nx_set_packet_in_format. */
+ OFPRAW_NXT_SET_PACKET_IN_FORMAT,
+
+ /* NXT 1.0+ (18): void. */
+ OFPRAW_NXT_FLOW_AGE,
+
+ /* NXT 1.0+ (19): struct nx_async_config. */
+ OFPRAW_NXT_SET_ASYNC_CONFIG,
+
+ /* NXT 1.0+ (20): struct nx_controller_id. */
+ OFPRAW_NXT_SET_CONTROLLER_ID,
+
+ /* NXT 1.0+ (21): struct nx_flow_monitor_cancel. */
+ OFPRAW_NXT_FLOW_MONITOR_CANCEL,
+
+ /* NXT 1.0+ (22): void. */
+ OFPRAW_NXT_FLOW_MONITOR_PAUSED,
+
+ /* NXT 1.0+ (23): void. */
+ OFPRAW_NXT_FLOW_MONITOR_RESUMED,
+
+/* Nicira extension statistics.
+ *
+ * Nicira extension statistics that correspond to standard OpenFlow statistics
+ * are listed alongside the standard versions above. */
+
+ /* NXST 1.0 (2): uint8_t[8][]. */
+ OFPRAW_NXST_FLOW_MONITOR_REQUEST,
+
+ /* NXST 1.0 (2): uint8_t[8][]. */
+ OFPRAW_NXST_FLOW_MONITOR_REPLY,
+};
+
+/* Decoding messages into OFPRAW_* values. */
+enum ofperr ofpraw_decode(enum ofpraw *, const struct ofp_header *);
+enum ofperr ofpraw_pull(enum ofpraw *, struct ofpbuf *);
+enum ofpraw ofpraw_pull_assert(struct ofpbuf *);
+
+enum ofperr ofpraw_decode_partial(enum ofpraw *,
+ const struct ofp_header *, size_t length);
+
+/* Encoding messages using OFPRAW_* values. */
+struct ofpbuf *ofpraw_alloc(enum ofpraw, uint8_t ofp_version,
+ size_t extra_tailroom);
+struct ofpbuf *ofpraw_alloc_xid(enum ofpraw, uint8_t ofp_version,
+ ovs_be32 xid, size_t extra_tailroom);
+struct ofpbuf *ofpraw_alloc_reply(enum ofpraw,
+ const struct ofp_header *request,
+ size_t extra_tailroom);
+struct ofpbuf *ofpraw_alloc_stats_reply(const struct ofp_header *request,
+ size_t extra_tailroom);
+
+void ofpraw_put(enum ofpraw, uint8_t ofp_version, struct ofpbuf *);
+void ofpraw_put_xid(enum ofpraw, uint8_t ofp_version, ovs_be32 xid,
+ struct ofpbuf *);
+void ofpraw_put_reply(enum ofpraw, const struct ofp_header *request,
+ struct ofpbuf *);
+void ofpraw_put_stats_reply(const struct ofp_header *request, struct ofpbuf *);
+
+/* Information about OFPRAW_* values. */
+const char *ofpraw_get_name(enum ofpraw);
+enum ofpraw ofpraw_stats_request_to_reply(enum ofpraw, uint8_t version);
+\f
+/* Semantic identifiers for OpenFlow messages.
+ *
+ * Each OFPTYPE_* enumeration constant represents one or more concrete format
+ * of OpenFlow message. When two variants of a message have essentially the
+ * same meaning, they are assigned a single OFPTYPE_* value.
+ *
+ * The comments here must follow a stylized form because the "extract-ofp-msgs"
+ * program parses them at build time to generate data tables. The format is
+ * simply to list each OFPRAW_* enumeration constant for a given OFPTYPE_*,
+ * each followed by a period. */
+enum ofptype {
+ /* Immutable messages. */
+ OFPTYPE_HELLO, /* OFPRAW_OFPT_HELLO. */
+ OFPTYPE_ERROR, /* OFPRAW_OFPT_ERROR. */
+ OFPTYPE_ECHO_REQUEST, /* OFPRAW_OFPT_ECHO_REQUEST. */
+ OFPTYPE_ECHO_REPLY, /* OFPRAW_OFPT_ECHO_REPLY. */
+
+ /* Switch configuration messages. */
+ OFPTYPE_FEATURES_REQUEST, /* OFPRAW_OFPT_FEATURES_REQUEST. */
+ OFPTYPE_FEATURES_REPLY, /* OFPRAW_OFPT10_FEATURES_REPLY.
+ * OFPRAW_OFPT11_FEATURES_REPLY. */
+ OFPTYPE_GET_CONFIG_REQUEST, /* OFPRAW_OFPT_GET_CONFIG_REQUEST. */
+ OFPTYPE_GET_CONFIG_REPLY, /* OFPRAW_OFPT_GET_CONFIG_REPLY. */
+ OFPTYPE_SET_CONFIG, /* OFPRAW_OFPT_SET_CONFIG. */
+
+ /* Asynchronous messages. */
+ OFPTYPE_PACKET_IN, /* OFPRAW_OFPT10_PACKET_IN.
+ * OFPRAW_OFPT11_PACKET_IN.
+ * OFPRAW_OFPT12_PACKET_IN.
+ * OFPRAW_NXT_PACKET_IN. */
+ OFPTYPE_FLOW_REMOVED, /* OFPRAW_OFPT10_FLOW_REMOVED.
+ * OFPRAW_OFPT11_FLOW_REMOVED.
+ * OFPRAW_NXT_FLOW_REMOVED. */
+ OFPTYPE_PORT_STATUS, /* OFPRAW_OFPT10_PORT_STATUS.
+ * OFPRAW_OFPT11_PORT_STATUS. */
+
+ /* Controller command messages. */
+ OFPTYPE_PACKET_OUT, /* OFPRAW_OFPT10_PACKET_OUT.
+ * OFPRAW_OFPT11_PACKET_OUT. */
+ OFPTYPE_FLOW_MOD, /* OFPRAW_OFPT10_FLOW_MOD.
+ * OFPRAW_OFPT11_FLOW_MOD.
+ * OFPRAW_NXT_FLOW_MOD. */
+ OFPTYPE_PORT_MOD, /* OFPRAW_OFPT10_PORT_MOD.
+ * OFPRAW_OFPT11_PORT_MOD. */
+
+ /* Barrier messages. */
+ OFPTYPE_BARRIER_REQUEST, /* OFPRAW_OFPT10_BARRIER_REQUEST.
+ * OFPRAW_OFPT11_BARRIER_REQUEST. */
+ OFPTYPE_BARRIER_REPLY, /* OFPRAW_OFPT10_BARRIER_REPLY.
+ * OFPRAW_OFPT11_BARRIER_REPLY. */
+
+ /* Statistics. */
+ OFPTYPE_DESC_STATS_REQUEST, /* OFPRAW_OFPST_DESC_REQUEST. */
+ OFPTYPE_DESC_STATS_REPLY, /* OFPRAW_OFPST_DESC_REPLY. */
+ OFPTYPE_FLOW_STATS_REQUEST, /* OFPRAW_OFPST10_FLOW_REQUEST.
+ * OFPRAW_OFPST11_FLOW_REQUEST.
+ * OFPRAW_NXST_FLOW_REQUEST. */
+ OFPTYPE_FLOW_STATS_REPLY, /* OFPRAW_OFPST10_FLOW_REPLY.
+ * OFPRAW_OFPST11_FLOW_REPLY.
+ * OFPRAW_NXST_FLOW_REPLY. */
+ OFPTYPE_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST10_AGGREGATE_REQUEST.
+ * OFPRAW_OFPST11_AGGREGATE_REQUEST.
+ * OFPRAW_NXST_AGGREGATE_REQUEST. */
+ OFPTYPE_AGGREGATE_STATS_REPLY, /* OFPRAW_OFPST_AGGREGATE_REPLY.
+ * OFPRAW_NXST_AGGREGATE_REPLY. */
+ OFPTYPE_TABLE_STATS_REQUEST, /* OFPRAW_OFPST_TABLE_REQUEST. */
+ OFPTYPE_TABLE_STATS_REPLY, /* OFPRAW_OFPST10_TABLE_REPLY.
+ * OFPRAW_OFPST11_TABLE_REPLY.
+ * OFPRAW_OFPST12_TABLE_REPLY. */
+ OFPTYPE_PORT_STATS_REQUEST, /* OFPRAW_OFPST_PORT_REQUEST. */
+ OFPTYPE_PORT_STATS_REPLY, /* OFPRAW_OFPST_PORT_REPLY. */
+ OFPTYPE_QUEUE_STATS_REQUEST, /* OFPRAW_OFPST_QUEUE_REQUEST. */
+ OFPTYPE_QUEUE_STATS_REPLY, /* OFPRAW_OFPST_QUEUE_REPLY. */
+ OFPTYPE_PORT_DESC_STATS_REQUEST, /* OFPRAW_OFPST_PORT_DESC_REQUEST. */
+ OFPTYPE_PORT_DESC_STATS_REPLY, /* OFPRAW_OFPST_PORT_DESC_REPLY. */
+
+ /* Nicira extensions. */
+ OFPTYPE_ROLE_REQUEST, /* OFPRAW_NXT_ROLE_REQUEST. */
+ OFPTYPE_ROLE_REPLY, /* OFPRAW_NXT_ROLE_REPLY. */
+ OFPTYPE_SET_FLOW_FORMAT, /* OFPRAW_NXT_SET_FLOW_FORMAT. */
+ OFPTYPE_FLOW_MOD_TABLE_ID, /* OFPRAW_NXT_FLOW_MOD_TABLE_ID. */
+ OFPTYPE_SET_PACKET_IN_FORMAT, /* OFPRAW_NXT_SET_PACKET_IN_FORMAT. */
+ OFPTYPE_FLOW_AGE, /* OFPRAW_NXT_FLOW_AGE. */
+ OFPTYPE_SET_ASYNC_CONFIG, /* OFPRAW_NXT_SET_ASYNC_CONFIG. */
+ OFPTYPE_SET_CONTROLLER_ID, /* OFPRAW_NXT_SET_CONTROLLER_ID. */
+
+ /* Flow monitor extension. */
+ OFPTYPE_FLOW_MONITOR_STATS_REQUEST, /* OFPRAW_NXST_FLOW_MONITOR_REQUEST. */
+ OFPTYPE_FLOW_MONITOR_STATS_REPLY, /* OFPRAW_NXST_FLOW_MONITOR_REPLY. */
+ OFPTYPE_FLOW_MONITOR_CANCEL, /* OFPRAW_NXT_FLOW_MONITOR_CANCEL. */
+ OFPTYPE_FLOW_MONITOR_PAUSED, /* OFPRAW_NXT_FLOW_MONITOR_PAUSED. */
+ OFPTYPE_FLOW_MONITOR_RESUMED, /* OFPRAW_NXT_FLOW_MONITOR_RESUMED. */
+};
+
+/* Decoding messages into OFPTYPE_* values. */
+enum ofperr ofptype_decode(enum ofptype *, const struct ofp_header *);
+enum ofperr ofptype_pull(enum ofptype *, struct ofpbuf *);
+enum ofptype ofptype_from_ofpraw(enum ofpraw);
+\f
+/* OpenFlow message properties. */
+void ofpmsg_update_length(struct ofpbuf *);
+const void *ofpmsg_body(const struct ofp_header *);
+\f
+/* Multipart messages (aka "statistics").
+ *
+ * Individual OpenFlow messages are limited to 64 kB in size, but some messages
+ * need to be longer. Therefore, multipart messages allow a longer message to
+ * be divided into multiple parts at some convenient boundary. For example,
+ * limiting the response to a "flow dump" request to 64 kB would unreasonably
+ * limit the maximum number of flows in an OpenFlow switch, so a "flow dump" is
+ * expressed as a multipart request/reply pair, with the reply broken into
+ * pieces between flows.
+ *
+ * Multipart messages always consist of a request/reply pair.
+ *
+ * In OpenFlow 1.0, 1.1, and 1.2, requests must always fit in a single message,
+ * that is, only a multipart reply may have more than one part. OpenFlow 1.3
+ * adds one multipart request. This code does not yet support multipart
+ * requests. */
+
+/* Encoding multipart replies.
+ *
+ * These functions are useful for multipart replies that might really require
+ * more than one message. A multipart message that is known in advance to fit
+ * within 64 kB doesn't need any special treatment, so you might as well use
+ * the ofpraw_alloc_*() functions.
+ *
+ * These functions work with a "struct list" of "struct ofpbuf"s, each of
+ * which represents one part of a multipart message. */
+void ofpmp_init(struct list *, const struct ofp_header *request);
+struct ofpbuf *ofpmp_reserve(struct list *, size_t len);
+void *ofpmp_append(struct list *, size_t len);
+void ofpmp_postappend(struct list *, size_t start_ofs);
+
+/* Decoding multipart replies. */
+uint16_t ofpmp_flags(const struct ofp_header *);
+bool ofpmp_more(const struct ofp_header *);
+
+#endif /* ofp-msgs.h */
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "dynamic-string.h"
#include "learn.h"
#include "meta-flow.h"
-#include "netdev.h"
#include "multipath.h"
+#include "netdev.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
VLOG_DEFINE_THIS_MODULE(ofp_parse);
+static void ofp_fatal(const char *flow, bool verbose, const char *format, ...)
+ NO_RETURN;
+
static uint8_t
str_to_table_id(const char *str)
{
*ip = in_addr.s_addr;
}
-static struct ofp_action_output *
-put_output_action(struct ofpbuf *b, uint16_t port)
-{
- struct ofp_action_output *oao;
-
- oao = ofputil_put_OFPAT10_OUTPUT(b);
- oao->port = htons(port);
- return oao;
-}
-
static void
-parse_enqueue(struct ofpbuf *b, char *arg)
+parse_enqueue(char *arg, struct ofpbuf *ofpacts)
{
char *sp = NULL;
char *port = strtok_r(arg, ":q", &sp);
char *queue = strtok_r(NULL, "", &sp);
- struct ofp_action_enqueue *oae;
+ struct ofpact_enqueue *enqueue;
if (port == NULL || queue == NULL) {
ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
}
- oae = ofputil_put_OFPAT10_ENQUEUE(b);
- oae->port = htons(str_to_u32(port));
- oae->queue_id = htonl(str_to_u32(queue));
+ enqueue = ofpact_put_ENQUEUE(ofpacts);
+ enqueue->port = str_to_u32(port);
+ enqueue->queue = str_to_u32(queue);
}
static void
-parse_output(struct ofpbuf *b, char *arg)
+parse_output(char *arg, struct ofpbuf *ofpacts)
{
if (strchr(arg, '[')) {
- struct nx_action_output_reg *naor;
- struct mf_subfield src;
-
- mf_parse_subfield(&src, arg);
+ struct ofpact_output_reg *output_reg;
- naor = ofputil_put_NXAST_OUTPUT_REG(b);
- naor->ofs_nbits = nxm_encode_ofs_nbits(src.ofs, src.n_bits);
- naor->src = htonl(src.field->nxm_header);
- naor->max_len = htons(UINT16_MAX);
+ output_reg = ofpact_put_OUTPUT_REG(ofpacts);
+ mf_parse_subfield(&output_reg->src, arg);
+ output_reg->max_len = UINT16_MAX;
} else {
- put_output_action(b, str_to_u32(arg));
+ struct ofpact_output *output;
+
+ output = ofpact_put_OUTPUT(ofpacts);
+ output->port = str_to_u32(arg);
+ output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
}
}
static void
-parse_resubmit(struct ofpbuf *b, char *arg)
+parse_resubmit(char *arg, struct ofpbuf *ofpacts)
{
- struct nx_action_resubmit *nar;
+ struct ofpact_resubmit *resubmit;
char *in_port_s, *table_s;
- uint16_t in_port;
- uint8_t table;
+
+ resubmit = ofpact_put_RESUBMIT(ofpacts);
in_port_s = strsep(&arg, ",");
if (in_port_s && in_port_s[0]) {
- if (!ofputil_port_from_string(in_port_s, &in_port)) {
- in_port = str_to_u32(in_port_s);
+ if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) {
+ resubmit->in_port = str_to_u32(in_port_s);
}
} else {
- in_port = OFPP_IN_PORT;
+ resubmit->in_port = OFPP_IN_PORT;
}
table_s = strsep(&arg, ",");
- table = table_s && table_s[0] ? str_to_u32(table_s) : 255;
+ resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255;
- if (in_port == OFPP_IN_PORT && table == 255) {
+ if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) {
ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified "
" on resubmit");
}
-
- if (in_port != OFPP_IN_PORT && table == 255) {
- nar = ofputil_put_NXAST_RESUBMIT(b);
- } else {
- nar = ofputil_put_NXAST_RESUBMIT_TABLE(b);
- nar->table = table;
- }
- nar->in_port = htons(in_port);
}
static void
-parse_set_tunnel(struct ofpbuf *b, const char *arg)
+parse_note(const char *arg, struct ofpbuf *ofpacts)
{
- uint64_t tun_id = str_to_u64(arg);
- if (tun_id > UINT32_MAX) {
- ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(tun_id);
- } else {
- ofputil_put_NXAST_SET_TUNNEL(b)->tun_id = htonl(tun_id);
- }
-}
-
-static void
-parse_note(struct ofpbuf *b, const char *arg)
-{
- size_t start_ofs = b->size;
- struct nx_action_note *nan;
- int remainder;
- size_t len;
+ struct ofpact_note *note;
- nan = ofputil_put_NXAST_NOTE(b);
-
- b->size -= sizeof nan->note;
+ note = ofpact_put_NOTE(ofpacts);
while (*arg != '\0') {
uint8_t byte;
bool ok;
if (!ok) {
ovs_fatal(0, "bad hex digit in `note' argument");
}
- ofpbuf_put(b, &byte, 1);
+ ofpbuf_put(ofpacts, &byte, 1);
- arg += 2;
- }
+ note = ofpacts->l2;
+ note->length++;
- len = b->size - start_ofs;
- remainder = len % OFP_ACTION_ALIGN;
- if (remainder) {
- ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder);
+ arg += 2;
}
- nan = (struct nx_action_note *)((char *)b->data + start_ofs);
- nan->len = htons(b->size - start_ofs);
+ ofpact_update_len(ofpacts, ¬e->ofpact);
}
static void
parse_fin_timeout(struct ofpbuf *b, char *arg)
{
- struct nx_action_fin_timeout *naft;
+ struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b);
char *key, *value;
- naft = ofputil_put_NXAST_FIN_TIMEOUT(b);
while (ofputil_parse_key_value(&arg, &key, &value)) {
if (!strcmp(key, "idle_timeout")) {
- naft->fin_idle_timeout = htons(str_to_u16(value, key));
+ oft->fin_idle_timeout = str_to_u16(value, key);
} else if (!strcmp(key, "hard_timeout")) {
- naft->fin_hard_timeout = htons(str_to_u16(value, key));
+ oft->fin_hard_timeout = str_to_u16(value, key);
} else {
ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key);
}
}
if (reason == OFPR_ACTION && controller_id == 0) {
- put_output_action(b, OFPP_CONTROLLER)->max_len = htons(max_len);
+ struct ofpact_output *output;
+
+ output = ofpact_put_OUTPUT(b);
+ output->port = OFPP_CONTROLLER;
+ output->max_len = max_len;
+ } else {
+ struct ofpact_controller *controller;
+
+ controller = ofpact_put_CONTROLLER(b);
+ controller->max_len = max_len;
+ controller->reason = reason;
+ controller->controller_id = controller_id;
+ }
+}
+
+static void
+parse_dec_ttl(struct ofpbuf *b, char *arg)
+{
+ struct ofpact_cnt_ids *ids;
+
+ ids = ofpact_put_DEC_TTL(b);
+
+ if (*arg == '\0') {
+ uint16_t id = 0;
+
+ ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
+ ofpbuf_put(b, &id, sizeof id);
+ ids = b->l2;
+ ids->n_controllers++;
} else {
- struct nx_action_controller *nac;
+ char *cntr;
+
+ ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS;
+ for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL;
+ cntr = strtok_r(NULL, ", ", &arg)) {
+ uint16_t id = atoi(cntr);
+
+ ofpbuf_put(b, &id, sizeof id);
+ ids = b->l2;
+ ids->n_controllers++;
+ }
+ if (!ids->n_controllers) {
+ ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller "
+ "id.");
+ }
- nac = ofputil_put_NXAST_CONTROLLER(b);
- nac->max_len = htons(max_len);
- nac->reason = reason;
- nac->controller_id = htons(controller_id);
}
+ ofpact_update_len(b, &ids->ofpact);
}
static void
parse_named_action(enum ofputil_action_code code, const struct flow *flow,
- struct ofpbuf *b, char *arg)
+ char *arg, struct ofpbuf *ofpacts)
{
- struct ofp_action_dl_addr *oada;
- struct ofp_action_vlan_pcp *oavp;
- struct ofp_action_vlan_vid *oavv;
- struct ofp_action_nw_addr *oana;
- struct ofp_action_tp_port *oata;
+ struct ofpact_tunnel *tunnel;
+ uint16_t vid;
+ ovs_be32 ip;
+ uint8_t pcp;
+ uint8_t tos;
switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+ NOT_REACHED();
+
case OFPUTIL_OFPAT10_OUTPUT:
- parse_output(b, arg);
+ case OFPUTIL_OFPAT11_OUTPUT:
+ parse_output(arg, ofpacts);
break;
case OFPUTIL_OFPAT10_SET_VLAN_VID:
- oavv = ofputil_put_OFPAT10_SET_VLAN_VID(b);
- oavv->vlan_vid = htons(str_to_u32(arg));
+ case OFPUTIL_OFPAT11_SET_VLAN_VID:
+ vid = str_to_u32(arg);
+ if (vid & ~VLAN_VID_MASK) {
+ ovs_fatal(0, "%s: not a valid VLAN VID", arg);
+ }
+ ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid;
break;
case OFPUTIL_OFPAT10_SET_VLAN_PCP:
- oavp = ofputil_put_OFPAT10_SET_VLAN_PCP(b);
- oavp->vlan_pcp = str_to_u32(arg);
+ case OFPUTIL_OFPAT11_SET_VLAN_PCP:
+ pcp = str_to_u32(arg);
+ if (pcp & ~7) {
+ ovs_fatal(0, "%s: not a valid VLAN PCP", arg);
+ }
+ ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
break;
case OFPUTIL_OFPAT10_STRIP_VLAN:
- ofputil_put_OFPAT10_STRIP_VLAN(b);
+ ofpact_put_STRIP_VLAN(ofpacts);
break;
case OFPUTIL_OFPAT10_SET_DL_SRC:
+ case OFPUTIL_OFPAT11_SET_DL_SRC:
+ str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
+ break;
+
case OFPUTIL_OFPAT10_SET_DL_DST:
- oada = ofputil_put_action(code, b);
- str_to_mac(arg, oada->dl_addr);
+ case OFPUTIL_OFPAT11_SET_DL_DST:
+ str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac);
break;
case OFPUTIL_OFPAT10_SET_NW_SRC:
+ case OFPUTIL_OFPAT11_SET_NW_SRC:
+ str_to_ip(arg, &ip);
+ ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip;
+ break;
+
case OFPUTIL_OFPAT10_SET_NW_DST:
- oana = ofputil_put_action(code, b);
- str_to_ip(arg, &oana->nw_addr);
+ case OFPUTIL_OFPAT11_SET_NW_DST:
+ str_to_ip(arg, &ip);
+ ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip;
break;
case OFPUTIL_OFPAT10_SET_NW_TOS:
- ofputil_put_OFPAT10_SET_NW_TOS(b)->nw_tos = str_to_u32(arg);
+ case OFPUTIL_OFPAT11_SET_NW_TOS:
+ tos = str_to_u32(arg);
+ if (tos & ~IP_DSCP_MASK) {
+ ovs_fatal(0, "%s: not a valid TOS", arg);
+ }
+ ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos;
break;
case OFPUTIL_OFPAT10_SET_TP_SRC:
+ case OFPUTIL_OFPAT11_SET_TP_SRC:
+ ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg);
+ break;
+
case OFPUTIL_OFPAT10_SET_TP_DST:
- oata = ofputil_put_action(code, b);
- oata->tp_port = htons(str_to_u32(arg));
+ case OFPUTIL_OFPAT11_SET_TP_DST:
+ ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg);
break;
case OFPUTIL_OFPAT10_ENQUEUE:
- parse_enqueue(b, arg);
+ parse_enqueue(arg, ofpacts);
break;
case OFPUTIL_NXAST_RESUBMIT:
- parse_resubmit(b, arg);
+ parse_resubmit(arg, ofpacts);
break;
case OFPUTIL_NXAST_SET_TUNNEL:
- parse_set_tunnel(b, arg);
+ case OFPUTIL_NXAST_SET_TUNNEL64:
+ tunnel = ofpact_put_SET_TUNNEL(ofpacts);
+ tunnel->ofpact.compat = code;
+ tunnel->tun_id = str_to_u64(arg);
break;
case OFPUTIL_NXAST_SET_QUEUE:
- ofputil_put_NXAST_SET_QUEUE(b)->queue_id = htonl(str_to_u32(arg));
+ ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
break;
case OFPUTIL_NXAST_POP_QUEUE:
- ofputil_put_NXAST_POP_QUEUE(b);
+ ofpact_put_POP_QUEUE(ofpacts);
break;
case OFPUTIL_NXAST_REG_MOVE:
- nxm_parse_reg_move(ofputil_put_NXAST_REG_MOVE(b), arg);
+ nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg);
break;
case OFPUTIL_NXAST_REG_LOAD:
- nxm_parse_reg_load(ofputil_put_NXAST_REG_LOAD(b), arg);
+ nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg);
break;
case OFPUTIL_NXAST_NOTE:
- parse_note(b, arg);
- break;
-
- case OFPUTIL_NXAST_SET_TUNNEL64:
- ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(str_to_u64(arg));
+ parse_note(arg, ofpacts);
break;
case OFPUTIL_NXAST_MULTIPATH:
- multipath_parse(ofputil_put_NXAST_MULTIPATH(b), arg);
+ multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
break;
- case OFPUTIL_NXAST_AUTOPATH:
- autopath_parse(ofputil_put_NXAST_AUTOPATH(b), arg);
+ case OFPUTIL_NXAST_AUTOPATH__DEPRECATED:
+ autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg);
break;
case OFPUTIL_NXAST_BUNDLE:
- bundle_parse(b, arg);
+ bundle_parse(arg, ofpacts);
break;
case OFPUTIL_NXAST_BUNDLE_LOAD:
- bundle_parse_load(b, arg);
+ bundle_parse_load(arg, ofpacts);
break;
case OFPUTIL_NXAST_RESUBMIT_TABLE:
case OFPUTIL_NXAST_OUTPUT_REG:
+ case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
NOT_REACHED();
case OFPUTIL_NXAST_LEARN:
- learn_parse(b, arg, flow);
+ learn_parse(arg, flow, ofpacts);
break;
case OFPUTIL_NXAST_EXIT:
- ofputil_put_NXAST_EXIT(b);
+ ofpact_put_EXIT(ofpacts);
break;
case OFPUTIL_NXAST_DEC_TTL:
- ofputil_put_NXAST_DEC_TTL(b);
+ parse_dec_ttl(ofpacts, arg);
break;
case OFPUTIL_NXAST_FIN_TIMEOUT:
- parse_fin_timeout(b, arg);
+ parse_fin_timeout(ofpacts, arg);
break;
case OFPUTIL_NXAST_CONTROLLER:
- parse_controller(b, arg);
+ parse_controller(ofpacts, arg);
break;
}
}
static void
-str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
+str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
{
char *pos, *act, *arg;
int n_actions;
code = ofputil_action_code_from_name(act);
if (code >= 0) {
- parse_named_action(code, flow, b, arg);
+ parse_named_action(code, flow, arg, ofpacts);
} else if (!strcasecmp(act, "drop")) {
- /* A drop action in OpenFlow occurs by just not setting
- * an action. */
if (n_actions) {
ovs_fatal(0, "Drop actions must not be preceded by other "
"actions");
}
break;
} else if (ofputil_port_from_string(act, &port)) {
- put_output_action(b, port);
+ ofpact_put_OUTPUT(ofpacts)->port = port;
} else {
ovs_fatal(0, "Unknown action: %s", act);
}
n_actions++;
}
+ ofpact_pad(ofpacts);
}
struct protocol {
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;
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")
}
}
}
+ if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX)
+ && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) {
+ /* On modifies without a mask, we are supposed to add a flow if
+ * one does not exist. If a cookie wasn't been specified, use a
+ * default of zero. */
+ fm->new_cookie = htonll(0);
+ }
if (fields & F_ACTIONS) {
- struct ofpbuf actions;
+ struct ofpbuf ofpacts;
- ofpbuf_init(&actions, sizeof(union ofp_action));
- str_to_action(&fm->cr.flow, act_str, &actions);
- fm->actions = ofpbuf_steal_data(&actions);
- fm->n_actions = actions.size / sizeof(union ofp_action);
+ ofpbuf_init(&ofpacts, 32);
+ str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts);
+ fm->ofpacts_len = ofpacts.size;
+ fm->ofpacts = ofpbuf_steal_data(&ofpacts);
} else {
- fm->actions = NULL;
- fm->n_actions = 0;
+ fm->ofpacts_len = 0;
+ fm->ofpacts = NULL;
}
free(string);
}
+/* Convert 'str_' (as described in the documentation for the "monitor" command
+ * in the ovs-ofctl man page) into 'fmr'. */
+void
+parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
+ const char *str_)
+{
+ static uint32_t id;
+
+ char *string = xstrdup(str_);
+ char *save_ptr = NULL;
+ char *name;
+
+ fmr->id = id++;
+ fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY
+ | NXFMF_OWN | NXFMF_ACTIONS);
+ fmr->out_port = OFPP_NONE;
+ fmr->table_id = 0xff;
+ cls_rule_init_catchall(&fmr->match, 0);
+
+ for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+ name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+ const struct protocol *p;
+
+ if (!strcmp(name, "!initial")) {
+ fmr->flags &= ~NXFMF_INITIAL;
+ } else if (!strcmp(name, "!add")) {
+ fmr->flags &= ~NXFMF_ADD;
+ } else if (!strcmp(name, "!delete")) {
+ fmr->flags &= ~NXFMF_DELETE;
+ } else if (!strcmp(name, "!modify")) {
+ fmr->flags &= ~NXFMF_MODIFY;
+ } else if (!strcmp(name, "!actions")) {
+ fmr->flags &= ~NXFMF_ACTIONS;
+ } else if (!strcmp(name, "!own")) {
+ fmr->flags &= ~NXFMF_OWN;
+ } else if (parse_protocol(name, &p)) {
+ cls_rule_set_dl_type(&fmr->match, htons(p->dl_type));
+ if (p->nw_proto) {
+ cls_rule_set_nw_proto(&fmr->match, p->nw_proto);
+ }
+ } else {
+ char *value;
+
+ value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+ if (!value) {
+ ovs_fatal(0, "%s: field %s missing value", str_, name);
+ }
+
+ if (!strcmp(name, "table")) {
+ fmr->table_id = str_to_table_id(value);
+ } else if (!strcmp(name, "out_port")) {
+ fmr->out_port = atoi(value);
+ } else if (mf_from_name(name)) {
+ parse_field(mf_from_name(name), value, &fmr->match);
+ } else {
+ ovs_fatal(0, "%s: unknown keyword %s", str_, name);
+ }
+ }
+ }
+ free(string);
+}
+
/* Parses 's' as a set of OpenFlow actions and appends the actions to
* 'actions'.
*
* Prints an error on stderr and aborts the program if 's' syntax is
* invalid. */
void
-parse_ofp_actions(const char *s_, struct ofpbuf *actions)
+parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
{
char *s = xstrdup(s_);
- str_to_action(NULL, s, actions);
+ str_to_ofpacts(NULL, s, ofpacts);
free(s);
}
fsr->out_port = fm.out_port;
fsr->table_id = fm.table_id;
}
+
+/* Parses a specification of a flow from 's' into 'flow'. 's' must take the
+ * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a
+ * mf_field. Fields must be specified in a natural order for satisfying
+ * prerequisites.
+ *
+ * Returns NULL on success, otherwise a malloc()'d string that explains the
+ * problem. */
+char *
+parse_ofp_exact_flow(struct flow *flow, const char *s)
+{
+ char *pos, *key, *value_s;
+ char *error = NULL;
+ char *copy;
+
+ memset(flow, 0, sizeof *flow);
+
+ pos = copy = xstrdup(s);
+ while (ofputil_parse_key_value(&pos, &key, &value_s)) {
+ const struct protocol *p;
+ if (parse_protocol(key, &p)) {
+ if (flow->dl_type) {
+ error = xasprintf("%s: Ethernet type set multiple times", s);
+ goto exit;
+ }
+ flow->dl_type = htons(p->dl_type);
+
+ if (p->nw_proto) {
+ if (flow->nw_proto) {
+ error = xasprintf("%s: network protocol set "
+ "multiple times", s);
+ goto exit;
+ }
+ flow->nw_proto = p->nw_proto;
+ }
+ } else {
+ const struct mf_field *mf;
+ union mf_value value;
+ char *field_error;
+
+ mf = mf_from_name(key);
+ if (!mf) {
+ error = xasprintf("%s: unknown field %s", s, key);
+ goto exit;
+ }
+
+ if (!mf_are_prereqs_ok(mf, flow)) {
+ error = xasprintf("%s: prerequisites not met for setting %s",
+ s, key);
+ goto exit;
+ }
+
+ if (!mf_is_zero(mf, flow)) {
+ error = xasprintf("%s: field %s set multiple times", s, key);
+ goto exit;
+ }
+
+ field_error = mf_parse_value(mf, value_s, &value);
+ if (field_error) {
+ error = xasprintf("%s: bad value for %s (%s)",
+ s, key, field_error);
+ free(field_error);
+ goto exit;
+ }
+
+ mf_set_flow_value(mf, &value, flow);
+ }
+ }
+
+exit:
+ free(copy);
+
+ if (error) {
+ memset(flow, 0, sizeof *flow);
+ }
+ return error;
+}
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdint.h>
#include <stdio.h>
+struct flow;
struct ofpbuf;
struct ofputil_flow_mod;
+struct ofputil_flow_monitor_request;
struct ofputil_flow_stats_request;
void parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
bool aggregate, const char *string);
-void parse_ofp_actions(const char *, struct ofpbuf *actions);
+void parse_ofpacts(const char *, struct ofpbuf *ofpacts);
+
+char *parse_ofp_exact_flow(struct flow *, const char *);
+
+void parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
+ const char *);
#endif /* ofp-parse.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "meta-flow.h"
#include "netdev.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
+#include "ofp-msgs.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
ds_put_format(string, " total_len=%"PRIu16" in_port=", pin.total_len);
ofputil_format_port(pin.fmd.in_port, string);
- if (pin.fmd.tun_id_mask) {
+ if (pin.fmd.tun_id != htonll(0)) {
ds_put_format(string, " tun_id=0x%"PRIx64, ntohll(pin.fmd.tun_id));
- if (pin.fmd.tun_id_mask != htonll(UINT64_MAX)) {
- ds_put_format(string, "/0x%"PRIx64, ntohll(pin.fmd.tun_id_mask));
- }
+ }
+
+ if (pin.fmd.metadata != htonll(0)) {
+ ds_put_format(string, " metadata=0x%"PRIx64, ntohll(pin.fmd.metadata));
}
for (i = 0; i < FLOW_N_REGS; i++) {
- if (pin.fmd.reg_masks[i]) {
+ if (pin.fmd.regs[i]) {
ds_put_format(string, " reg%d=0x%"PRIx32, i, pin.fmd.regs[i]);
- if (pin.fmd.reg_masks[i] != UINT32_MAX) {
- ds_put_format(string, "/0x%"PRIx32, pin.fmd.reg_masks[i]);
- }
}
}
}
static void
-print_note(struct ds *string, const struct nx_action_note *nan)
-{
- size_t len;
- size_t i;
-
- ds_put_cstr(string, "note:");
- len = ntohs(nan->len) - offsetof(struct nx_action_note, note);
- for (i = 0; i < len; i++) {
- if (i) {
- ds_put_char(string, '.');
- }
- ds_put_format(string, "%02"PRIx8, nan->note[i]);
- }
-}
-
-static void
-ofp_print_action(struct ds *s, const union ofp_action *a,
- enum ofputil_action_code code)
-{
- const struct ofp_action_enqueue *oae;
- const struct ofp_action_dl_addr *oada;
- const struct nx_action_set_tunnel64 *nast64;
- const struct nx_action_set_tunnel *nast;
- const struct nx_action_set_queue *nasq;
- const struct nx_action_resubmit *nar;
- const struct nx_action_reg_move *move;
- const struct nx_action_reg_load *load;
- const struct nx_action_multipath *nam;
- const struct nx_action_autopath *naa;
- const struct nx_action_output_reg *naor;
- const struct nx_action_fin_timeout *naft;
- const struct nx_action_controller *nac;
- struct mf_subfield subfield;
- uint16_t port;
-
- switch (code) {
- case OFPUTIL_OFPAT10_OUTPUT:
- port = ntohs(a->output.port);
- if (port < OFPP_MAX) {
- ds_put_format(s, "output:%"PRIu16, port);
- } else {
- ofputil_format_port(port, s);
- if (port == OFPP_CONTROLLER) {
- if (a->output.max_len != htons(0)) {
- ds_put_format(s, ":%"PRIu16, ntohs(a->output.max_len));
- } else {
- ds_put_cstr(s, ":all");
- }
- }
- }
- break;
-
- case OFPUTIL_OFPAT10_ENQUEUE:
- oae = (const struct ofp_action_enqueue *) a;
- ds_put_format(s, "enqueue:");
- ofputil_format_port(ntohs(oae->port), s);
- ds_put_format(s, "q%"PRIu32, ntohl(oae->queue_id));
- break;
-
- case OFPUTIL_OFPAT10_SET_VLAN_VID:
- ds_put_format(s, "mod_vlan_vid:%"PRIu16,
- ntohs(a->vlan_vid.vlan_vid));
- break;
-
- case OFPUTIL_OFPAT10_SET_VLAN_PCP:
- ds_put_format(s, "mod_vlan_pcp:%"PRIu8, a->vlan_pcp.vlan_pcp);
- break;
-
- case OFPUTIL_OFPAT10_STRIP_VLAN:
- ds_put_cstr(s, "strip_vlan");
- break;
-
- case OFPUTIL_OFPAT10_SET_DL_SRC:
- oada = (const struct ofp_action_dl_addr *) a;
- ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
- ETH_ADDR_ARGS(oada->dl_addr));
- break;
-
- case OFPUTIL_OFPAT10_SET_DL_DST:
- oada = (const struct ofp_action_dl_addr *) a;
- ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT,
- ETH_ADDR_ARGS(oada->dl_addr));
- break;
-
- case OFPUTIL_OFPAT10_SET_NW_SRC:
- ds_put_format(s, "mod_nw_src:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr));
- break;
-
- case OFPUTIL_OFPAT10_SET_NW_DST:
- ds_put_format(s, "mod_nw_dst:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr));
- break;
-
- case OFPUTIL_OFPAT10_SET_NW_TOS:
- ds_put_format(s, "mod_nw_tos:%d", a->nw_tos.nw_tos);
- break;
-
- case OFPUTIL_OFPAT10_SET_TP_SRC:
- ds_put_format(s, "mod_tp_src:%d", ntohs(a->tp_port.tp_port));
- break;
-
- case OFPUTIL_OFPAT10_SET_TP_DST:
- ds_put_format(s, "mod_tp_dst:%d", ntohs(a->tp_port.tp_port));
- break;
-
- case OFPUTIL_NXAST_RESUBMIT:
- nar = (struct nx_action_resubmit *)a;
- ds_put_format(s, "resubmit:");
- ofputil_format_port(ntohs(nar->in_port), s);
- break;
-
- case OFPUTIL_NXAST_RESUBMIT_TABLE:
- nar = (struct nx_action_resubmit *)a;
- ds_put_format(s, "resubmit(");
- if (nar->in_port != htons(OFPP_IN_PORT)) {
- ofputil_format_port(ntohs(nar->in_port), s);
- }
- ds_put_char(s, ',');
- if (nar->table != 255) {
- ds_put_format(s, "%"PRIu8, nar->table);
- }
- ds_put_char(s, ')');
- break;
-
- case OFPUTIL_NXAST_SET_TUNNEL:
- nast = (struct nx_action_set_tunnel *)a;
- ds_put_format(s, "set_tunnel:%#"PRIx32, ntohl(nast->tun_id));
- break;
-
- case OFPUTIL_NXAST_SET_QUEUE:
- nasq = (struct nx_action_set_queue *)a;
- ds_put_format(s, "set_queue:%u", ntohl(nasq->queue_id));
- break;
-
- case OFPUTIL_NXAST_POP_QUEUE:
- ds_put_cstr(s, "pop_queue");
- break;
-
- case OFPUTIL_NXAST_NOTE:
- print_note(s, (const struct nx_action_note *) a);
- break;
-
- case OFPUTIL_NXAST_REG_MOVE:
- move = (const struct nx_action_reg_move *) a;
- nxm_format_reg_move(move, s);
- break;
-
- case OFPUTIL_NXAST_REG_LOAD:
- load = (const struct nx_action_reg_load *) a;
- nxm_format_reg_load(load, s);
- break;
-
- case OFPUTIL_NXAST_SET_TUNNEL64:
- nast64 = (const struct nx_action_set_tunnel64 *) a;
- ds_put_format(s, "set_tunnel64:%#"PRIx64,
- ntohll(nast64->tun_id));
- break;
-
- case OFPUTIL_NXAST_MULTIPATH:
- nam = (const struct nx_action_multipath *) a;
- multipath_format(nam, s);
- break;
-
- case OFPUTIL_NXAST_AUTOPATH:
- naa = (const struct nx_action_autopath *)a;
- ds_put_format(s, "autopath(%u,", ntohl(naa->id));
- nxm_decode(&subfield, naa->dst, naa->ofs_nbits);
- mf_format_subfield(&subfield, s);
- ds_put_char(s, ')');
- break;
-
- case OFPUTIL_NXAST_BUNDLE:
- case OFPUTIL_NXAST_BUNDLE_LOAD:
- bundle_format((const struct nx_action_bundle *) a, s);
- break;
-
- case OFPUTIL_NXAST_OUTPUT_REG:
- naor = (const struct nx_action_output_reg *) a;
- ds_put_cstr(s, "output:");
- nxm_decode(&subfield, naor->src, naor->ofs_nbits);
- mf_format_subfield(&subfield, s);
- break;
-
- case OFPUTIL_NXAST_LEARN:
- learn_format((const struct nx_action_learn *) a, s);
- break;
-
- case OFPUTIL_NXAST_DEC_TTL:
- ds_put_cstr(s, "dec_ttl");
- break;
-
- case OFPUTIL_NXAST_EXIT:
- ds_put_cstr(s, "exit");
- break;
-
- case OFPUTIL_NXAST_FIN_TIMEOUT:
- naft = (const struct nx_action_fin_timeout *) a;
- ds_put_cstr(s, "fin_timeout(");
- if (naft->fin_idle_timeout) {
- ds_put_format(s, "idle_timeout=%"PRIu16",",
- ntohs(naft->fin_idle_timeout));
- }
- if (naft->fin_hard_timeout) {
- ds_put_format(s, "hard_timeout=%"PRIu16",",
- ntohs(naft->fin_hard_timeout));
- }
- ds_chomp(s, ',');
- ds_put_char(s, ')');
- break;
-
- case OFPUTIL_NXAST_CONTROLLER:
- nac = (const struct nx_action_controller *) a;
- ds_put_cstr(s, "controller(");
- if (nac->reason != OFPR_ACTION) {
- ds_put_format(s, "reason=%s,",
- ofputil_packet_in_reason_to_string(nac->reason));
- }
- if (nac->max_len != htons(UINT16_MAX)) {
- ds_put_format(s, "max_len=%"PRIu16",", ntohs(nac->max_len));
- }
- if (nac->controller_id != htons(0)) {
- ds_put_format(s, "id=%"PRIu16",", ntohs(nac->controller_id));
- }
- ds_chomp(s, ',');
- ds_put_char(s, ')');
- break;
-
- default:
- break;
- }
-}
-
-void
-ofp_print_actions(struct ds *string, const union ofp_action *actions,
- size_t n_actions)
-{
- const union ofp_action *a;
- size_t left;
-
- ds_put_cstr(string, "actions=");
- if (!n_actions) {
- ds_put_cstr(string, "drop");
- }
-
- OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
- int code = ofputil_decode_action(a);
- if (code >= 0) {
- if (a != actions) {
- ds_put_cstr(string, ",");
- }
- ofp_print_action(string, a, code);
- } else {
- ofp_print_error(string, -code);
- }
- }
- if (left > 0) {
- ds_put_format(string, " ***%zu leftover bytes following actions",
- left * sizeof *a);
- }
-}
-
-static void
-ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
+ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
int verbosity)
{
struct ofputil_packet_out po;
+ struct ofpbuf ofpacts;
enum ofperr error;
- error = ofputil_decode_packet_out(&po, opo);
+ ofpbuf_init(&ofpacts, 64);
+ error = ofputil_decode_packet_out(&po, oh, &ofpacts);
if (error) {
+ ofpbuf_uninit(&ofpacts);
ofp_print_error(string, error);
return;
}
ofputil_format_port(po.in_port, string);
ds_put_char(string, ' ');
- ofp_print_actions(string, po.actions, po.n_actions);
+ ofpacts_format(po.ofpacts, po.ofpacts_len, string);
if (po.buffer_id == UINT32_MAX) {
ds_put_format(string, " data_len=%zu", po.packet_len);
ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id);
}
ds_put_char(string, '\n');
+
+ ofpbuf_uninit(&ofpacts);
}
/* qsort comparison function. */
static void
ofp_print_bit_names(struct ds *string, uint32_t bits,
- const char *(*bit_to_name)(uint32_t bit))
+ const char *(*bit_to_name)(uint32_t bit),
+ char separator)
{
int n = 0;
int i;
const char *name = bit_to_name(bit);
if (name) {
if (n++) {
- ds_put_char(string, ' ');
+ ds_put_char(string, separator);
}
ds_put_cstr(string, name);
bits &= ~bit;
}
if (bits) {
- if (n++) {
- ds_put_char(string, ' ');
+ if (n) {
+ ds_put_char(string, separator);
}
ds_put_format(string, "0x%"PRIx32, bits);
}
static void
ofp_print_port_features(struct ds *string, enum netdev_features features)
{
- ofp_print_bit_names(string, features, netdev_feature_to_name);
+ ofp_print_bit_names(string, features, netdev_feature_to_name, ' ');
ds_put_char(string, '\n');
}
static void
ofp_print_port_config(struct ds *string, enum ofputil_port_config config)
{
- ofp_print_bit_names(string, config, ofputil_port_config_to_name);
+ ofp_print_bit_names(string, config, ofputil_port_config_to_name, ' ');
ds_put_char(string, '\n');
}
: "STP_BLOCK"));
state &= ~OFPUTIL_PS_STP_MASK;
if (state) {
- ofp_print_bit_names(string, state, ofputil_port_state_to_name);
+ ofp_print_bit_names(string, state, ofputil_port_state_to_name,
+ ' ');
}
} else {
- ofp_print_bit_names(string, state, ofputil_port_state_to_name);
+ ofp_print_bit_names(string, state, ofputil_port_state_to_name, ' ');
}
ds_put_char(string, '\n');
}
port->max_speed / UINT32_C(1000));
}
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', writes a detailed description of each port into
+ * 'string'. */
+static void
+ofp_print_phy_ports(struct ds *string, uint8_t ofp_version,
+ struct ofpbuf *b)
+{
+ size_t n_ports;
+ struct ofputil_phy_port *ports;
+ enum ofperr error;
+ size_t i;
+
+ n_ports = ofputil_count_phy_ports(ofp_version, b);
+
+ ports = xmalloc(n_ports * sizeof *ports);
+ for (i = 0; i < n_ports; i++) {
+ error = ofputil_pull_phy_port(ofp_version, b, &ports[i]);
+ if (error) {
+ ofp_print_error(string, error);
+ goto exit;
+ }
+ }
+ qsort(ports, n_ports, sizeof *ports, compare_ports);
+ for (i = 0; i < n_ports; i++) {
+ ofp_print_phy_port(string, &ports[i]);
+ }
+
+exit:
+ free(ports);
+}
+
static const char *
ofputil_capabilities_to_name(uint32_t bit)
{
case OFPUTIL_C_ARP_MATCH_IP: return "ARP_MATCH_IP";
case OFPUTIL_C_STP: return "STP";
case OFPUTIL_C_GROUP_STATS: return "GROUP_STATS";
+ case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED";
}
return NULL;
}
static void
-ofp_print_switch_features(struct ds *string,
- const struct ofp_switch_features *osf)
+ofp_print_switch_features(struct ds *string, const struct ofp_header *oh)
{
struct ofputil_switch_features features;
- struct ofputil_phy_port *ports;
enum ofperr error;
struct ofpbuf b;
- size_t n_ports;
- size_t i;
- error = ofputil_decode_switch_features(osf, &features, &b);
+ error = ofputil_decode_switch_features(oh, &features, &b);
if (error) {
ofp_print_error(string, error);
return;
ds_put_cstr(string, "capabilities: ");
ofp_print_bit_names(string, features.capabilities,
- ofputil_capabilities_to_name);
- ds_put_char(string, '\n');
-
- ds_put_cstr(string, "actions: ");
- ofp_print_bit_names(string, features.actions,
- ofputil_action_bitmap_to_name);
+ ofputil_capabilities_to_name, ' ');
ds_put_char(string, '\n');
- n_ports = ofputil_count_phy_ports(osf);
-
- ports = xmalloc(n_ports * sizeof *ports);
- for (i = 0; i < n_ports; i++) {
- error = ofputil_pull_switch_features_port(&b, &ports[i]);
- if (error) {
- ofp_print_error(string, error);
- return;
- }
- }
- qsort(ports, n_ports, sizeof *ports, compare_ports);
- for (i = 0; i < n_ports; i++) {
- ofp_print_phy_port(string, &ports[i]);
+ switch ((enum ofp_version)oh->version) {
+ case OFP10_VERSION:
+ ds_put_cstr(string, "actions: ");
+ ofp_print_bit_names(string, features.actions,
+ ofputil_action_bitmap_to_name, ' ');
+ ds_put_char(string, '\n');
+ break;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ break;
+ default:
+ NOT_REACHED();
}
- free(ports);
+
+ ofp_print_phy_ports(string, oh->version, &b);
}
static void
}
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,");
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) == ',') {
}
static void
-ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
- enum ofputil_msg_code code, int verbosity)
+ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
{
struct ofputil_flow_mod fm;
+ struct ofpbuf ofpacts;
bool need_priority;
enum ofperr error;
+ enum ofpraw raw;
+ enum ofputil_protocol protocol;
+
+ protocol = ofputil_protocol_from_ofp_version(oh->version);
+ protocol = ofputil_protocol_set_tid(protocol, true);
- error = ofputil_decode_flow_mod(&fm, oh, OFPUTIL_P_OF10_TID);
+ ofpbuf_init(&ofpacts, 64);
+ error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts);
if (error) {
+ ofpbuf_uninit(&ofpacts);
ofp_print_error(s, error);
return;
}
}
ds_put_char(s, ' ');
- if (verbosity >= 3 && code == OFPUTIL_OFPT_FLOW_MOD) {
- const struct ofp_flow_mod *ofm = (const struct ofp_flow_mod *) oh;
- ofp_print_match(s, &ofm->match, verbosity);
+ ofpraw_decode(&raw, oh);
+ if (verbosity >= 3 && raw == OFPRAW_OFPT10_FLOW_MOD) {
+ const struct ofp10_flow_mod *ofm = ofpmsg_body(oh);
+ ofp10_match_print(s, &ofm->match, verbosity);
/* ofp_print_match() doesn't print priority. */
need_priority = true;
- } else if (verbosity >= 3 && code == OFPUTIL_NXT_FLOW_MOD) {
- const struct nx_flow_mod *nfm = (const struct nx_flow_mod *) oh;
+ } else if (verbosity >= 3 && raw == OFPRAW_NXT_FLOW_MOD) {
+ const struct nx_flow_mod *nfm = ofpmsg_body(oh);
const void *nxm = nfm + 1;
char *nxm_s;
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);
if (fm.buffer_id != UINT32_MAX) {
ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id);
}
+ if (fm.out_port != OFPP_NONE) {
+ ds_put_format(s, "out_port:");
+ ofputil_format_port(fm.out_port, s);
+ ds_put_char(s, ' ');
+ }
if (fm.flags != 0) {
uint16_t flags = fm.flags;
if (flags & OFPFF_CHECK_OVERLAP) {
ds_put_cstr(s, "check_overlap ");
}
- if (flags & OFPFF_EMERG) {
+ if (flags & OFPFF10_EMERG) {
ds_put_cstr(s, "emerg ");
}
- flags &= ~(OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF_EMERG);
+ flags &= ~(OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF10_EMERG);
if (flags) {
ds_put_format(s, "flags:0x%"PRIx16" ", flags);
}
}
- ofp_print_actions(s, fm.actions, fm.n_actions);
+ ofpacts_format(fm.ofpacts, fm.ofpacts_len, s);
+ ofpbuf_uninit(&ofpacts);
}
static void
return "delete";
case OFPRR_GROUP_DELETE:
return "group_delete";
+ case OFPRR_EVICTION:
+ return "eviction";
default:
sprintf(s, "%d", (int) reason);
return s;
}
ds_put_cstr(string, " duration");
ofp_print_duration(string, fr.duration_sec, fr.duration_nsec);
- ds_put_format(string, " idle%"PRIu16" pkts%"PRIu64" bytes%"PRIu64"\n",
- fr.idle_timeout, fr.packet_count, fr.byte_count);
+ ds_put_format(string, " idle%"PRIu16, fr.idle_timeout);
+ if (fr.hard_timeout) {
+ /* The hard timeout was only added in OF1.2, so only print it if it is
+ * actually in use to avoid gratuitous change to the formatting. */
+ ds_put_format(string, " hard%"PRIu16, fr.hard_timeout);
+ }
+ ds_put_format(string, " pkts%"PRIu64" bytes%"PRIu64"\n",
+ fr.packet_count, fr.byte_count);
}
static void
}
static void
-ofp_print_error_msg(struct ds *string, const struct ofp_error_msg *oem)
+ofp_print_error_msg(struct ds *string, const struct ofp_header *oh)
{
- size_t len = ntohs(oem->header.length);
- size_t payload_ofs, payload_len;
- const void *payload;
+ size_t len = ntohs(oh->length);
+ struct ofpbuf payload;
enum ofperr error;
char *s;
- error = ofperr_decode_msg(&oem->header, &payload_ofs);
+ error = ofperr_decode_msg(oh, &payload);
if (!error) {
ds_put_cstr(string, "***decode error***");
- ds_put_hex_dump(string, oem->data, len - sizeof *oem, 0, true);
+ ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true);
return;
}
ds_put_format(string, " %s\n", ofperr_get_name(error));
- payload = (const uint8_t *) oem + payload_ofs;
- payload_len = len - payload_ofs;
if (error == OFPERR_OFPHFC_INCOMPATIBLE || error == OFPERR_OFPHFC_EPERM) {
- ds_put_printable(string, payload, payload_len);
+ ds_put_printable(string, payload.data, payload.size);
} else {
- s = ofp_to_string(payload, payload_len, 1);
+ s = ofp_to_string(payload.data, payload.size, 1);
ds_put_cstr(string, s);
free(s);
}
}
static void
-ofp_print_port_status(struct ds *string, const struct ofp_port_status *ops)
+ofp_print_port_status(struct ds *string, const struct ofp_header *oh)
{
struct ofputil_port_status ps;
enum ofperr error;
- error = ofputil_decode_port_status(ops, &ps);
+ error = ofputil_decode_port_status(oh, &ps);
if (error) {
ofp_print_error(string, error);
return;
}
static void
-ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_desc_stats *ods)
+ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_header *oh)
{
+ const struct ofp_desc_stats *ods = ofpmsg_body(oh);
+
ds_put_char(string, '\n');
ds_put_format(string, "Manufacturer: %.*s\n",
(int) sizeof ods->mfr_desc, ods->mfr_desc);
}
static void
-ofp_print_flow_stats_request(struct ds *string,
- const struct ofp_stats_msg *osm)
+ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh)
{
struct ofputil_flow_stats_request fsr;
enum ofperr error;
- error = ofputil_decode_flow_stats_request(&fsr, &osm->header);
+ error = ofputil_decode_flow_stats_request(&fsr, oh);
if (error) {
ofp_print_error(string, error);
return;
cls_rule_format(&fsr.match, string);
}
+void
+ofp_print_flow_stats(struct ds *string, struct ofputil_flow_stats *fs)
+{
+ ds_put_format(string, " cookie=0x%"PRIx64", duration=",
+ ntohll(fs->cookie));
+
+ ofp_print_duration(string, fs->duration_sec, fs->duration_nsec);
+ ds_put_format(string, ", table=%"PRIu8", ", fs->table_id);
+ ds_put_format(string, "n_packets=%"PRIu64", ", fs->packet_count);
+ ds_put_format(string, "n_bytes=%"PRIu64", ", fs->byte_count);
+ if (fs->idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, "idle_timeout=%"PRIu16", ", fs->idle_timeout);
+ }
+ if (fs->hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, "hard_timeout=%"PRIu16", ", fs->hard_timeout);
+ }
+ if (fs->idle_age >= 0) {
+ ds_put_format(string, "idle_age=%d, ", fs->idle_age);
+ }
+ if (fs->hard_age >= 0 && fs->hard_age != fs->duration_sec) {
+ ds_put_format(string, "hard_age=%d, ", fs->hard_age);
+ }
+
+ cls_rule_format(&fs->rule, string);
+ if (string->string[string->length - 1] != ' ') {
+ ds_put_char(string, ' ');
+ }
+
+ ofpacts_format(fs->ofpacts, fs->ofpacts_len, string);
+}
+
static void
ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
{
+ struct ofpbuf ofpacts;
struct ofpbuf b;
ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpbuf_init(&ofpacts, 64);
for (;;) {
struct ofputil_flow_stats fs;
int retval;
- retval = ofputil_decode_flow_stats_reply(&fs, &b, true);
+ retval = ofputil_decode_flow_stats_reply(&fs, &b, true, &ofpacts);
if (retval) {
if (retval != EOF) {
ds_put_cstr(string, " ***parse error***");
}
break;
}
-
ds_put_char(string, '\n');
-
- ds_put_format(string, " cookie=0x%"PRIx64", duration=",
- ntohll(fs.cookie));
- ofp_print_duration(string, fs.duration_sec, fs.duration_nsec);
- ds_put_format(string, ", table=%"PRIu8", ", fs.table_id);
- ds_put_format(string, "n_packets=%"PRIu64", ", fs.packet_count);
- ds_put_format(string, "n_bytes=%"PRIu64", ", fs.byte_count);
- if (fs.idle_timeout != OFP_FLOW_PERMANENT) {
- ds_put_format(string, "idle_timeout=%"PRIu16",", fs.idle_timeout);
- }
- if (fs.hard_timeout != OFP_FLOW_PERMANENT) {
- ds_put_format(string, "hard_timeout=%"PRIu16",", fs.hard_timeout);
- }
- if (fs.idle_age >= 0) {
- ds_put_format(string, "idle_age=%d,", fs.idle_age);
- }
- if (fs.hard_age >= 0 && fs.hard_age != fs.duration_sec) {
- ds_put_format(string, "hard_age=%d,", fs.hard_age);
- }
-
- cls_rule_format(&fs.rule, string);
- if (string->string[string->length - 1] != ' ') {
- ds_put_char(string, ' ');
- }
- ofp_print_actions(string, fs.actions, fs.n_actions);
+ ofp_print_flow_stats(string, &fs);
}
+ ofpbuf_uninit(&ofpacts);
}
static void
-ofp_print_ofpst_aggregate_reply(struct ds *string,
- const struct ofp_aggregate_stats_reply *asr)
+ofp_print_aggregate_stats_reply(struct ds *string, const struct ofp_header *oh)
{
- ds_put_format(string, " packet_count=%"PRIu64,
- ntohll(get_32aligned_be64(&asr->packet_count)));
- ds_put_format(string, " byte_count=%"PRIu64,
- ntohll(get_32aligned_be64(&asr->byte_count)));
- ds_put_format(string, " flow_count=%"PRIu32, ntohl(asr->flow_count));
-}
+ struct ofputil_aggregate_stats as;
+ enum ofperr error;
-static void
-ofp_print_nxst_aggregate_reply(struct ds *string,
- const struct nx_aggregate_stats_reply *nasr)
-{
- ds_put_format(string, " packet_count=%"PRIu64, ntohll(nasr->packet_count));
- ds_put_format(string, " byte_count=%"PRIu64, ntohll(nasr->byte_count));
- ds_put_format(string, " flow_count=%"PRIu32, ntohl(nasr->flow_count));
+ error = ofputil_decode_aggregate_stats_reply(&as, oh);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
+ }
+
+ ds_put_format(string, " packet_count=%"PRIu64, as.packet_count);
+ ds_put_format(string, " byte_count=%"PRIu64, as.byte_count);
+ ds_put_format(string, " flow_count=%"PRIu32, as.flow_count);
}
static void print_port_stat(struct ds *string, const char *leader,
}
static void
-ofp_print_ofpst_port_request(struct ds *string,
- const struct ofp_port_stats_request *psr)
+ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh)
{
+ const struct ofp10_port_stats_request *psr = ofpmsg_body(oh);
ds_put_format(string, " port_no=%"PRIu16, ntohs(psr->port_no));
}
ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
int verbosity)
{
- const struct ofp_port_stats *ps = ofputil_stats_body(oh);
- size_t n = ofputil_stats_body_len(oh) / sizeof *ps;
+ struct ofp10_port_stats *ps;
+ struct ofpbuf b;
+ size_t n;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ n = b.size / sizeof *ps;
ds_put_format(string, " %zu ports\n", n);
if (verbosity < 1) {
return;
}
- for (; n--; ps++) {
+ for (;;) {
+ ps = ofpbuf_try_pull(&b, sizeof *ps);
+ if (!ps) {
+ return;
+ }
+
ds_put_format(string, " port %2"PRIu16": ", ntohs(ps->port_no));
ds_put_cstr(string, "rx ");
}
static void
-ofp_print_ofpst_table_reply(struct ds *string, const struct ofp_header *oh,
- int verbosity)
+ofp_print_one_ofpst_table_reply(struct ds *string, enum ofp_version ofp_version,
+ const char *name, struct ofp12_table_stats *ts)
{
- const struct ofp_table_stats *ts = ofputil_stats_body(oh);
- size_t n = ofputil_stats_body_len(oh) / sizeof *ts;
+ char name_[OFP_MAX_TABLE_NAME_LEN + 1];
+
+ ovs_strlcpy(name_, name, sizeof name_);
+
+ ds_put_format(string, " %d: %-8s: ", ts->table_id, name_);
+ ds_put_format(string, "wild=0x%05"PRIx64", ", ntohll(ts->wildcards));
+ ds_put_format(string, "max=%6"PRIu32", ", ntohl(ts->max_entries));
+ ds_put_format(string, "active=%"PRIu32"\n", ntohl(ts->active_count));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "lookup=%"PRIu64", ", ntohll(ts->lookup_count));
+ ds_put_format(string, "matched=%"PRIu64"\n", ntohll(ts->matched_count));
+
+ if (ofp_version < OFP11_VERSION) {
+ return;
+ }
+
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "match=0x%08"PRIx64", ", ntohll(ts->match));
+ ds_put_format(string, "instructions=0x%08"PRIx32", ",
+ ntohl(ts->instructions));
+ ds_put_format(string, "config=0x%08"PRIx32"\n", ntohl(ts->config));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "write_actions=0x%08"PRIx32", ",
+ ntohl(ts->write_actions));
+ ds_put_format(string, "apply_actions=0x%08"PRIx32"\n",
+ ntohl(ts->apply_actions));
+
+ if (ofp_version < OFP12_VERSION) {
+ return;
+ }
+
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "write_setfields=0x%016"PRIx64"\n",
+ ntohll(ts->write_setfields));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "apply_setfields=0x%016"PRIx64"\n",
+ ntohll(ts->apply_setfields));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "metadata_match=0x%016"PRIx64"\n",
+ ntohll(ts->metadata_match));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "metadata_write=0x%016"PRIx64"\n",
+ ntohll(ts->metadata_write));
+}
+
+static void
+ofp_print_ofpst_table_reply12(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
+{
+ struct ofp12_table_stats *ts;
+ struct ofpbuf b;
+ size_t n;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ n = b.size / sizeof *ts;
ds_put_format(string, " %zu tables\n", n);
if (verbosity < 1) {
return;
}
- for (; n--; ts++) {
- char name[OFP_MAX_TABLE_NAME_LEN + 1];
- ovs_strlcpy(name, ts->name, sizeof name);
+ for (;;) {
+ ts = ofpbuf_try_pull(&b, sizeof *ts);
+ if (!ts) {
+ return;
+ }
- ds_put_format(string, " %d: %-8s: ", ts->table_id, name);
- ds_put_format(string, "wild=0x%05"PRIx32", ", ntohl(ts->wildcards));
- ds_put_format(string, "max=%6"PRIu32", ", ntohl(ts->max_entries));
- ds_put_format(string, "active=%"PRIu32"\n", ntohl(ts->active_count));
- ds_put_cstr(string, " ");
- ds_put_format(string, "lookup=%"PRIu64", ",
- ntohll(get_32aligned_be64(&ts->lookup_count)));
- ds_put_format(string, "matched=%"PRIu64"\n",
- ntohll(get_32aligned_be64(&ts->matched_count)));
+ ofp_print_one_ofpst_table_reply(string, OFP12_VERSION, ts->name, ts);
}
}
+static void
+ofp_print_ofpst_table_reply11(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
+{
+ struct ofp11_table_stats *ts;
+ struct ofpbuf b;
+ size_t n;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ n = b.size / sizeof *ts;
+ ds_put_format(string, " %zu tables\n", n);
+ if (verbosity < 1) {
+ return;
+ }
+
+ for (;;) {
+ struct ofp12_table_stats ts12;
+
+ ts = ofpbuf_try_pull(&b, sizeof *ts);
+ if (!ts) {
+ return;
+ }
+
+ ts12.table_id = ts->table_id;
+ ts12.wildcards = htonll(ntohl(ts->wildcards));
+ ts12.max_entries = ts->max_entries;
+ ts12.active_count = ts->active_count;
+ ts12.lookup_count = ts->lookup_count;
+ ts12.matched_count = ts->matched_count;
+ ts12.match = htonll(ntohl(ts->match));
+ ts12.instructions = ts->instructions;
+ ts12.config = ts->config;
+ ts12.write_actions = ts->write_actions;
+ ts12.apply_actions = ts->apply_actions;
+ ofp_print_one_ofpst_table_reply(string, OFP11_VERSION, ts->name, &ts12);
+ }
+}
+
+static void
+ofp_print_ofpst_table_reply10(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
+{
+ struct ofp10_table_stats *ts;
+ struct ofpbuf b;
+ size_t n;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ n = b.size / sizeof *ts;
+ ds_put_format(string, " %zu tables\n", n);
+ if (verbosity < 1) {
+ return;
+ }
+
+ for (;;) {
+ struct ofp12_table_stats ts12;
+
+ ts = ofpbuf_try_pull(&b, sizeof *ts);
+ if (!ts) {
+ return;
+ }
+
+ ts12.table_id = ts->table_id;
+ ts12.wildcards = htonll(ntohl(ts->wildcards));
+ ts12.max_entries = ts->max_entries;
+ ts12.active_count = ts->active_count;
+ ts12.lookup_count = get_32aligned_be64(&ts->lookup_count);
+ ts12.matched_count = get_32aligned_be64(&ts->matched_count);
+ ofp_print_one_ofpst_table_reply(string, OFP10_VERSION, ts->name, &ts12);
+ }
+}
+
+static void
+ofp_print_ofpst_table_reply(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
+{
+ switch ((enum ofp_version)oh->version) {
+ case OFP12_VERSION:
+ ofp_print_ofpst_table_reply12(string, oh, verbosity);
+ break;
+
+ case OFP11_VERSION:
+ ofp_print_ofpst_table_reply11(string, oh, verbosity);
+ break;
+
+ case OFP10_VERSION:
+ ofp_print_ofpst_table_reply10(string, oh, verbosity);
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+}
+
static void
ofp_print_queue_name(struct ds *string, uint32_t queue_id)
{
}
static void
-ofp_print_ofpst_queue_request(struct ds *string,
- const struct ofp_queue_stats_request *qsr)
+ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh)
{
+ const struct ofp10_queue_stats_request *qsr = ofpmsg_body(oh);
+
ds_put_cstr(string, "port=");
ofputil_format_port(ntohs(qsr->port_no), string);
ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh,
int verbosity)
{
- const struct ofp_queue_stats *qs = ofputil_stats_body(oh);
- size_t n = ofputil_stats_body_len(oh) / sizeof *qs;
+ struct ofp10_queue_stats *qs;
+ struct ofpbuf b;
+ size_t n;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ n = b.size / sizeof *qs;
ds_put_format(string, " %zu queues\n", n);
if (verbosity < 1) {
return;
}
- for (; n--; qs++) {
+ for (;;) {
+ qs = ofpbuf_try_pull(&b, sizeof *qs);
+ if (!qs) {
+ return;
+ }
+
ds_put_cstr(string, " port ");
ofputil_format_port(ntohs(qs->port_no), string);
ds_put_cstr(string, " queue ");
}
}
+static void
+ofp_print_ofpst_port_desc_reply(struct ds *string,
+ const struct ofp_header *oh)
+{
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+ ds_put_char(string, '\n');
+ ofp_print_phy_ports(string, oh->version, &b);
+}
+
static void
ofp_print_stats_request(struct ds *string, const struct ofp_header *oh)
{
- const struct ofp_stats_msg *srq = (const struct ofp_stats_msg *) oh;
+ uint16_t flags = ofpmp_flags(oh);
- if (srq->flags) {
- ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***",
- ntohs(srq->flags));
+ if (flags) {
+ ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags);
}
}
static void
ofp_print_stats_reply(struct ds *string, const struct ofp_header *oh)
{
- const struct ofp_stats_msg *srp = (const struct ofp_stats_msg *) oh;
-
- if (srp->flags) {
- uint16_t flags = ntohs(srp->flags);
+ uint16_t flags = ofpmp_flags(oh);
+ if (flags) {
ds_put_cstr(string, " flags=");
if (flags & OFPSF_REPLY_MORE) {
ds_put_cstr(string, "[more]");
}
static void
-ofp_to_string__(const struct ofp_header *oh,
- const struct ofputil_msg_type *type, struct ds *string,
- int verbosity)
+ofp_print_nxt_flow_monitor_cancel(struct ds *string,
+ const struct ofp_header *oh)
{
- enum ofputil_msg_code code;
- const void *msg = oh;
+ ds_put_format(string, " id=%"PRIu32,
+ ofputil_decode_flow_monitor_cancel(oh));
+}
+
+static const char *
+nx_flow_monitor_flags_to_name(uint32_t bit)
+{
+ enum nx_flow_monitor_flags fmf = bit;
+
+ switch (fmf) {
+ case NXFMF_INITIAL: return "initial";
+ case NXFMF_ADD: return "add";
+ case NXFMF_DELETE: return "delete";
+ case NXFMF_MODIFY: return "modify";
+ case NXFMF_ACTIONS: return "actions";
+ case NXFMF_OWN: return "own";
+ }
+
+ return NULL;
+}
+
+static void
+ofp_print_nxst_flow_monitor_request(struct ds *string,
+ const struct ofp_header *oh)
+{
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ for (;;) {
+ struct ofputil_flow_monitor_request request;
+ int retval;
+
+ retval = ofputil_decode_flow_monitor_request(&request, &b);
+ if (retval) {
+ if (retval != EOF) {
+ ofp_print_error(string, retval);
+ }
+ return;
+ }
+
+ ds_put_format(string, "\n id=%"PRIu32" flags=", request.id);
+ ofp_print_bit_names(string, request.flags,
+ nx_flow_monitor_flags_to_name, ',');
+
+ if (request.out_port != OFPP_NONE) {
+ ds_put_cstr(string, " out_port=");
+ ofputil_format_port(request.out_port, string);
+ }
+
+ if (request.table_id != 0xff) {
+ ds_put_format(string, " table=%"PRIu8, request.table_id);
+ }
+
+ ds_put_char(string, ' ');
+ cls_rule_format(&request.match, string);
+ ds_chomp(string, ' ');
+ }
+}
+
+static void
+ofp_print_nxst_flow_monitor_reply(struct ds *string,
+ const struct ofp_header *oh)
+{
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ for (;;) {
+ struct ofputil_flow_update update;
+ struct cls_rule match;
+ int retval;
+
+ update.match = &match;
+ retval = ofputil_decode_flow_update(&update, &b, &ofpacts);
+ if (retval) {
+ if (retval != EOF) {
+ ofp_print_error(string, retval);
+ }
+ ofpbuf_uninit(&ofpacts);
+ return;
+ }
+
+ ds_put_cstr(string, "\n event=");
+ switch (update.event) {
+ case NXFME_ADDED:
+ ds_put_cstr(string, "ADDED");
+ break;
+
+ case NXFME_DELETED:
+ ds_put_format(string, "DELETED reason=%s",
+ ofp_flow_removed_reason_to_string(update.reason));
+ break;
+
+ case NXFME_MODIFIED:
+ ds_put_cstr(string, "MODIFIED");
+ break;
+
+ case NXFME_ABBREV:
+ ds_put_format(string, "ABBREV xid=0x%"PRIx32, ntohl(update.xid));
+ continue;
+ }
+
+ ds_put_format(string, " table=%"PRIu8, update.table_id);
+ if (update.idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, " idle_timeout=%"PRIu16,
+ update.idle_timeout);
+ }
+ if (update.hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, " hard_timeout=%"PRIu16,
+ update.hard_timeout);
+ }
+ ds_put_format(string, " cookie=%#"PRIx64, ntohll(update.cookie));
- ds_put_cstr(string, ofputil_msg_type_name(type));
+ ds_put_char(string, ' ');
+ cls_rule_format(update.match, string);
+
+ if (update.ofpacts_len) {
+ if (string->string[string->length - 1] != ' ') {
+ ds_put_char(string, ' ');
+ }
+ ofpacts_format(update.ofpacts, update.ofpacts_len, string);
+ }
+ }
+}
+
+void
+ofp_print_version(const struct ofp_header *oh,
+ struct ds *string)
+{
switch (oh->version) {
case OFP10_VERSION:
break;
case OFP11_VERSION:
ds_put_cstr(string, " (OF1.1)");
break;
+ case OFP12_VERSION:
+ ds_put_cstr(string, " (OF1.2)");
+ break;
default:
ds_put_format(string, " (OF 0x%02"PRIx8")", oh->version);
break;
}
ds_put_format(string, " (xid=0x%"PRIx32"):", ntohl(oh->xid));
+}
- code = ofputil_msg_type_code(type);
- switch (code) {
- case OFPUTIL_MSG_INVALID:
- break;
+static void
+ofp_header_to_string__(const struct ofp_header *oh, enum ofpraw raw,
+ struct ds *string)
+{
+ ds_put_cstr(string, ofpraw_get_name(raw));
+ ofp_print_version(oh, string);
+}
+
+static void
+ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
+ struct ds *string, int verbosity)
+{
+ const void *msg = oh;
- case OFPUTIL_OFPT_HELLO:
+ ofp_header_to_string__(oh, raw, string);
+ switch (ofptype_from_ofpraw(raw)) {
+ case OFPTYPE_HELLO:
ds_put_char(string, '\n');
ds_put_hex_dump(string, oh + 1, ntohs(oh->length) - sizeof *oh,
0, true);
break;
- case OFPUTIL_OFPT_ERROR:
- ofp_print_error_msg(string, msg);
+ case OFPTYPE_ERROR:
+ ofp_print_error_msg(string, oh);
break;
- case OFPUTIL_OFPT_ECHO_REQUEST:
- case OFPUTIL_OFPT_ECHO_REPLY:
+ case OFPTYPE_ECHO_REQUEST:
+ case OFPTYPE_ECHO_REPLY:
ofp_print_echo(string, oh, verbosity);
break;
- case OFPUTIL_OFPT_FEATURES_REQUEST:
- break;
-
- case OFPUTIL_OFPT_FEATURES_REPLY:
- ofp_print_switch_features(string, msg);
+ case OFPTYPE_FEATURES_REQUEST:
break;
- case OFPUTIL_OFPT_GET_CONFIG_REQUEST:
+ case OFPTYPE_FEATURES_REPLY:
+ ofp_print_switch_features(string, oh);
break;
- case OFPUTIL_OFPT_GET_CONFIG_REPLY:
- case OFPUTIL_OFPT_SET_CONFIG:
- ofp_print_switch_config(string, msg);
+ case OFPTYPE_GET_CONFIG_REQUEST:
break;
- case OFPUTIL_OFPT_PACKET_IN:
- case OFPUTIL_NXT_PACKET_IN:
- ofp_print_packet_in(string, msg, verbosity);
+ case OFPTYPE_GET_CONFIG_REPLY:
+ case OFPTYPE_SET_CONFIG:
+ ofp_print_switch_config(string, ofpmsg_body(oh));
break;
- case OFPUTIL_OFPT_FLOW_REMOVED:
- case OFPUTIL_NXT_FLOW_REMOVED:
- ofp_print_flow_removed(string, msg);
+ case OFPTYPE_PACKET_IN:
+ ofp_print_packet_in(string, oh, verbosity);
break;
- case OFPUTIL_OFPT_PORT_STATUS:
- ofp_print_port_status(string, msg);
+ case OFPTYPE_FLOW_REMOVED:
+ ofp_print_flow_removed(string, oh);
break;
- case OFPUTIL_OFPT_PACKET_OUT:
- ofp_print_packet_out(string, msg, verbosity);
+ case OFPTYPE_PORT_STATUS:
+ ofp_print_port_status(string, oh);
break;
- case OFPUTIL_OFPT_FLOW_MOD:
- case OFPUTIL_NXT_FLOW_MOD:
- ofp_print_flow_mod(string, msg, code, verbosity);
+ case OFPTYPE_PACKET_OUT:
+ ofp_print_packet_out(string, oh, verbosity);
break;
- case OFPUTIL_OFPT_PORT_MOD:
- ofp_print_port_mod(string, msg);
+ case OFPTYPE_FLOW_MOD:
+ ofp_print_flow_mod(string, oh, verbosity);
break;
- case OFPUTIL_OFPT_BARRIER_REQUEST:
- case OFPUTIL_OFPT_BARRIER_REPLY:
+ case OFPTYPE_PORT_MOD:
+ ofp_print_port_mod(string, oh);
break;
- case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
- case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
- /* XXX */
+ case OFPTYPE_BARRIER_REQUEST:
+ case OFPTYPE_BARRIER_REPLY:
break;
- case OFPUTIL_OFPST_DESC_REQUEST:
+ case OFPTYPE_DESC_STATS_REQUEST:
+ case OFPTYPE_PORT_DESC_STATS_REQUEST:
ofp_print_stats_request(string, oh);
break;
- case OFPUTIL_OFPST_FLOW_REQUEST:
- case OFPUTIL_NXST_FLOW_REQUEST:
- case OFPUTIL_OFPST_AGGREGATE_REQUEST:
- case OFPUTIL_NXST_AGGREGATE_REQUEST:
+ case OFPTYPE_FLOW_STATS_REQUEST:
+ case OFPTYPE_AGGREGATE_STATS_REQUEST:
ofp_print_stats_request(string, oh);
- ofp_print_flow_stats_request(string, msg);
+ ofp_print_flow_stats_request(string, oh);
break;
- case OFPUTIL_OFPST_TABLE_REQUEST:
+ case OFPTYPE_TABLE_STATS_REQUEST:
ofp_print_stats_request(string, oh);
break;
- case OFPUTIL_OFPST_PORT_REQUEST:
+ case OFPTYPE_PORT_STATS_REQUEST:
ofp_print_stats_request(string, oh);
- ofp_print_ofpst_port_request(string, msg);
+ ofp_print_ofpst_port_request(string, oh);
break;
- case OFPUTIL_OFPST_QUEUE_REQUEST:
+ case OFPTYPE_QUEUE_STATS_REQUEST:
ofp_print_stats_request(string, oh);
- ofp_print_ofpst_queue_request(string, msg);
+ ofp_print_ofpst_queue_request(string, oh);
break;
- case OFPUTIL_OFPST_DESC_REPLY:
+ case OFPTYPE_DESC_STATS_REPLY:
ofp_print_stats_reply(string, oh);
- ofp_print_ofpst_desc_reply(string, msg);
+ ofp_print_ofpst_desc_reply(string, oh);
break;
- case OFPUTIL_OFPST_FLOW_REPLY:
- case OFPUTIL_NXST_FLOW_REPLY:
+ case OFPTYPE_FLOW_STATS_REPLY:
ofp_print_stats_reply(string, oh);
ofp_print_flow_stats_reply(string, oh);
break;
- case OFPUTIL_OFPST_QUEUE_REPLY:
+ case OFPTYPE_QUEUE_STATS_REPLY:
ofp_print_stats_reply(string, oh);
ofp_print_ofpst_queue_reply(string, oh, verbosity);
break;
- case OFPUTIL_OFPST_PORT_REPLY:
+ case OFPTYPE_PORT_STATS_REPLY:
ofp_print_stats_reply(string, oh);
ofp_print_ofpst_port_reply(string, oh, verbosity);
break;
- case OFPUTIL_OFPST_TABLE_REPLY:
+ case OFPTYPE_TABLE_STATS_REPLY:
ofp_print_stats_reply(string, oh);
ofp_print_ofpst_table_reply(string, oh, verbosity);
break;
- case OFPUTIL_OFPST_AGGREGATE_REPLY:
+ case OFPTYPE_AGGREGATE_STATS_REPLY:
ofp_print_stats_reply(string, oh);
- ofp_print_ofpst_aggregate_reply(string, msg);
+ ofp_print_aggregate_stats_reply(string, oh);
break;
- case OFPUTIL_NXT_ROLE_REQUEST:
- case OFPUTIL_NXT_ROLE_REPLY:
- ofp_print_nxt_role_message(string, msg);
+ case OFPTYPE_PORT_DESC_STATS_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_ofpst_port_desc_reply(string, oh);
break;
- case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
- ofp_print_nxt_flow_mod_table_id(string, msg);
+ case OFPTYPE_ROLE_REQUEST:
+ case OFPTYPE_ROLE_REPLY:
+ ofp_print_nxt_role_message(string, ofpmsg_body(oh));
break;
- case OFPUTIL_NXT_SET_FLOW_FORMAT:
- ofp_print_nxt_set_flow_format(string, msg);
+ case OFPTYPE_FLOW_MOD_TABLE_ID:
+ ofp_print_nxt_flow_mod_table_id(string, ofpmsg_body(oh));
break;
- case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
- ofp_print_nxt_set_packet_in_format(string, msg);
+ case OFPTYPE_SET_FLOW_FORMAT:
+ ofp_print_nxt_set_flow_format(string, ofpmsg_body(oh));
break;
- case OFPUTIL_NXT_FLOW_AGE:
+ case OFPTYPE_SET_PACKET_IN_FORMAT:
+ ofp_print_nxt_set_packet_in_format(string, ofpmsg_body(oh));
break;
- case OFPUTIL_NXT_SET_CONTROLLER_ID:
- ofp_print_nxt_set_controller_id(string, msg);
+ case OFPTYPE_FLOW_AGE:
break;
- case OFPUTIL_NXT_SET_ASYNC_CONFIG:
- ofp_print_nxt_set_async_config(string, msg);
+ case OFPTYPE_SET_CONTROLLER_ID:
+ ofp_print_nxt_set_controller_id(string, ofpmsg_body(oh));
break;
- case OFPUTIL_NXST_AGGREGATE_REPLY:
- ofp_print_stats_reply(string, oh);
- ofp_print_nxst_aggregate_reply(string, msg);
+ case OFPTYPE_SET_ASYNC_CONFIG:
+ ofp_print_nxt_set_async_config(string, ofpmsg_body(oh));
+ break;
+
+ case OFPTYPE_FLOW_MONITOR_CANCEL:
+ ofp_print_nxt_flow_monitor_cancel(string, msg);
+ break;
+
+ case OFPTYPE_FLOW_MONITOR_PAUSED:
+ case OFPTYPE_FLOW_MONITOR_RESUMED:
+ break;
+
+ case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+ ofp_print_nxst_flow_monitor_request(string, msg);
+ break;
+
+ case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
+ ofp_print_nxst_flow_monitor_reply(string, msg);
break;
}
}
ds_put_format(&string, "OpenFlow packet too short (only %zu bytes):\n",
len);
} else if (ntohs(oh->length) > len) {
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = ofpraw_decode_partial(&raw, oh, len);
+ if (!error) {
+ ofp_header_to_string__(oh, raw, &string);
+ ds_put_char(&string, '\n');
+ }
+
ds_put_format(&string,
"(***truncated to %zu bytes from %"PRIu16"***)\n",
len, ntohs(oh->length));
"(***only uses %"PRIu16" bytes out of %zu***)\n",
ntohs(oh->length), len);
} else {
- const struct ofputil_msg_type *type;
enum ofperr error;
+ enum ofpraw raw;
- error = ofputil_decode_msg_type(oh, &type);
+ error = ofpraw_decode(&raw, oh);
if (!error) {
- ofp_to_string__(oh, type, &string, verbosity);
+ ofp_to_string__(oh, raw, &string, verbosity);
if (verbosity >= 5) {
if (ds_last(&string) != '\n') {
ds_put_char(&string, '\n');
/*
- * Copyright (c) 2008, 2009, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdint.h>
#include <stdio.h>
-struct ofp_flow_mod;
-struct ofp_match;
struct ds;
-union ofp_action;
+struct ofp10_match;
+struct ofp_flow_mod;
+struct ofp_header;
+struct ofputil_flow_stats;
#ifdef __cplusplus
extern "C" {
void ofp_print(FILE *, const void *, size_t, int verbosity);
void ofp_print_packet(FILE *stream, const void *data, size_t len);
-void ofp_print_actions(struct ds *, const union ofp_action *, size_t);
-void ofp_print_match(struct ds *, const struct ofp_match *, int verbosity);
+void ofp10_match_print(struct ds *, const struct ofp10_match *, int verbosity);
char *ofp_to_string(const void *, size_t, int verbosity);
-char *ofp_match_to_string(const struct ofp_match *, int verbosity);
+char *ofp10_match_to_string(const struct ofp10_match *, int verbosity);
char *ofp_packet_to_string(const void *data, size_t len);
+void ofp_print_flow_stats(struct ds *, struct ofputil_flow_stats *);
+void ofp_print_version(const struct ofp_header *, struct ds *);
+
#ifdef __cplusplus
}
#endif
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "multipath.h"
#include "netdev.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
+#include "ofp-msgs.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "packets.h"
}
/* 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
#undef WC_INVARIANT_BIT
;
-/* Converts the wildcard in 'ofpfw' into a flow_wildcards in 'wc' for use in
- * struct cls_rule. It is the caller's responsibility to handle the special
- * case where the flow match's dl_vlan is set to OFP_VLAN_NONE. */
+/* Converts the OpenFlow 1.0 wildcards in 'ofpfw' (OFPFW10_*) into a
+ * flow_wildcards in 'wc' for use in struct cls_rule. It is the caller's
+ * responsibility to handle the special case where the flow match's dl_vlan is
+ * set to OFP_VLAN_NONE. */
void
-ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc)
+ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
/* Initialize most of rule->wc. */
flow_wildcards_init_catchall(wc);
wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
- /* Wildcard fields that aren't defined by ofp_match or tun_id. */
- wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_ECN | FWW_NW_TTL
- | FWW_IPV6_LABEL);
+ /* Wildcard fields that aren't defined by ofp10_match. */
+ wc->wildcards |= FWW_NW_ECN | FWW_NW_TTL;
- if (ofpfw & OFPFW_NW_TOS) {
+ if (ofpfw & OFPFW10_NW_TOS) {
/* OpenFlow 1.0 defines a TOS wildcard, but it's much later in
* the enum than we can use. */
wc->wildcards |= FWW_NW_DSCP;
}
- wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT);
- wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT);
+ wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_SRC_SHIFT);
+ wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_DST_SHIFT);
- if (!(ofpfw & OFPFW_TP_SRC)) {
+ if (!(ofpfw & OFPFW10_TP_SRC)) {
wc->tp_src_mask = htons(UINT16_MAX);
}
- if (!(ofpfw & OFPFW_TP_DST)) {
+ if (!(ofpfw & OFPFW10_TP_DST)) {
wc->tp_dst_mask = htons(UINT16_MAX);
}
- if (ofpfw & OFPFW_DL_DST) {
- /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, but
- * Open vSwitch breaks the Ethernet destination into bits as FWW_DL_DST
- * and FWW_ETH_MCAST. */
- wc->wildcards |= FWW_ETH_MCAST;
+ if (!(ofpfw & OFPFW10_DL_SRC)) {
+ memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN);
+ }
+ if (!(ofpfw & OFPFW10_DL_DST)) {
+ memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN);
}
/* VLAN TCI mask. */
- if (!(ofpfw & OFPFW_DL_VLAN_PCP)) {
+ if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) {
wc->vlan_tci_mask |= htons(VLAN_PCP_MASK | VLAN_CFI);
}
- if (!(ofpfw & OFPFW_DL_VLAN)) {
+ if (!(ofpfw & OFPFW10_DL_VLAN)) {
wc->vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
}
}
-/* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
- * 'priority'. */
+/* Converts the ofp10_match in 'match' into a cls_rule in 'rule', with the
+ * given 'priority'. */
void
-ofputil_cls_rule_from_match(const struct ofp_match *match,
- unsigned int priority, struct cls_rule *rule)
+ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *match,
+ unsigned int priority, struct cls_rule *rule)
{
- uint32_t ofpfw = ntohl(match->wildcards) & OFPFW_ALL;
+ uint32_t ofpfw = ntohl(match->wildcards) & OFPFW10_ALL;
/* Initialize rule->priority, rule->wc. */
rule->priority = !ofpfw ? UINT16_MAX : priority;
- ofputil_wildcard_from_openflow(ofpfw, &rule->wc);
+ ofputil_wildcard_from_ofpfw10(ofpfw, &rule->wc);
/* Initialize most of rule->flow. */
rule->flow.nw_src = match->nw_src;
rule->flow.nw_proto = match->nw_proto;
/* Translate VLANs. */
- if (!(ofpfw & OFPFW_DL_VLAN) && match->dl_vlan == htons(OFP_VLAN_NONE)) {
+ if (!(ofpfw & OFPFW10_DL_VLAN) &&
+ match->dl_vlan == htons(OFP10_VLAN_NONE)) {
/* Match only packets without 802.1Q header.
*
- * When OFPFW_DL_VLAN_PCP is wildcarded, this is obviously correct.
+ * When OFPFW10_DL_VLAN_PCP is wildcarded, this is obviously correct.
*
- * If OFPFW_DL_VLAN_PCP is matched, the flow match is contradictory,
+ * If OFPFW10_DL_VLAN_PCP is matched, the flow match is contradictory,
* because we can't have a specific PCP without an 802.1Q header.
* However, older versions of OVS treated this as matching packets
* withut an 802.1Q header, so we do here too. */
cls_rule_zero_wildcarded_fields(rule);
}
-/* Convert 'rule' into the OpenFlow match structure 'match'. */
+/* Convert 'rule' into the OpenFlow 1.0 match structure 'match'. */
void
-ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
+ofputil_cls_rule_to_ofp10_match(const struct cls_rule *rule,
+ struct ofp10_match *match)
{
const struct flow_wildcards *wc = &rule->wc;
uint32_t ofpfw;
/* Figure out most OpenFlow wildcards. */
ofpfw = (OVS_FORCE uint32_t) (wc->wildcards & WC_INVARIANTS);
- ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT;
- ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT;
+ ofpfw |= (ofputil_netmask_to_wcbits(wc->nw_src_mask)
+ << OFPFW10_NW_SRC_SHIFT);
+ ofpfw |= (ofputil_netmask_to_wcbits(wc->nw_dst_mask)
+ << OFPFW10_NW_DST_SHIFT);
if (wc->wildcards & FWW_NW_DSCP) {
- ofpfw |= OFPFW_NW_TOS;
+ ofpfw |= OFPFW10_NW_TOS;
}
if (!wc->tp_src_mask) {
- ofpfw |= OFPFW_TP_SRC;
+ ofpfw |= OFPFW10_TP_SRC;
}
if (!wc->tp_dst_mask) {
- ofpfw |= OFPFW_TP_DST;
+ ofpfw |= OFPFW10_TP_DST;
+ }
+ if (eth_addr_is_zero(wc->dl_src_mask)) {
+ ofpfw |= OFPFW10_DL_SRC;
+ }
+ if (eth_addr_is_zero(wc->dl_dst_mask)) {
+ ofpfw |= OFPFW10_DL_DST;
}
/* Translate VLANs. */
match->dl_vlan = htons(0);
match->dl_vlan_pcp = 0;
if (rule->wc.vlan_tci_mask == htons(0)) {
- ofpfw |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP;
+ ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP;
} else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
&& !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
- match->dl_vlan = htons(OFP_VLAN_NONE);
+ match->dl_vlan = htons(OFP10_VLAN_NONE);
+ ofpfw |= OFPFW10_DL_VLAN_PCP;
} else {
if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
- ofpfw |= OFPFW_DL_VLAN;
+ ofpfw |= OFPFW10_DL_VLAN;
} else {
match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci));
}
if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) {
- ofpfw |= OFPFW_DL_VLAN_PCP;
+ ofpfw |= OFPFW10_DL_VLAN_PCP;
} else {
match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci);
}
memset(match->pad2, '\0', sizeof match->pad2);
}
-/* Given a 'dl_type' value in the format used in struct flow, returns the
- * corresponding 'dl_type' value for use in an OpenFlow ofp_match structure. */
-ovs_be16
-ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type)
-{
- return (flow_dl_type == htons(FLOW_DL_TYPE_NONE)
- ? htons(OFP_DL_TYPE_NOT_ETH_TYPE)
- : flow_dl_type);
-}
-
-/* Given a 'dl_type' value in the format used in an OpenFlow ofp_match
- * structure, returns the corresponding 'dl_type' value for use in struct
- * flow. */
-ovs_be16
-ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type)
-{
- return (ofp_dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
- ? htons(FLOW_DL_TYPE_NONE)
- : ofp_dl_type);
-}
-
-/* Returns a transaction ID to use for an outgoing OpenFlow message. */
-static ovs_be32
-alloc_xid(void)
+enum ofperr
+ofputil_pull_ofp11_match(struct ofpbuf *buf, unsigned int priority,
+ struct cls_rule *rule, uint16_t *padded_match_len)
{
- static uint32_t next_xid = 1;
- return htonl(next_xid++);
-}
-\f
-/* Basic parsing of OpenFlow messages. */
-
-struct ofputil_msg_type {
- enum ofputil_msg_code code; /* OFPUTIL_*. */
- uint8_t ofp_version; /* An OpenFlow version or 0 for "any". */
- uint32_t value; /* OFPT_*, OFPST_*, NXT_*, or NXST_*. */
- const char *name; /* e.g. "OFPT_FLOW_REMOVED". */
- unsigned int min_size; /* Minimum total message size in bytes. */
- /* 0 if 'min_size' is the exact size that the message must be. Otherwise,
- * the message may exceed 'min_size' by an even multiple of this value. */
- unsigned int extra_multiple;
-};
+ struct ofp11_match_header *omh = buf->data;
+ uint16_t match_len;
-/* Represents a malformed OpenFlow message. */
-static const struct ofputil_msg_type ofputil_invalid_type = {
- OFPUTIL_MSG_INVALID, 0, 0, "OFPUTIL_MSG_INVALID", 0, 0
-};
+ if (buf->size < sizeof *omh) {
+ return OFPERR_OFPBMC_BAD_LEN;
+ }
-struct ofputil_msg_category {
- const char *name; /* e.g. "OpenFlow message" */
- const struct ofputil_msg_type *types;
- size_t n_types;
- enum ofperr missing_error; /* Error value for missing type. */
-};
+ match_len = ntohs(omh->length);
-static enum ofperr
-ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size)
-{
- switch (type->extra_multiple) {
- case 0:
- if (size != type->min_size) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
- "length %u (expected length %u)",
- type->name, size, type->min_size);
- return OFPERR_OFPBRC_BAD_LEN;
- }
- return 0;
+ switch (ntohs(omh->type)) {
+ case OFPMT_STANDARD: {
+ struct ofp11_match *om;
- case 1:
- if (size < type->min_size) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
- "length %u (expected length at least %u bytes)",
- type->name, size, type->min_size);
- return OFPERR_OFPBRC_BAD_LEN;
+ if (match_len != sizeof *om || buf->size < sizeof *om) {
+ return OFPERR_OFPBMC_BAD_LEN;
}
- return 0;
-
- default:
- if (size < type->min_size
- || (size - type->min_size) % type->extra_multiple) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
- "length %u (must be exactly %u bytes or longer "
- "by an integer multiple of %u bytes)",
- type->name, size,
- type->min_size, type->extra_multiple);
- return OFPERR_OFPBRC_BAD_LEN;
+ om = ofpbuf_pull(buf, sizeof *om);
+ if (padded_match_len) {
+ *padded_match_len = match_len;
}
- return 0;
+ return ofputil_cls_rule_from_ofp11_match(om, priority, rule);
}
-}
-
-static enum ofperr
-ofputil_lookup_openflow_message(const struct ofputil_msg_category *cat,
- uint8_t version, uint32_t value,
- const struct ofputil_msg_type **typep)
-{
- const struct ofputil_msg_type *type;
- for (type = cat->types; type < &cat->types[cat->n_types]; type++) {
- if (type->value == value
- && (!type->ofp_version || version == type->ofp_version)) {
- *typep = type;
- return 0;
+ case OFPMT_OXM:
+ if (padded_match_len) {
+ *padded_match_len = ROUND_UP(match_len, 8);
}
- }
+ return oxm_pull_match(buf, priority, rule);
- VLOG_WARN_RL(&bad_ofmsg_rl, "received %s of unknown type %"PRIu32,
- cat->name, value);
- return cat->missing_error;
+ default:
+ return OFPERR_OFPBMC_BAD_TYPE;
+ }
}
-static enum ofperr
-ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
- const struct ofputil_msg_type **typep)
-{
- static const struct ofputil_msg_type nxt_messages[] = {
- { OFPUTIL_NXT_ROLE_REQUEST, OFP10_VERSION,
- NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST",
- sizeof(struct nx_role_request), 0 },
-
- { OFPUTIL_NXT_ROLE_REPLY, OFP10_VERSION,
- NXT_ROLE_REPLY, "NXT_ROLE_REPLY",
- sizeof(struct nx_role_request), 0 },
-
- { OFPUTIL_NXT_SET_FLOW_FORMAT, OFP10_VERSION,
- NXT_SET_FLOW_FORMAT, "NXT_SET_FLOW_FORMAT",
- sizeof(struct nx_set_flow_format), 0 },
-
- { OFPUTIL_NXT_SET_PACKET_IN_FORMAT, OFP10_VERSION,
- NXT_SET_PACKET_IN_FORMAT, "NXT_SET_PACKET_IN_FORMAT",
- sizeof(struct nx_set_packet_in_format), 0 },
-
- { OFPUTIL_NXT_PACKET_IN, OFP10_VERSION,
- NXT_PACKET_IN, "NXT_PACKET_IN",
- sizeof(struct nx_packet_in), 1 },
-
- { OFPUTIL_NXT_FLOW_MOD, OFP10_VERSION,
- NXT_FLOW_MOD, "NXT_FLOW_MOD",
- sizeof(struct nx_flow_mod), 8 },
-
- { OFPUTIL_NXT_FLOW_REMOVED, OFP10_VERSION,
- NXT_FLOW_REMOVED, "NXT_FLOW_REMOVED",
- sizeof(struct nx_flow_removed), 8 },
-
- { OFPUTIL_NXT_FLOW_MOD_TABLE_ID, OFP10_VERSION,
- NXT_FLOW_MOD_TABLE_ID, "NXT_FLOW_MOD_TABLE_ID",
- sizeof(struct nx_flow_mod_table_id), 0 },
-
- { OFPUTIL_NXT_FLOW_AGE, OFP10_VERSION,
- NXT_FLOW_AGE, "NXT_FLOW_AGE",
- sizeof(struct nicira_header), 0 },
-
- { OFPUTIL_NXT_SET_ASYNC_CONFIG, OFP10_VERSION,
- NXT_SET_ASYNC_CONFIG, "NXT_SET_ASYNC_CONFIG",
- sizeof(struct nx_async_config), 0 },
-
- { OFPUTIL_NXT_SET_CONTROLLER_ID, OFP10_VERSION,
- NXT_SET_CONTROLLER_ID, "NXT_SET_CONTROLLER_ID",
- sizeof(struct nx_controller_id), 0 },
- };
+/* Converts the ofp11_match in 'match' into a cls_rule in 'rule', with the
+ * given 'priority'. Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match,
+ unsigned int priority,
+ struct cls_rule *rule)
+{
+ uint16_t wc = ntohl(match->wildcards);
+ uint8_t dl_src_mask[ETH_ADDR_LEN];
+ uint8_t dl_dst_mask[ETH_ADDR_LEN];
+ bool ipv4, arp;
+ int i;
- static const struct ofputil_msg_category nxt_category = {
- "Nicira extension message",
- nxt_messages, ARRAY_SIZE(nxt_messages),
- OFPERR_OFPBRC_BAD_SUBTYPE
- };
+ cls_rule_init_catchall(rule, priority);
- const struct ofp_vendor_header *ovh;
- const struct nicira_header *nh;
+ if (!(wc & OFPFW11_IN_PORT)) {
+ uint16_t ofp_port;
+ enum ofperr error;
- if (length < sizeof(struct ofp_vendor_header)) {
- if (length == ntohs(oh->length)) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor message");
+ error = ofputil_port_from_ofp11(match->in_port, &ofp_port);
+ if (error) {
+ return OFPERR_OFPBMC_BAD_VALUE;
}
- return OFPERR_OFPBRC_BAD_LEN;
+ cls_rule_set_in_port(rule, ofp_port);
}
- ovh = (const struct ofp_vendor_header *) oh;
- if (ovh->vendor != htonl(NX_VENDOR_ID)) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor message for unknown "
- "vendor %"PRIx32, ntohl(ovh->vendor));
- return OFPERR_OFPBRC_BAD_VENDOR;
+ for (i = 0; i < ETH_ADDR_LEN; i++) {
+ dl_src_mask[i] = ~match->dl_src_mask[i];
}
+ cls_rule_set_dl_src_masked(rule, match->dl_src, dl_src_mask);
- if (length < sizeof(struct nicira_header)) {
- if (length == ntohs(oh->length)) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "received Nicira vendor message of "
- "length %u (expected at least %zu)",
- ntohs(ovh->header.length),
- sizeof(struct nicira_header));
- }
- return OFPERR_OFPBRC_BAD_LEN;
+ for (i = 0; i < ETH_ADDR_LEN; i++) {
+ dl_dst_mask[i] = ~match->dl_dst_mask[i];
}
+ cls_rule_set_dl_dst_masked(rule, match->dl_dst, dl_dst_mask);
- nh = (const struct nicira_header *) oh;
- return ofputil_lookup_openflow_message(&nxt_category, oh->version,
- ntohl(nh->subtype), typep);
-}
-
-static enum ofperr
-check_nxstats_msg(const struct ofp_header *oh, size_t length)
-{
- const struct ofp_stats_msg *osm = (const struct ofp_stats_msg *) oh;
- ovs_be32 vendor;
+ if (!(wc & OFPFW11_DL_VLAN)) {
+ if (match->dl_vlan == htons(OFPVID11_NONE)) {
+ /* Match only packets without a VLAN tag. */
+ rule->flow.vlan_tci = htons(0);
+ rule->wc.vlan_tci_mask = htons(UINT16_MAX);
+ } else {
+ if (match->dl_vlan == htons(OFPVID11_ANY)) {
+ /* Match any packet with a VLAN tag regardless of VID. */
+ rule->flow.vlan_tci = htons(VLAN_CFI);
+ rule->wc.vlan_tci_mask = htons(VLAN_CFI);
+ } else if (ntohs(match->dl_vlan) < 4096) {
+ /* Match only packets with the specified VLAN VID. */
+ rule->flow.vlan_tci = htons(VLAN_CFI) | match->dl_vlan;
+ rule->wc.vlan_tci_mask = htons(VLAN_CFI | VLAN_VID_MASK);
+ } else {
+ /* Invalid VID. */
+ return OFPERR_OFPBMC_BAD_VALUE;
+ }
- if (length < sizeof(struct ofp_vendor_stats_msg)) {
- if (length == ntohs(oh->length)) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor stats message");
+ if (!(wc & OFPFW11_DL_VLAN_PCP)) {
+ if (match->dl_vlan_pcp <= 7) {
+ rule->flow.vlan_tci |= htons(match->dl_vlan_pcp
+ << VLAN_PCP_SHIFT);
+ rule->wc.vlan_tci_mask |= htons(VLAN_PCP_MASK);
+ } else {
+ /* Invalid PCP. */
+ return OFPERR_OFPBMC_BAD_VALUE;
+ }
+ }
}
- return OFPERR_OFPBRC_BAD_LEN;
- }
-
- memcpy(&vendor, osm + 1, sizeof vendor);
- if (vendor != htonl(NX_VENDOR_ID)) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor stats message for "
- "unknown vendor %"PRIx32, ntohl(vendor));
- return OFPERR_OFPBRC_BAD_VENDOR;
}
- if (length < sizeof(struct nicira_stats_msg)) {
- if (length == ntohs(osm->header.length)) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "truncated Nicira stats message");
- }
- return OFPERR_OFPBRC_BAD_LEN;
+ if (!(wc & OFPFW11_DL_TYPE)) {
+ cls_rule_set_dl_type(rule,
+ ofputil_dl_type_from_openflow(match->dl_type));
}
- return 0;
-}
-
-static enum ofperr
-ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length,
- const struct ofputil_msg_type **typep)
-{
- static const struct ofputil_msg_type nxst_requests[] = {
- { OFPUTIL_NXST_FLOW_REQUEST, OFP10_VERSION,
- NXST_FLOW, "NXST_FLOW request",
- sizeof(struct nx_flow_stats_request), 8 },
-
- { OFPUTIL_NXST_AGGREGATE_REQUEST, OFP10_VERSION,
- NXST_AGGREGATE, "NXST_AGGREGATE request",
- sizeof(struct nx_aggregate_stats_request), 8 },
- };
-
- static const struct ofputil_msg_category nxst_request_category = {
- "Nicira extension statistics request",
- nxst_requests, ARRAY_SIZE(nxst_requests),
- OFPERR_OFPBRC_BAD_SUBTYPE
- };
+ ipv4 = rule->flow.dl_type == htons(ETH_TYPE_IP);
+ arp = rule->flow.dl_type == htons(ETH_TYPE_ARP);
- const struct nicira_stats_msg *nsm;
- enum ofperr error;
+ if (ipv4 && !(wc & OFPFW11_NW_TOS)) {
+ if (match->nw_tos & ~IP_DSCP_MASK) {
+ /* Invalid TOS. */
+ return OFPERR_OFPBMC_BAD_VALUE;
+ }
- error = check_nxstats_msg(oh, length);
- if (error) {
- return error;
+ cls_rule_set_nw_dscp(rule, match->nw_tos);
}
- nsm = (struct nicira_stats_msg *) oh;
- return ofputil_lookup_openflow_message(&nxst_request_category, oh->version,
- ntohl(nsm->subtype), typep);
-}
-
-static enum ofperr
-ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length,
- const struct ofputil_msg_type **typep)
-{
- static const struct ofputil_msg_type nxst_replies[] = {
- { OFPUTIL_NXST_FLOW_REPLY, OFP10_VERSION,
- NXST_FLOW, "NXST_FLOW reply",
- sizeof(struct nicira_stats_msg), 8 },
+ if (ipv4 || arp) {
+ if (!(wc & OFPFW11_NW_PROTO)) {
+ cls_rule_set_nw_proto(rule, match->nw_proto);
+ }
+ cls_rule_set_nw_src_masked(rule, match->nw_src, ~match->nw_src_mask);
+ cls_rule_set_nw_dst_masked(rule, match->nw_dst, ~match->nw_dst_mask);
+ }
- { OFPUTIL_NXST_AGGREGATE_REPLY, OFP10_VERSION,
- NXST_AGGREGATE, "NXST_AGGREGATE reply",
- sizeof(struct nx_aggregate_stats_reply), 0 },
- };
+#define OFPFW11_TP_ALL (OFPFW11_TP_SRC | OFPFW11_TP_DST)
+ if (ipv4 && (wc & OFPFW11_TP_ALL) != OFPFW11_TP_ALL) {
+ switch (rule->flow.nw_proto) {
+ case IPPROTO_ICMP:
+ /* "A.2.3 Flow Match Structures" in OF1.1 says:
+ *
+ * The tp_src and tp_dst fields will be ignored unless the
+ * network protocol specified is as TCP, UDP or SCTP.
+ *
+ * but I'm pretty sure we should support ICMP too, otherwise
+ * that's a regression from OF1.0. */
+ if (!(wc & OFPFW11_TP_SRC)) {
+ uint16_t icmp_type = ntohs(match->tp_src);
+ if (icmp_type < 0x100) {
+ cls_rule_set_icmp_type(rule, icmp_type);
+ } else {
+ return OFPERR_OFPBMC_BAD_FIELD;
+ }
+ }
+ if (!(wc & OFPFW11_TP_DST)) {
+ uint16_t icmp_code = ntohs(match->tp_dst);
+ if (icmp_code < 0x100) {
+ cls_rule_set_icmp_code(rule, icmp_code);
+ } else {
+ return OFPERR_OFPBMC_BAD_FIELD;
+ }
+ }
+ break;
- static const struct ofputil_msg_category nxst_reply_category = {
- "Nicira extension statistics reply",
- nxst_replies, ARRAY_SIZE(nxst_replies),
- OFPERR_OFPBRC_BAD_SUBTYPE
- };
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ if (!(wc & (OFPFW11_TP_SRC))) {
+ cls_rule_set_tp_src(rule, match->tp_src);
+ }
+ if (!(wc & (OFPFW11_TP_DST))) {
+ cls_rule_set_tp_dst(rule, match->tp_dst);
+ }
+ break;
- const struct nicira_stats_msg *nsm;
- enum ofperr error;
+ case IPPROTO_SCTP:
+ /* We don't support SCTP and it seems that we should tell the
+ * controller, since OF1.1 implementations are supposed to. */
+ return OFPERR_OFPBMC_BAD_FIELD;
- error = check_nxstats_msg(oh, length);
- if (error) {
- return error;
+ default:
+ /* OF1.1 says explicitly to ignore this. */
+ break;
+ }
}
- nsm = (struct nicira_stats_msg *) oh;
- return ofputil_lookup_openflow_message(&nxst_reply_category, oh->version,
- ntohl(nsm->subtype), typep);
-}
+ if (rule->flow.dl_type == htons(ETH_TYPE_MPLS) ||
+ rule->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ enum { OFPFW11_MPLS_ALL = OFPFW11_MPLS_LABEL | OFPFW11_MPLS_TC };
-static enum ofperr
-check_stats_msg(const struct ofp_header *oh, size_t length)
-{
- if (length < sizeof(struct ofp_stats_msg)) {
- if (length == ntohs(oh->length)) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "truncated stats message");
+ if ((wc & OFPFW11_MPLS_ALL) != OFPFW11_MPLS_ALL) {
+ /* MPLS not supported. */
+ return OFPERR_OFPBMC_BAD_TAG;
}
- return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ if (match->metadata_mask != htonll(UINT64_MAX)) {
+ cls_rule_set_metadata_masked(rule, match->metadata,
+ ~match->metadata_mask);
}
return 0;
}
-static enum ofperr
-ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length,
- const struct ofputil_msg_type **typep)
+/* Convert 'rule' into the OpenFlow 1.1 match structure 'match'. */
+void
+ofputil_cls_rule_to_ofp11_match(const struct cls_rule *rule,
+ struct ofp11_match *match)
{
- static const struct ofputil_msg_type ofpst_requests[] = {
- { OFPUTIL_OFPST_DESC_REQUEST, OFP10_VERSION,
- OFPST_DESC, "OFPST_DESC request",
- sizeof(struct ofp_stats_msg), 0 },
-
- { OFPUTIL_OFPST_FLOW_REQUEST, OFP10_VERSION,
- OFPST_FLOW, "OFPST_FLOW request",
- sizeof(struct ofp_flow_stats_request), 0 },
-
- { OFPUTIL_OFPST_AGGREGATE_REQUEST, OFP10_VERSION,
- OFPST_AGGREGATE, "OFPST_AGGREGATE request",
- sizeof(struct ofp_flow_stats_request), 0 },
-
- { OFPUTIL_OFPST_TABLE_REQUEST, OFP10_VERSION,
- OFPST_TABLE, "OFPST_TABLE request",
- sizeof(struct ofp_stats_msg), 0 },
-
- { OFPUTIL_OFPST_PORT_REQUEST, OFP10_VERSION,
- OFPST_PORT, "OFPST_PORT request",
- sizeof(struct ofp_port_stats_request), 0 },
-
- { OFPUTIL_OFPST_QUEUE_REQUEST, OFP10_VERSION,
- OFPST_QUEUE, "OFPST_QUEUE request",
- sizeof(struct ofp_queue_stats_request), 0 },
-
- { 0, 0,
- OFPST_VENDOR, "OFPST_VENDOR request",
- sizeof(struct ofp_vendor_stats_msg), 1 },
- };
-
- static const struct ofputil_msg_category ofpst_request_category = {
- "OpenFlow statistics",
- ofpst_requests, ARRAY_SIZE(ofpst_requests),
- OFPERR_OFPBRC_BAD_STAT
- };
+ uint32_t wc = 0;
+ int i;
- const struct ofp_stats_msg *request = (const struct ofp_stats_msg *) oh;
- enum ofperr error;
+ memset(match, 0, sizeof *match);
+ match->omh.type = htons(OFPMT_STANDARD);
+ match->omh.length = htons(OFPMT11_STANDARD_LENGTH);
- error = check_stats_msg(oh, length);
- if (error) {
- return error;
+ if (rule->wc.wildcards & FWW_IN_PORT) {
+ wc |= OFPFW11_IN_PORT;
+ } else {
+ match->in_port = ofputil_port_to_ofp11(rule->flow.in_port);
}
- error = ofputil_lookup_openflow_message(&ofpst_request_category,
- oh->version, ntohs(request->type),
- typep);
- if (!error && request->type == htons(OFPST_VENDOR)) {
- error = ofputil_decode_nxst_request(oh, length, typep);
+ memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
+ for (i = 0; i < ETH_ADDR_LEN; i++) {
+ match->dl_src_mask[i] = ~rule->wc.dl_src_mask[i];
}
- return error;
-}
-
-static enum ofperr
-ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length,
- const struct ofputil_msg_type **typep)
-{
- static const struct ofputil_msg_type ofpst_replies[] = {
- { OFPUTIL_OFPST_DESC_REPLY, OFP10_VERSION,
- OFPST_DESC, "OFPST_DESC reply",
- sizeof(struct ofp_desc_stats), 0 },
-
- { OFPUTIL_OFPST_FLOW_REPLY, OFP10_VERSION,
- OFPST_FLOW, "OFPST_FLOW reply",
- sizeof(struct ofp_stats_msg), 1 },
- { OFPUTIL_OFPST_AGGREGATE_REPLY, OFP10_VERSION,
- OFPST_AGGREGATE, "OFPST_AGGREGATE reply",
- sizeof(struct ofp_aggregate_stats_reply), 0 },
-
- { OFPUTIL_OFPST_TABLE_REPLY, OFP10_VERSION,
- OFPST_TABLE, "OFPST_TABLE reply",
- sizeof(struct ofp_stats_msg), sizeof(struct ofp_table_stats) },
-
- { OFPUTIL_OFPST_PORT_REPLY, OFP10_VERSION,
- OFPST_PORT, "OFPST_PORT reply",
- sizeof(struct ofp_stats_msg), sizeof(struct ofp_port_stats) },
-
- { OFPUTIL_OFPST_QUEUE_REPLY, OFP10_VERSION,
- OFPST_QUEUE, "OFPST_QUEUE reply",
- sizeof(struct ofp_stats_msg), sizeof(struct ofp_queue_stats) },
-
- { 0, 0,
- OFPST_VENDOR, "OFPST_VENDOR reply",
- sizeof(struct ofp_vendor_stats_msg), 1 },
- };
-
- static const struct ofputil_msg_category ofpst_reply_category = {
- "OpenFlow statistics",
- ofpst_replies, ARRAY_SIZE(ofpst_replies),
- OFPERR_OFPBRC_BAD_STAT
- };
+ memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN);
+ for (i = 0; i < ETH_ADDR_LEN; i++) {
+ match->dl_dst_mask[i] = ~rule->wc.dl_dst_mask[i];
+ }
- const struct ofp_stats_msg *reply = (const struct ofp_stats_msg *) oh;
- enum ofperr error;
+ if (rule->wc.vlan_tci_mask == htons(0)) {
+ wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP;
+ } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
+ && !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
+ match->dl_vlan = htons(OFPVID11_NONE);
+ wc |= OFPFW11_DL_VLAN_PCP;
+ } else {
+ if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
+ match->dl_vlan = htons(OFPVID11_ANY);
+ } else {
+ match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci));
+ }
- error = check_stats_msg(oh, length);
- if (error) {
- return error;
+ if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) {
+ wc |= OFPFW11_DL_VLAN_PCP;
+ } else {
+ match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci);
+ }
}
- error = ofputil_lookup_openflow_message(&ofpst_reply_category, oh->version,
- ntohs(reply->type), typep);
- if (!error && reply->type == htons(OFPST_VENDOR)) {
- error = ofputil_decode_nxst_reply(oh, length, typep);
+ if (rule->wc.wildcards & FWW_DL_TYPE) {
+ wc |= OFPFW11_DL_TYPE;
+ } else {
+ match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type);
}
- return error;
-}
-
-static enum ofperr
-ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length,
- const struct ofputil_msg_type **typep)
-{
- static const struct ofputil_msg_type ofpt_messages[] = {
- { OFPUTIL_OFPT_HELLO, OFP10_VERSION,
- OFPT_HELLO, "OFPT_HELLO",
- sizeof(struct ofp_hello), 1 },
-
- { OFPUTIL_OFPT_ERROR, 0,
- OFPT_ERROR, "OFPT_ERROR",
- sizeof(struct ofp_error_msg), 1 },
-
- { OFPUTIL_OFPT_ECHO_REQUEST, OFP10_VERSION,
- OFPT_ECHO_REQUEST, "OFPT_ECHO_REQUEST",
- sizeof(struct ofp_header), 1 },
-
- { OFPUTIL_OFPT_ECHO_REPLY, OFP10_VERSION,
- OFPT_ECHO_REPLY, "OFPT_ECHO_REPLY",
- sizeof(struct ofp_header), 1 },
-
- { OFPUTIL_OFPT_FEATURES_REQUEST, OFP10_VERSION,
- OFPT_FEATURES_REQUEST, "OFPT_FEATURES_REQUEST",
- sizeof(struct ofp_header), 0 },
-
- { OFPUTIL_OFPT_FEATURES_REPLY, OFP10_VERSION,
- OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY",
- sizeof(struct ofp_switch_features), sizeof(struct ofp10_phy_port) },
- { OFPUTIL_OFPT_FEATURES_REPLY, OFP11_VERSION,
- OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY",
- sizeof(struct ofp_switch_features), sizeof(struct ofp11_port) },
-
- { OFPUTIL_OFPT_GET_CONFIG_REQUEST, OFP10_VERSION,
- OFPT_GET_CONFIG_REQUEST, "OFPT_GET_CONFIG_REQUEST",
- sizeof(struct ofp_header), 0 },
-
- { OFPUTIL_OFPT_GET_CONFIG_REPLY, OFP10_VERSION,
- OFPT_GET_CONFIG_REPLY, "OFPT_GET_CONFIG_REPLY",
- sizeof(struct ofp_switch_config), 0 },
-
- { OFPUTIL_OFPT_SET_CONFIG, OFP10_VERSION,
- OFPT_SET_CONFIG, "OFPT_SET_CONFIG",
- sizeof(struct ofp_switch_config), 0 },
-
- { OFPUTIL_OFPT_PACKET_IN, OFP10_VERSION,
- OFPT_PACKET_IN, "OFPT_PACKET_IN",
- offsetof(struct ofp_packet_in, data), 1 },
-
- { OFPUTIL_OFPT_FLOW_REMOVED, OFP10_VERSION,
- OFPT_FLOW_REMOVED, "OFPT_FLOW_REMOVED",
- sizeof(struct ofp_flow_removed), 0 },
-
- { OFPUTIL_OFPT_PORT_STATUS, OFP10_VERSION,
- OFPT_PORT_STATUS, "OFPT_PORT_STATUS",
- sizeof(struct ofp_port_status) + sizeof(struct ofp10_phy_port), 0 },
- { OFPUTIL_OFPT_PORT_STATUS, OFP11_VERSION,
- OFPT_PORT_STATUS, "OFPT_PORT_STATUS",
- sizeof(struct ofp_port_status) + sizeof(struct ofp11_port), 0 },
-
- { OFPUTIL_OFPT_PACKET_OUT, OFP10_VERSION,
- OFPT10_PACKET_OUT, "OFPT_PACKET_OUT",
- sizeof(struct ofp_packet_out), 1 },
-
- { OFPUTIL_OFPT_FLOW_MOD, OFP10_VERSION,
- OFPT10_FLOW_MOD, "OFPT_FLOW_MOD",
- sizeof(struct ofp_flow_mod), 1 },
-
- { OFPUTIL_OFPT_PORT_MOD, OFP10_VERSION,
- OFPT10_PORT_MOD, "OFPT_PORT_MOD",
- sizeof(struct ofp10_port_mod), 0 },
- { OFPUTIL_OFPT_PORT_MOD, OFP11_VERSION,
- OFPT11_PORT_MOD, "OFPT_PORT_MOD",
- sizeof(struct ofp11_port_mod), 0 },
-
- { 0, OFP10_VERSION,
- OFPT10_STATS_REQUEST, "OFPT_STATS_REQUEST",
- sizeof(struct ofp_stats_msg), 1 },
-
- { 0, OFP10_VERSION,
- OFPT10_STATS_REPLY, "OFPT_STATS_REPLY",
- sizeof(struct ofp_stats_msg), 1 },
-
- { OFPUTIL_OFPT_BARRIER_REQUEST, OFP10_VERSION,
- OFPT10_BARRIER_REQUEST, "OFPT_BARRIER_REQUEST",
- sizeof(struct ofp_header), 0 },
-
- { OFPUTIL_OFPT_BARRIER_REPLY, OFP10_VERSION,
- OFPT10_BARRIER_REPLY, "OFPT_BARRIER_REPLY",
- sizeof(struct ofp_header), 0 },
-
- { 0, 0,
- OFPT_VENDOR, "OFPT_VENDOR",
- sizeof(struct ofp_vendor_header), 1 },
- };
-
- static const struct ofputil_msg_category ofpt_category = {
- "OpenFlow message",
- ofpt_messages, ARRAY_SIZE(ofpt_messages),
- OFPERR_OFPBRC_BAD_TYPE
- };
- enum ofperr error;
+ if (rule->wc.wildcards & FWW_NW_DSCP) {
+ wc |= OFPFW11_NW_TOS;
+ } else {
+ match->nw_tos = rule->flow.nw_tos & IP_DSCP_MASK;
+ }
- error = ofputil_lookup_openflow_message(&ofpt_category, oh->version,
- oh->type, typep);
- if (!error) {
- switch ((oh->version << 8) | oh->type) {
- case (OFP10_VERSION << 8) | OFPT_VENDOR:
- case (OFP11_VERSION << 8) | OFPT_VENDOR:
- error = ofputil_decode_vendor(oh, length, typep);
- break;
+ if (rule->wc.wildcards & FWW_NW_PROTO) {
+ wc |= OFPFW11_NW_PROTO;
+ } else {
+ match->nw_proto = rule->flow.nw_proto;
+ }
- case (OFP10_VERSION << 8) | OFPT10_STATS_REQUEST:
- case (OFP11_VERSION << 8) | OFPT11_STATS_REQUEST:
- error = ofputil_decode_ofpst_request(oh, length, typep);
- break;
+ match->nw_src = rule->flow.nw_src;
+ match->nw_src_mask = ~rule->wc.nw_src_mask;
+ match->nw_dst = rule->flow.nw_dst;
+ match->nw_dst_mask = ~rule->wc.nw_dst_mask;
- case (OFP10_VERSION << 8) | OFPT10_STATS_REPLY:
- case (OFP11_VERSION << 8) | OFPT11_STATS_REPLY:
- error = ofputil_decode_ofpst_reply(oh, length, typep);
+ if (!rule->wc.tp_src_mask) {
+ wc |= OFPFW11_TP_SRC;
+ } else {
+ match->tp_src = rule->flow.tp_src;
+ }
- default:
- break;
- }
+ if (!rule->wc.tp_dst_mask) {
+ wc |= OFPFW11_TP_DST;
+ } else {
+ match->tp_dst = rule->flow.tp_dst;
}
- return error;
-}
-/* Decodes the message type represented by 'oh'. Returns 0 if successful or an
- * OpenFlow error code on failure. Either way, stores in '*typep' a type
- * structure that can be inspected with the ofputil_msg_type_*() functions.
- *
- * oh->length must indicate the correct length of the message (and must be at
- * least sizeof(struct ofp_header)).
- *
- * Success indicates that 'oh' is at least as long as the minimum-length
- * message of its type. */
-enum ofperr
-ofputil_decode_msg_type(const struct ofp_header *oh,
- const struct ofputil_msg_type **typep)
-{
- size_t length = ntohs(oh->length);
- enum ofperr error;
+ /* MPLS not supported. */
+ wc |= OFPFW11_MPLS_LABEL;
+ wc |= OFPFW11_MPLS_TC;
- error = ofputil_decode_msg_type__(oh, length, typep);
- if (!error) {
- error = ofputil_check_length(*typep, length);
- }
- if (error) {
- *typep = &ofputil_invalid_type;
- }
- return error;
+ match->metadata = rule->flow.metadata;
+ match->metadata_mask = ~rule->wc.metadata_mask;
+
+ match->wildcards = htonl(wc);
}
-/* Decodes the message type represented by 'oh', of which only the first
- * 'length' bytes are available. Returns 0 if successful or an OpenFlow error
- * code on failure. Either way, stores in '*typep' a type structure that can
- * be inspected with the ofputil_msg_type_*() functions. */
-enum ofperr
-ofputil_decode_msg_type_partial(const struct ofp_header *oh, size_t length,
- const struct ofputil_msg_type **typep)
+/* Given a 'dl_type' value in the format used in struct flow, returns the
+ * corresponding 'dl_type' value for use in an ofp10_match or ofp11_match
+ * structure. */
+ovs_be16
+ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type)
{
- enum ofperr error;
-
- error = (length >= sizeof *oh
- ? ofputil_decode_msg_type__(oh, length, typep)
- : OFPERR_OFPBRC_BAD_LEN);
- if (error) {
- *typep = &ofputil_invalid_type;
- }
- return error;
+ return (flow_dl_type == htons(FLOW_DL_TYPE_NONE)
+ ? htons(OFP_DL_TYPE_NOT_ETH_TYPE)
+ : flow_dl_type);
}
-/* Returns an OFPUTIL_* message type code for 'type'. */
-enum ofputil_msg_code
-ofputil_msg_type_code(const struct ofputil_msg_type *type)
+/* Given a 'dl_type' value in the format used in an ofp10_match or ofp11_match
+ * structure, returns the corresponding 'dl_type' value for use in struct
+ * flow. */
+ovs_be16
+ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type)
{
- return type->code;
+ return (ofp_dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
+ ? htons(FLOW_DL_TYPE_NONE)
+ : ofp_dl_type);
}
\f
/* Protocols. */
* 1.0, 0x02 for OpenFlow 1.1). Returns 0 if 'version' is not supported or
* outside the valid range. */
enum ofputil_protocol
-ofputil_protocol_from_ofp_version(int version)
+ofputil_protocol_from_ofp_version(enum ofp_version version)
{
switch (version) {
- case OFP10_VERSION: return OFPUTIL_P_OF10;
- default: return 0;
+ case OFP10_VERSION:
+ return OFPUTIL_P_OF10;
+ case OFP12_VERSION:
+ return OFPUTIL_P_OF12;
+ case OFP11_VERSION:
+ default:
+ return 0;
}
}
-/* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION or
- * OFP11_VERSION) that corresponds to 'protocol'. */
-uint8_t
+/* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION,
+ * OFP11_VERSION or OFP12_VERSION) that corresponds to 'protocol'. */
+enum ofp_version
ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol)
{
switch (protocol) {
case OFPUTIL_P_NXM:
case OFPUTIL_P_NXM_TID:
return OFP10_VERSION;
+ case OFPUTIL_P_OF12:
+ return OFP12_VERSION;
}
NOT_REACHED();
case OFPUTIL_P_NXM_TID:
return enable ? OFPUTIL_P_NXM_TID : OFPUTIL_P_NXM;
+ case OFPUTIL_P_OF12:
+ return OFPUTIL_P_OF12;
+
default:
NOT_REACHED();
}
case OFPUTIL_P_NXM_TID:
return ofputil_protocol_set_tid(OFPUTIL_P_NXM, tid);
+ case OFPUTIL_P_OF12:
+ return ofputil_protocol_set_tid(OFPUTIL_P_OF12, tid);
+
default:
NOT_REACHED();
}
case OFPUTIL_P_OF10_TID:
return "OpenFlow10+table_id";
+
+ case OFPUTIL_P_OF12:
+ return NULL;
}
/* Check abbreviations. */
{
const struct flow_wildcards *wc = &rule->wc;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14);
- /* Only NXM supports separately wildcards the Ethernet multicast bit. */
- if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
+ /* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
+ if (!eth_mask_is_exact(wc->dl_src_mask)
+ && !eth_addr_is_zero(wc->dl_src_mask)) {
+ return OFPUTIL_P_NXM_ANY;
+ }
+ if (!eth_mask_is_exact(wc->dl_dst_mask)
+ && !eth_addr_is_zero(wc->dl_dst_mask)) {
+ return OFPUTIL_P_NXM_ANY;
+ }
+
+ /* NXM and OF1.1+ support matching metadata. */
+ if (wc->metadata_mask != htonll(0)) {
return OFPUTIL_P_NXM_ANY;
}
/* Only NXM supports matching ARP hardware addresses. */
- if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) {
+ if (!eth_addr_is_zero(wc->arp_sha_mask) ||
+ !eth_addr_is_zero(wc->arp_tha_mask)) {
return OFPUTIL_P_NXM_ANY;
}
}
/* Only NXM supports matching IPv6 flow label. */
- if (!(wc->wildcards & FWW_IPV6_LABEL)) {
+ if (wc->ipv6_label_mask) {
return OFPUTIL_P_NXM_ANY;
}
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))) {
case OFPUTIL_P_OF10:
return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10);
+ case OFPUTIL_P_OF12:
+ return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW12);
+
case OFPUTIL_P_OF10_TID:
case OFPUTIL_P_NXM_TID:
NOT_REACHED();
assert(ofputil_nx_flow_format_is_valid(nxff));
- sff = make_nxmsg(sizeof *sff, NXT_SET_FLOW_FORMAT, &msg);
+ msg = ofpraw_alloc(OFPRAW_NXT_SET_FLOW_FORMAT, OFP10_VERSION, 0);
+ sff = ofpbuf_put_zeros(msg, sizeof *sff);
sff->format = htonl(nxff);
return msg;
case NXFF_NXM:
return OFPUTIL_P_NXM;
+ case NXFF_OPENFLOW12:
+ return OFPUTIL_P_OF12;
+
default:
return 0;
}
return "openflow10";
case NXFF_NXM:
return "nxm";
+ case NXFF_OPENFLOW12:
+ return "openflow12";
default:
NOT_REACHED();
}
struct nx_set_packet_in_format *spif;
struct ofpbuf *msg;
- spif = make_nxmsg(sizeof *spif, NXT_SET_PACKET_IN_FORMAT, &msg);
+ msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT, OFP10_VERSION, 0);
+ spif = ofpbuf_put_zeros(msg, sizeof *spif);
spif->format = htonl(packet_in_format);
return msg;
struct nx_flow_mod_table_id *nfmti;
struct ofpbuf *msg;
- nfmti = make_nxmsg(sizeof *nfmti, NXT_FLOW_MOD_TABLE_ID, &msg);
+ msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD_TABLE_ID, OFP10_VERSION, 0);
+ nfmti = ofpbuf_put_zeros(msg, sizeof *nfmti);
nfmti->set = flow_mod_table_id;
return msg;
}
* flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error
* code.
*
- * Does not validate the flow_mod actions. */
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of 'oh''s actions.
+ * The caller must initialize 'ofpacts' and retains ownership of it.
+ * 'fm->ofpacts' will point into the 'ofpacts' buffer.
+ *
+ * Does not validate the flow_mod actions. The caller should do that, with
+ * ofpacts_check(). */
enum ofperr
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
const struct ofp_header *oh,
- enum ofputil_protocol protocol)
+ enum ofputil_protocol protocol,
+ struct ofpbuf *ofpacts)
{
- const struct ofputil_msg_type *type;
uint16_t command;
struct ofpbuf b;
+ enum ofpraw raw;
ofpbuf_use_const(&b, oh, ntohs(oh->length));
-
- ofputil_decode_msg_type(oh, &type);
- if (ofputil_msg_type_code(type) == OFPUTIL_OFPT_FLOW_MOD) {
- /* Standard OpenFlow flow_mod. */
- const struct ofp_flow_mod *ofm;
- uint16_t priority;
+ raw = ofpraw_pull_assert(&b);
+ if (raw == OFPRAW_OFPT11_FLOW_MOD) {
+ /* Standard OpenFlow 1.1 flow_mod. */
+ const struct ofp11_flow_mod *ofm;
enum ofperr error;
- /* Dissect the message. */
ofm = ofpbuf_pull(&b, sizeof *ofm);
- error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
+
+ error = ofputil_pull_ofp11_match(&b, ntohs(ofm->priority), &fm->cr,
+ NULL);
if (error) {
return error;
}
- /* Set priority based on original wildcards. Normally we'd allow
- * ofputil_cls_rule_from_match() to do this for us, but
- * ofputil_normalize_rule() can put wildcards where the original flow
- * didn't have them. */
- priority = ntohs(ofm->priority);
- if (!(ofm->match.wildcards & htonl(OFPFW_ALL))) {
- priority = UINT16_MAX;
+ error = ofpacts_pull_openflow11_instructions(&b, b.size, ofpacts);
+ if (error) {
+ return error;
}
- /* Translate the rule. */
- ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr);
- ofputil_normalize_rule(&fm->cr);
-
/* Translate the message. */
- fm->cookie = ofm->cookie;
- fm->cookie_mask = htonll(UINT64_MAX);
- command = ntohs(ofm->command);
+ if (ofm->command == OFPFC_ADD) {
+ fm->cookie = htonll(0);
+ fm->cookie_mask = htonll(0);
+ fm->new_cookie = ofm->cookie;
+ } else {
+ /* XXX */
+ fm->cookie = ofm->cookie;
+ fm->cookie_mask = ofm->cookie_mask;
+ fm->new_cookie = htonll(UINT64_MAX);
+ }
+ fm->command = ofm->command;
+ fm->table_id = ofm->table_id;
fm->idle_timeout = ntohs(ofm->idle_timeout);
fm->hard_timeout = ntohs(ofm->hard_timeout);
fm->buffer_id = ntohl(ofm->buffer_id);
- fm->out_port = ntohs(ofm->out_port);
- fm->flags = ntohs(ofm->flags);
- } else if (ofputil_msg_type_code(type) == OFPUTIL_NXT_FLOW_MOD) {
- /* Nicira extended flow_mod. */
- const struct nx_flow_mod *nfm;
- enum ofperr error;
-
- /* Dissect the message. */
- nfm = ofpbuf_pull(&b, sizeof *nfm);
- error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
- &fm->cr, &fm->cookie, &fm->cookie_mask);
+ error = ofputil_port_from_ofp11(ofm->out_port, &fm->out_port);
if (error) {
return error;
}
- error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
- if (error) {
- return error;
+ if (ofm->out_group != htonl(OFPG_ANY)) {
+ return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED;
}
+ fm->flags = ntohs(ofm->flags);
+ } else {
+ if (raw == OFPRAW_OFPT10_FLOW_MOD) {
+ /* Standard OpenFlow 1.0 flow_mod. */
+ const struct ofp10_flow_mod *ofm;
+ uint16_t priority;
+ enum ofperr error;
+
+ /* Get the ofp10_flow_mod. */
+ ofm = ofpbuf_pull(&b, sizeof *ofm);
+
+ /* Set priority based on original wildcards. Normally we'd allow
+ * ofputil_cls_rule_from_match() to do this for us, but
+ * ofputil_normalize_rule() can put wildcards where the original
+ * flow didn't have them. */
+ priority = ntohs(ofm->priority);
+ if (!(ofm->match.wildcards & htonl(OFPFW10_ALL))) {
+ priority = UINT16_MAX;
+ }
- /* Translate the message. */
- command = ntohs(nfm->command);
- if (command == OFPFC_ADD) {
- if (fm->cookie_mask) {
- /* The "NXM_NX_COOKIE*" matches are not valid for flow
- * additions. Additions must use the "cookie" field of
- * the "nx_flow_mod" structure. */
- return OFPERR_NXBRC_NXM_INVALID;
- } else {
- fm->cookie = nfm->cookie;
- fm->cookie_mask = htonll(UINT64_MAX);
+ /* Translate the rule. */
+ ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr);
+ ofputil_normalize_rule(&fm->cr);
+
+ /* Now get the actions. */
+ error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+ if (error) {
+ return error;
}
- }
- fm->idle_timeout = ntohs(nfm->idle_timeout);
- fm->hard_timeout = ntohs(nfm->hard_timeout);
- fm->buffer_id = ntohl(nfm->buffer_id);
- fm->out_port = ntohs(nfm->out_port);
- fm->flags = ntohs(nfm->flags);
- } else {
- NOT_REACHED();
- }
- if (protocol & OFPUTIL_P_TID) {
- fm->command = command & 0xff;
- fm->table_id = command >> 8;
- } else {
- fm->command = command;
- fm->table_id = 0xff;
+ /* Translate the message. */
+ command = ntohs(ofm->command);
+ fm->cookie = htonll(0);
+ fm->cookie_mask = htonll(0);
+ fm->new_cookie = ofm->cookie;
+ fm->idle_timeout = ntohs(ofm->idle_timeout);
+ fm->hard_timeout = ntohs(ofm->hard_timeout);
+ fm->buffer_id = ntohl(ofm->buffer_id);
+ fm->out_port = ntohs(ofm->out_port);
+ fm->flags = ntohs(ofm->flags);
+ } else if (raw == OFPRAW_NXT_FLOW_MOD) {
+ /* Nicira extended flow_mod. */
+ const struct nx_flow_mod *nfm;
+ enum ofperr error;
+
+ /* Dissect the message. */
+ nfm = ofpbuf_pull(&b, sizeof *nfm);
+ error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
+ &fm->cr, &fm->cookie, &fm->cookie_mask);
+ if (error) {
+ return error;
+ }
+ error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+ if (error) {
+ return error;
+ }
+
+ /* Translate the message. */
+ command = ntohs(nfm->command);
+ if ((command & 0xff) == OFPFC_ADD && fm->cookie_mask) {
+ /* Flow additions may only set a new cookie, not match an
+ * existing cookie. */
+ return OFPERR_NXBRC_NXM_INVALID;
+ }
+ fm->new_cookie = nfm->cookie;
+ fm->idle_timeout = ntohs(nfm->idle_timeout);
+ fm->hard_timeout = ntohs(nfm->hard_timeout);
+ fm->buffer_id = ntohl(nfm->buffer_id);
+ fm->out_port = ntohs(nfm->out_port);
+ fm->flags = ntohs(nfm->flags);
+ } else {
+ NOT_REACHED();
+ }
+
+ if (protocol & OFPUTIL_P_TID) {
+ fm->command = command & 0xff;
+ fm->table_id = command >> 8;
+ } else {
+ fm->command = command;
+ fm->table_id = 0xff;
+ }
}
+ fm->ofpacts = ofpacts->data;
+ fm->ofpacts_len = ofpacts->size;
+
return 0;
}
+static ovs_be16
+ofputil_tid_command(const struct ofputil_flow_mod *fm,
+ enum ofputil_protocol protocol)
+{
+ return htons(protocol & OFPUTIL_P_TID
+ ? (fm->command & 0xff) | (fm->table_id << 8)
+ : fm->command);
+}
+
/* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
- * 'protocol' and returns the message.
- *
- * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is
- * enabled, false otherwise. */
+ * 'protocol' and returns the message. */
struct ofpbuf *
ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
enum ofputil_protocol protocol)
{
- size_t actions_len = fm->n_actions * sizeof *fm->actions;
- struct ofp_flow_mod *ofm;
- struct nx_flow_mod *nfm;
struct ofpbuf *msg;
- uint16_t command;
- int match_len;
-
- command = (protocol & OFPUTIL_P_TID
- ? (fm->command & 0xff) | (fm->table_id << 8)
- : fm->command);
switch (protocol) {
+ case OFPUTIL_P_OF12: {
+ struct ofp11_flow_mod *ofm;
+
+ msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, OFP12_VERSION,
+ NXM_TYPICAL_LEN + fm->ofpacts_len);
+ ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
+ ofm->cookie = fm->new_cookie;
+ ofm->cookie_mask = fm->cookie_mask;
+ ofm->table_id = fm->table_id;
+ ofm->command = fm->command;
+ ofm->idle_timeout = htons(fm->idle_timeout);
+ ofm->hard_timeout = htons(fm->hard_timeout);
+ ofm->priority = htons(fm->cr.priority);
+ ofm->buffer_id = htonl(fm->buffer_id);
+ ofm->out_port = ofputil_port_to_ofp11(fm->out_port);
+ ofm->out_group = htonl(OFPG11_ANY);
+ ofm->flags = htons(fm->flags);
+ oxm_put_match(msg, &fm->cr);
+ ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg);
+ break;
+ }
+
case OFPUTIL_P_OF10:
- case OFPUTIL_P_OF10_TID:
- msg = ofpbuf_new(sizeof *ofm + actions_len);
- ofm = put_openflow(sizeof *ofm, OFPT10_FLOW_MOD, msg);
- ofputil_cls_rule_to_match(&fm->cr, &ofm->match);
- ofm->cookie = fm->cookie;
- ofm->command = htons(command);
+ case OFPUTIL_P_OF10_TID: {
+ struct ofp10_flow_mod *ofm;
+
+ msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION,
+ fm->ofpacts_len);
+ ofm = ofpbuf_put_zeros(msg, sizeof *ofm);
+ ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match);
+ ofm->cookie = fm->new_cookie;
+ ofm->command = ofputil_tid_command(fm, protocol);
ofm->idle_timeout = htons(fm->idle_timeout);
ofm->hard_timeout = htons(fm->hard_timeout);
ofm->priority = htons(fm->cr.priority);
ofm->buffer_id = htonl(fm->buffer_id);
ofm->out_port = htons(fm->out_port);
ofm->flags = htons(fm->flags);
+ ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
break;
+ }
case OFPUTIL_P_NXM:
- case OFPUTIL_P_NXM_TID:
- msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len);
- put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg);
- nfm = msg->data;
- nfm->command = htons(command);
- if (command == OFPFC_ADD) {
- nfm->cookie = fm->cookie;
- match_len = nx_put_match(msg, &fm->cr, 0, 0);
- } else {
- nfm->cookie = 0;
- match_len = nx_put_match(msg, &fm->cr,
- fm->cookie, fm->cookie_mask);
- }
+ case OFPUTIL_P_NXM_TID: {
+ struct nx_flow_mod *nfm;
+ int match_len;
+
+ msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD, OFP10_VERSION,
+ NXM_TYPICAL_LEN + fm->ofpacts_len);
+ nfm = ofpbuf_put_zeros(msg, sizeof *nfm);
+ nfm->command = ofputil_tid_command(fm, protocol);
+ nfm->cookie = fm->new_cookie;
+ match_len = nx_put_match(msg, &fm->cr, fm->cookie, fm->cookie_mask);
+ nfm = msg->l3;
nfm->idle_timeout = htons(fm->idle_timeout);
nfm->hard_timeout = htons(fm->hard_timeout);
nfm->priority = htons(fm->cr.priority);
nfm->out_port = htons(fm->out_port);
nfm->flags = htons(fm->flags);
nfm->match_len = htons(match_len);
+ ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
break;
+ }
default:
NOT_REACHED();
}
- ofpbuf_put(msg, fm->actions, actions_len);
- update_openflow_length(msg);
+ ofpmsg_update_length(msg);
return msg;
}
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;
}
}
}
static enum ofperr
-ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
- const struct ofp_header *oh,
- bool aggregate)
+ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr,
+ const struct ofp10_flow_stats_request *ofsr,
+ bool aggregate)
{
- const struct ofp_flow_stats_request *ofsr =
- (const struct ofp_flow_stats_request *) oh;
-
fsr->aggregate = aggregate;
- ofputil_cls_rule_from_match(&ofsr->match, 0, &fsr->match);
+ ofputil_cls_rule_from_ofp10_match(&ofsr->match, 0, &fsr->match);
fsr->out_port = ntohs(ofsr->out_port);
fsr->table_id = ofsr->table_id;
fsr->cookie = fsr->cookie_mask = htonll(0);
return 0;
}
+static enum ofperr
+ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
+ struct ofpbuf *b, bool aggregate)
+{
+ const struct ofp11_flow_stats_request *ofsr;
+ enum ofperr error;
+
+ ofsr = ofpbuf_pull(b, sizeof *ofsr);
+ fsr->aggregate = aggregate;
+ fsr->table_id = ofsr->table_id;
+ error = ofputil_port_from_ofp11(ofsr->out_port, &fsr->out_port);
+ if (error) {
+ return error;
+ }
+ if (ofsr->out_group != htonl(OFPG11_ANY)) {
+ return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED;
+ }
+ fsr->cookie = ofsr->cookie;
+ fsr->cookie_mask = ofsr->cookie_mask;
+ error = ofputil_pull_ofp11_match(b, 0, &fsr->match, NULL);
+ if (error) {
+ return error;
+ }
+
+ return 0;
+}
+
static enum ofperr
ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
- const struct ofp_header *oh,
- bool aggregate)
+ struct ofpbuf *b, bool aggregate)
{
const struct nx_flow_stats_request *nfsr;
- struct ofpbuf b;
enum ofperr error;
- ofpbuf_use_const(&b, oh, ntohs(oh->length));
-
- nfsr = ofpbuf_pull(&b, sizeof *nfsr);
- error = nx_pull_match(&b, ntohs(nfsr->match_len), 0, &fsr->match,
+ nfsr = ofpbuf_pull(b, sizeof *nfsr);
+ error = nx_pull_match(b, ntohs(nfsr->match_len), 0, &fsr->match,
&fsr->cookie, &fsr->cookie_mask);
if (error) {
return error;
}
- if (b.size) {
+ if (b->size) {
return OFPERR_OFPBRC_BAD_LEN;
}
ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
const struct ofp_header *oh)
{
- const struct ofputil_msg_type *type;
+ enum ofpraw raw;
struct ofpbuf b;
- int code;
ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ switch ((int) raw) {
+ case OFPRAW_OFPST10_FLOW_REQUEST:
+ return ofputil_decode_ofpst10_flow_request(fsr, b.data, false);
- ofputil_decode_msg_type(oh, &type);
- code = ofputil_msg_type_code(type);
- switch (code) {
- case OFPUTIL_OFPST_FLOW_REQUEST:
- return ofputil_decode_ofpst_flow_request(fsr, oh, false);
+ case OFPRAW_OFPST10_AGGREGATE_REQUEST:
+ return ofputil_decode_ofpst10_flow_request(fsr, b.data, true);
- case OFPUTIL_OFPST_AGGREGATE_REQUEST:
- return ofputil_decode_ofpst_flow_request(fsr, oh, true);
+ case OFPRAW_OFPST11_FLOW_REQUEST:
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, false);
- case OFPUTIL_NXST_FLOW_REQUEST:
- return ofputil_decode_nxst_flow_request(fsr, oh, false);
+ case OFPRAW_OFPST11_AGGREGATE_REQUEST:
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, true);
- case OFPUTIL_NXST_AGGREGATE_REQUEST:
- return ofputil_decode_nxst_flow_request(fsr, oh, true);
+ case OFPRAW_NXST_FLOW_REQUEST:
+ return ofputil_decode_nxst_flow_request(fsr, &b, false);
+
+ case OFPRAW_NXST_AGGREGATE_REQUEST:
+ return ofputil_decode_nxst_flow_request(fsr, &b, true);
default:
/* Hey, the caller lied. */
enum ofputil_protocol protocol)
{
struct ofpbuf *msg;
+ enum ofpraw raw;
switch (protocol) {
+ case OFPUTIL_P_OF12: {
+ struct ofp11_flow_stats_request *ofsr;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST11_AGGREGATE_REQUEST
+ : OFPRAW_OFPST11_FLOW_REQUEST);
+ msg = ofpraw_alloc(raw, OFP12_VERSION, NXM_TYPICAL_LEN);
+ ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+ ofsr->table_id = fsr->table_id;
+ ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port);
+ ofsr->out_group = htonl(OFPG11_ANY);
+ ofsr->cookie = fsr->cookie;
+ ofsr->cookie_mask = fsr->cookie_mask;
+ oxm_put_match(msg, &fsr->match);
+ break;
+ }
+
case OFPUTIL_P_OF10:
case OFPUTIL_P_OF10_TID: {
- struct ofp_flow_stats_request *ofsr;
- int type;
-
- type = fsr->aggregate ? OFPST_AGGREGATE : OFPST_FLOW;
- ofsr = ofputil_make_stats_request(sizeof *ofsr, type, 0, &msg);
- ofputil_cls_rule_to_match(&fsr->match, &ofsr->match);
+ struct ofp10_flow_stats_request *ofsr;
+
+ raw = (fsr->aggregate
+ ? OFPRAW_OFPST10_AGGREGATE_REQUEST
+ : OFPRAW_OFPST10_FLOW_REQUEST);
+ msg = ofpraw_alloc(raw, OFP10_VERSION, 0);
+ ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr);
+ ofputil_cls_rule_to_ofp10_match(&fsr->match, &ofsr->match);
ofsr->table_id = fsr->table_id;
ofsr->out_port = htons(fsr->out_port);
break;
case OFPUTIL_P_NXM_TID: {
struct nx_flow_stats_request *nfsr;
int match_len;
- int subtype;
- subtype = fsr->aggregate ? NXST_AGGREGATE : NXST_FLOW;
- ofputil_make_stats_request(sizeof *nfsr, OFPST_VENDOR, subtype, &msg);
+ raw = (fsr->aggregate
+ ? OFPRAW_NXST_AGGREGATE_REQUEST
+ : OFPRAW_NXST_FLOW_REQUEST);
+ msg = ofpraw_alloc(raw, OFP10_VERSION, NXM_TYPICAL_LEN);
+ ofpbuf_put_zeros(msg, sizeof *nfsr);
match_len = nx_put_match(msg, &fsr->match,
fsr->cookie, fsr->cookie_mask);
- nfsr = msg->data;
+ nfsr = msg->l3;
nfsr->out_port = htons(fsr->out_port);
nfsr->match_len = htons(match_len);
nfsr->table_id = fsr->table_id;
* 'flow_age_extension' as true so that the contents of 'msg' determine the
* 'idle_age' and 'hard_age' members in 'fs'.
*
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the flow stats
+ * reply's actions. The caller must initialize 'ofpacts' and retains ownership
+ * of it. 'fs->ofpacts' will point into the 'ofpacts' buffer.
+ *
* Returns 0 if successful, EOF if no replies were left in this 'msg',
* otherwise a positive errno value. */
int
ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
struct ofpbuf *msg,
- bool flow_age_extension)
+ bool flow_age_extension,
+ struct ofpbuf *ofpacts)
{
- const struct ofputil_msg_type *type;
- int code;
+ enum ofperr error;
+ enum ofpraw raw;
- ofputil_decode_msg_type(msg->l2 ? msg->l2 : msg->data, &type);
- code = ofputil_msg_type_code(type);
- if (!msg->l2) {
- msg->l2 = msg->data;
- if (code == OFPUTIL_OFPST_FLOW_REPLY) {
- ofpbuf_pull(msg, sizeof(struct ofp_stats_msg));
- } else if (code == OFPUTIL_NXST_FLOW_REPLY) {
- ofpbuf_pull(msg, sizeof(struct nicira_stats_msg));
- } else {
- NOT_REACHED();
- }
+ error = (msg->l2
+ ? ofpraw_decode(&raw, msg->l2)
+ : ofpraw_pull(&raw, msg));
+ if (error) {
+ return error;
}
if (!msg->size) {
return EOF;
- } else if (code == OFPUTIL_OFPST_FLOW_REPLY) {
- const struct ofp_flow_stats *ofs;
+ } else if (raw == OFPRAW_OFPST11_FLOW_REPLY) {
+ const struct ofp11_flow_stats *ofs;
+ size_t length;
+ uint16_t padded_match_len;
+
+ ofs = ofpbuf_try_pull(msg, sizeof *ofs);
+ if (!ofs) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %zu leftover "
+ "bytes at end", msg->size);
+ return EINVAL;
+ }
+
+ length = ntohs(ofs->length);
+ if (length < sizeof *ofs) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid "
+ "length %zu", length);
+ return EINVAL;
+ }
+
+ if (ofputil_pull_ofp11_match(msg, ntohs(ofs->priority), &fs->rule,
+ &padded_match_len)) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match");
+ return EINVAL;
+ }
+
+ if (ofpacts_pull_openflow11_instructions(msg, length - sizeof *ofs -
+ padded_match_len, ofpacts)) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions");
+ return EINVAL;
+ }
+
+ fs->table_id = ofs->table_id;
+ fs->duration_sec = ntohl(ofs->duration_sec);
+ fs->duration_nsec = ntohl(ofs->duration_nsec);
+ fs->idle_timeout = ntohs(ofs->idle_timeout);
+ fs->hard_timeout = ntohs(ofs->hard_timeout);
+ fs->idle_age = -1;
+ fs->hard_age = -1;
+ fs->cookie = ofs->cookie;
+ fs->packet_count = ntohll(ofs->packet_count);
+ fs->byte_count = ntohll(ofs->byte_count);
+ } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) {
+ const struct ofp10_flow_stats *ofs;
size_t length;
ofs = ofpbuf_try_pull(msg, sizeof *ofs);
return EINVAL;
}
- if (ofputil_pull_actions(msg, length - sizeof *ofs,
- &fs->actions, &fs->n_actions)) {
+ if (ofpacts_pull_openflow10(msg, length - sizeof *ofs, ofpacts)) {
return EINVAL;
}
fs->cookie = get_32aligned_be64(&ofs->cookie);
- ofputil_cls_rule_from_match(&ofs->match, ntohs(ofs->priority),
- &fs->rule);
+ ofputil_cls_rule_from_ofp10_match(&ofs->match, ntohs(ofs->priority),
+ &fs->rule);
fs->table_id = ofs->table_id;
fs->duration_sec = ntohl(ofs->duration_sec);
fs->duration_nsec = ntohl(ofs->duration_nsec);
fs->hard_age = -1;
fs->packet_count = ntohll(get_32aligned_be64(&ofs->packet_count));
fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count));
- } else if (code == OFPUTIL_NXST_FLOW_REPLY) {
+ } else if (raw == OFPRAW_NXST_FLOW_REPLY) {
const struct nx_flow_stats *nfs;
- size_t match_len, length;
+ size_t match_len, actions_len, length;
nfs = ofpbuf_try_pull(msg, sizeof *nfs);
if (!nfs) {
return EINVAL;
}
- if (ofputil_pull_actions(msg,
- length - sizeof *nfs - ROUND_UP(match_len, 8),
- &fs->actions, &fs->n_actions)) {
+ actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
+ if (ofpacts_pull_openflow10(msg, actions_len, ofpacts)) {
return EINVAL;
}
NOT_REACHED();
}
+ fs->ofpacts = ofpacts->data;
+ fs->ofpacts_len = ofpacts->size;
+
return 0;
}
ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
struct list *replies)
{
- size_t act_len = fs->n_actions * sizeof *fs->actions;
- const struct ofp_stats_msg *osm;
+ struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
+ size_t start_ofs = reply->size;
+ enum ofpraw raw;
+
+ ofpraw_decode_partial(&raw, reply->data, reply->size);
+ if (raw == OFPRAW_OFPST11_FLOW_REPLY) {
+ struct ofp11_flow_stats *ofs;
+
+ ofpbuf_put_uninit(reply, sizeof *ofs);
+ oxm_put_match(reply, &fs->rule);
+ ofpacts_put_openflow11_instructions(fs->ofpacts, fs->ofpacts_len,
+ reply);
+
+ ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
+ ofs->length = htons(reply->size - start_ofs);
+ ofs->table_id = fs->table_id;
+ ofs->pad = 0;
+ ofs->duration_sec = htonl(fs->duration_sec);
+ ofs->duration_nsec = htonl(fs->duration_nsec);
+ ofs->priority = htons(fs->rule.priority);
+ ofs->idle_timeout = htons(fs->idle_timeout);
+ ofs->hard_timeout = htons(fs->hard_timeout);
+ memset(ofs->pad2, 0, sizeof ofs->pad2);
+ ofs->cookie = fs->cookie;
+ ofs->packet_count = htonll(unknown_to_zero(fs->packet_count));
+ ofs->byte_count = htonll(unknown_to_zero(fs->byte_count));
+ } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) {
+ struct ofp10_flow_stats *ofs;
- osm = ofpbuf_from_list(list_back(replies))->data;
- if (osm->type == htons(OFPST_FLOW)) {
- size_t len = offsetof(struct ofp_flow_stats, actions) + act_len;
- struct ofp_flow_stats *ofs;
+ ofpbuf_put_uninit(reply, sizeof *ofs);
+ ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
- ofs = ofputil_append_stats_reply(len, replies);
- ofs->length = htons(len);
+ ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
+ ofs->length = htons(reply->size - start_ofs);
ofs->table_id = fs->table_id;
ofs->pad = 0;
- ofputil_cls_rule_to_match(&fs->rule, &ofs->match);
+ ofputil_cls_rule_to_ofp10_match(&fs->rule, &ofs->match);
ofs->duration_sec = htonl(fs->duration_sec);
ofs->duration_nsec = htonl(fs->duration_nsec);
ofs->priority = htons(fs->rule.priority);
htonll(unknown_to_zero(fs->packet_count)));
put_32aligned_be64(&ofs->byte_count,
htonll(unknown_to_zero(fs->byte_count)));
- memcpy(ofs->actions, fs->actions, act_len);
- } else if (osm->type == htons(OFPST_VENDOR)) {
+ } else if (raw == OFPRAW_NXST_FLOW_REPLY) {
struct nx_flow_stats *nfs;
- struct ofpbuf *msg;
- size_t start_len;
+ int match_len;
- msg = ofputil_reserve_stats_reply(
- sizeof *nfs + NXM_MAX_LEN + act_len, replies);
- start_len = msg->size;
+ ofpbuf_put_uninit(reply, sizeof *nfs);
+ match_len = nx_put_match(reply, &fs->rule, 0, 0);
+ ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
- nfs = ofpbuf_put_uninit(msg, sizeof *nfs);
+ nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs);
+ nfs->length = htons(reply->size - start_ofs);
nfs->table_id = fs->table_id;
nfs->pad = 0;
nfs->duration_sec = htonl(fs->duration_sec);
nfs->hard_age = htons(fs->hard_age < 0 ? 0
: fs->hard_age < UINT16_MAX ? fs->hard_age + 1
: UINT16_MAX);
- nfs->match_len = htons(nx_put_match(msg, &fs->rule, 0, 0));
+ nfs->match_len = htons(match_len);
nfs->cookie = fs->cookie;
nfs->packet_count = htonll(fs->packet_count);
nfs->byte_count = htonll(fs->byte_count);
- ofpbuf_put(msg, fs->actions, act_len);
- nfs->length = htons(msg->size - start_len);
} else {
NOT_REACHED();
}
+
+ ofpmp_postappend(replies, start_ofs);
}
/* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
- * NXST_AGGREGATE reply according to 'protocol', and returns the message. */
+ * NXST_AGGREGATE reply matching 'request', and returns the message. */
struct ofpbuf *
ofputil_encode_aggregate_stats_reply(
const struct ofputil_aggregate_stats *stats,
- const struct ofp_stats_msg *request)
+ const struct ofp_header *request)
{
+ struct ofp_aggregate_stats_reply *asr;
+ uint64_t packet_count;
+ uint64_t byte_count;
struct ofpbuf *msg;
+ enum ofpraw raw;
- if (request->type == htons(OFPST_AGGREGATE)) {
- struct ofp_aggregate_stats_reply *asr;
-
- asr = ofputil_make_stats_reply(sizeof *asr, request, &msg);
- put_32aligned_be64(&asr->packet_count,
- htonll(unknown_to_zero(stats->packet_count)));
- put_32aligned_be64(&asr->byte_count,
- htonll(unknown_to_zero(stats->byte_count)));
- asr->flow_count = htonl(stats->flow_count);
- } else if (request->type == htons(OFPST_VENDOR)) {
- struct nx_aggregate_stats_reply *nasr;
-
- nasr = ofputil_make_stats_reply(sizeof *nasr, request, &msg);
- assert(nasr->nsm.subtype == htonl(NXST_AGGREGATE));
- nasr->packet_count = htonll(stats->packet_count);
- nasr->byte_count = htonll(stats->byte_count);
- nasr->flow_count = htonl(stats->flow_count);
+ ofpraw_decode(&raw, request);
+ if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) {
+ packet_count = unknown_to_zero(stats->packet_count);
+ byte_count = unknown_to_zero(stats->byte_count);
} else {
- NOT_REACHED();
+ packet_count = stats->packet_count;
+ byte_count = stats->byte_count;
}
+ msg = ofpraw_alloc_stats_reply(request, 0);
+ asr = ofpbuf_put_zeros(msg, sizeof *asr);
+ put_32aligned_be64(&asr->packet_count, htonll(packet_count));
+ put_32aligned_be64(&asr->byte_count, htonll(byte_count));
+ asr->flow_count = htonl(stats->flow_count);
+
return msg;
}
+enum ofperr
+ofputil_decode_aggregate_stats_reply(struct ofputil_aggregate_stats *stats,
+ const struct ofp_header *reply)
+{
+ struct ofp_aggregate_stats_reply *asr;
+ struct ofpbuf msg;
+
+ ofpbuf_use_const(&msg, reply, ntohs(reply->length));
+ ofpraw_pull_assert(&msg);
+
+ asr = msg.l3;
+ stats->packet_count = ntohll(get_32aligned_be64(&asr->packet_count));
+ stats->byte_count = ntohll(get_32aligned_be64(&asr->byte_count));
+ stats->flow_count = ntohl(asr->flow_count);
+
+ return 0;
+}
+
/* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an
* abstract ofputil_flow_removed in 'fr'. Returns 0 if successful, otherwise
* an OpenFlow error code. */
ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
const struct ofp_header *oh)
{
- const struct ofputil_msg_type *type;
- enum ofputil_msg_code code;
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ if (raw == OFPRAW_OFPT11_FLOW_REMOVED) {
+ const struct ofp12_flow_removed *ofr;
+ enum ofperr error;
- ofputil_decode_msg_type(oh, &type);
- code = ofputil_msg_type_code(type);
- if (code == OFPUTIL_OFPT_FLOW_REMOVED) {
+ ofr = ofpbuf_pull(&b, sizeof *ofr);
+
+ error = ofputil_pull_ofp11_match(&b, ntohs(ofr->priority),
+ &fr->rule, NULL);
+ if (error) {
+ return error;
+ }
+
+ fr->cookie = ofr->cookie;
+ fr->reason = ofr->reason;
+ /* XXX: ofr->table_id is ignored */
+ fr->duration_sec = ntohl(ofr->duration_sec);
+ fr->duration_nsec = ntohl(ofr->duration_nsec);
+ fr->idle_timeout = ntohs(ofr->idle_timeout);
+ fr->hard_timeout = ntohs(ofr->hard_timeout);
+ fr->packet_count = ntohll(ofr->packet_count);
+ fr->byte_count = ntohll(ofr->byte_count);
+ } else if (raw == OFPRAW_OFPT10_FLOW_REMOVED) {
const struct ofp_flow_removed *ofr;
- ofr = (const struct ofp_flow_removed *) oh;
- ofputil_cls_rule_from_match(&ofr->match, ntohs(ofr->priority),
- &fr->rule);
+ ofr = ofpbuf_pull(&b, sizeof *ofr);
+
+ ofputil_cls_rule_from_ofp10_match(&ofr->match, ntohs(ofr->priority),
+ &fr->rule);
fr->cookie = ofr->cookie;
fr->reason = ofr->reason;
fr->duration_sec = ntohl(ofr->duration_sec);
fr->duration_nsec = ntohl(ofr->duration_nsec);
fr->idle_timeout = ntohs(ofr->idle_timeout);
+ fr->hard_timeout = 0;
fr->packet_count = ntohll(ofr->packet_count);
fr->byte_count = ntohll(ofr->byte_count);
- } else if (code == OFPUTIL_NXT_FLOW_REMOVED) {
+ } else if (raw == OFPRAW_NXT_FLOW_REMOVED) {
struct nx_flow_removed *nfr;
- struct ofpbuf b;
int error;
- ofpbuf_use_const(&b, oh, ntohs(oh->length));
-
nfr = ofpbuf_pull(&b, sizeof *nfr);
error = nx_pull_match(&b, ntohs(nfr->match_len), ntohs(nfr->priority),
&fr->rule, NULL, NULL);
fr->duration_sec = ntohl(nfr->duration_sec);
fr->duration_nsec = ntohl(nfr->duration_nsec);
fr->idle_timeout = ntohs(nfr->idle_timeout);
+ fr->hard_timeout = 0;
fr->packet_count = ntohll(nfr->packet_count);
fr->byte_count = ntohll(nfr->byte_count);
} else {
struct ofpbuf *msg;
switch (protocol) {
+ case OFPUTIL_P_OF12: {
+ struct ofp12_flow_removed *ofr;
+
+ msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
+ ofputil_protocol_to_ofp_version(protocol),
+ htonl(0), NXM_TYPICAL_LEN);
+ ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
+ ofr->cookie = fr->cookie;
+ ofr->priority = htons(fr->rule.priority);
+ ofr->reason = fr->reason;
+ ofr->table_id = 0;
+ ofr->duration_sec = htonl(fr->duration_sec);
+ ofr->duration_nsec = htonl(fr->duration_nsec);
+ ofr->idle_timeout = htons(fr->idle_timeout);
+ ofr->hard_timeout = htons(fr->hard_timeout);
+ ofr->packet_count = htonll(fr->packet_count);
+ ofr->byte_count = htonll(fr->byte_count);
+ oxm_put_match(msg, &fr->rule);
+ break;
+ }
+
case OFPUTIL_P_OF10:
case OFPUTIL_P_OF10_TID: {
struct ofp_flow_removed *ofr;
- ofr = make_openflow_xid(sizeof *ofr, OFPT_FLOW_REMOVED, htonl(0),
- &msg);
- ofputil_cls_rule_to_match(&fr->rule, &ofr->match);
+ msg = ofpraw_alloc_xid(OFPRAW_OFPT10_FLOW_REMOVED, OFP10_VERSION,
+ htonl(0), 0);
+ ofr = ofpbuf_put_zeros(msg, sizeof *ofr);
+ ofputil_cls_rule_to_ofp10_match(&fr->rule, &ofr->match);
ofr->cookie = fr->cookie;
ofr->priority = htons(fr->rule.priority);
ofr->reason = fr->reason;
struct nx_flow_removed *nfr;
int match_len;
- make_nxmsg_xid(sizeof *nfr, NXT_FLOW_REMOVED, htonl(0), &msg);
+ msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_REMOVED, OFP10_VERSION,
+ htonl(0), NXM_TYPICAL_LEN);
+ nfr = ofpbuf_put_zeros(msg, sizeof *nfr);
match_len = nx_put_match(msg, &fr->rule, 0, 0);
- nfr = msg->data;
+ nfr = msg->l3;
nfr->cookie = fr->cookie;
nfr->priority = htons(fr->rule.priority);
nfr->reason = fr->reason;
return msg;
}
-int
+static void
+ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin,
+ struct cls_rule *rule,
+ struct ofpbuf *b)
+{
+ pin->packet = b->data;
+ pin->packet_len = b->size;
+
+ pin->fmd.in_port = rule->flow.in_port;
+ pin->fmd.tun_id = rule->flow.tun_id;
+ pin->fmd.metadata = rule->flow.metadata;
+ memcpy(pin->fmd.regs, rule->flow.regs, sizeof pin->fmd.regs);
+}
+
+enum ofperr
ofputil_decode_packet_in(struct ofputil_packet_in *pin,
const struct ofp_header *oh)
{
- const struct ofputil_msg_type *type;
- enum ofputil_msg_code code;
+ enum ofpraw raw;
+ struct ofpbuf b;
- ofputil_decode_msg_type(oh, &type);
- code = ofputil_msg_type_code(type);
memset(pin, 0, sizeof *pin);
- if (code == OFPUTIL_OFPT_PACKET_IN) {
- const struct ofp_packet_in *opi = (const struct ofp_packet_in *) oh;
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+ if (raw == OFPRAW_OFPT12_PACKET_IN) {
+ const struct ofp12_packet_in *opi;
+ struct cls_rule rule;
+ int error;
+
+ opi = ofpbuf_pull(&b, sizeof *opi);
+ error = oxm_pull_match_loose(&b, 0, &rule);
+ if (error) {
+ return error;
+ }
+
+ if (!ofpbuf_try_pull(&b, 2)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ pin->reason = opi->reason;
+ pin->table_id = opi->table_id;
+
+ pin->buffer_id = ntohl(opi->buffer_id);
+ pin->total_len = ntohs(opi->total_len);
+
+ ofputil_decode_packet_in_finish(pin, &rule, &b);
+ } else if (raw == OFPRAW_OFPT10_PACKET_IN) {
+ const struct ofp_packet_in *opi;
+
+ opi = ofpbuf_pull(&b, offsetof(struct ofp_packet_in, data));
pin->packet = opi->data;
- pin->packet_len = ntohs(opi->header.length)
- - offsetof(struct ofp_packet_in, data);
+ pin->packet_len = b.size;
pin->fmd.in_port = ntohs(opi->in_port);
pin->reason = opi->reason;
pin->buffer_id = ntohl(opi->buffer_id);
pin->total_len = ntohs(opi->total_len);
- } else if (code == OFPUTIL_NXT_PACKET_IN) {
+ } else if (raw == OFPRAW_NXT_PACKET_IN) {
const struct nx_packet_in *npi;
struct cls_rule rule;
- struct ofpbuf b;
int error;
- ofpbuf_use_const(&b, oh, ntohs(oh->length));
-
npi = ofpbuf_pull(&b, sizeof *npi);
error = nx_pull_match_loose(&b, ntohs(npi->match_len), 0, &rule, NULL,
- NULL);
+ NULL);
if (error) {
return error;
}
return OFPERR_OFPBRC_BAD_LEN;
}
- pin->packet = b.data;
- pin->packet_len = b.size;
pin->reason = npi->reason;
pin->table_id = npi->table_id;
pin->cookie = npi->cookie;
- pin->fmd.in_port = rule.flow.in_port;
-
- pin->fmd.tun_id = rule.flow.tun_id;
- pin->fmd.tun_id_mask = rule.wc.tun_id_mask;
-
- memcpy(pin->fmd.regs, rule.flow.regs, sizeof pin->fmd.regs);
- memcpy(pin->fmd.reg_masks, rule.wc.reg_masks,
- sizeof pin->fmd.reg_masks);
-
pin->buffer_id = ntohl(npi->buffer_id);
pin->total_len = ntohs(npi->total_len);
+
+ ofputil_decode_packet_in_finish(pin, &rule, &b);
} else {
NOT_REACHED();
}
return 0;
}
+static void
+ofputil_packet_in_to_rule(const struct ofputil_packet_in *pin,
+ struct cls_rule *rule)
+{
+ int i;
+
+ cls_rule_init_catchall(rule, 0);
+ if (pin->fmd.tun_id != htonll(0)) {
+ cls_rule_set_tun_id(rule, pin->fmd.tun_id);
+ }
+ if (pin->fmd.metadata != htonll(0)) {
+ cls_rule_set_metadata(rule, pin->fmd.metadata);
+ }
+
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ if (pin->fmd.regs[i]) {
+ cls_rule_set_reg(rule, i, pin->fmd.regs[i]);
+ }
+ }
+
+ cls_rule_set_in_port(rule, pin->fmd.in_port);
+}
+
/* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message
* in the format specified by 'packet_in_format'. */
struct ofpbuf *
ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
+ enum ofputil_protocol protocol,
enum nx_packet_in_format packet_in_format)
{
size_t send_len = MIN(pin->send_len, pin->packet_len);
struct ofpbuf *packet;
/* Add OFPT_PACKET_IN. */
- if (packet_in_format == NXPIF_OPENFLOW10) {
- size_t header_len = offsetof(struct ofp_packet_in, data);
+ if (protocol == OFPUTIL_P_OF12) {
+ struct ofp12_packet_in *opi;
+ struct cls_rule rule;
+
+ ofputil_packet_in_to_rule(pin, &rule);
+
+ /* The final argument is just an estimate of the space required. */
+ packet = ofpraw_alloc_xid(OFPRAW_OFPT12_PACKET_IN, OFP12_VERSION,
+ htonl(0), (sizeof(struct flow_metadata) * 2
+ + 2 + send_len));
+ ofpbuf_put_zeros(packet, sizeof *opi);
+ oxm_put_match(packet, &rule);
+ ofpbuf_put_zeros(packet, 2);
+ ofpbuf_put(packet, pin->packet, send_len);
+
+ opi = packet->l3;
+ opi->buffer_id = htonl(pin->buffer_id);
+ opi->total_len = htons(pin->total_len);
+ opi->reason = pin->reason;
+ opi->table_id = pin->table_id;
+ } else if (packet_in_format == NXPIF_OPENFLOW10) {
struct ofp_packet_in *opi;
- packet = ofpbuf_new(send_len + header_len);
- opi = ofpbuf_put_zeros(packet, header_len);
- opi->header.version = OFP10_VERSION;
- opi->header.type = OFPT_PACKET_IN;
+ packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION,
+ htonl(0), send_len);
+ opi = ofpbuf_put_zeros(packet, offsetof(struct ofp_packet_in, data));
opi->total_len = htons(pin->total_len);
opi->in_port = htons(pin->fmd.in_port);
opi->reason = pin->reason;
struct nx_packet_in *npi;
struct cls_rule rule;
size_t match_len;
- size_t i;
-
- /* Estimate of required PACKET_IN length includes the NPI header, space
- * for the match (2 times sizeof the metadata seems like enough), 2
- * bytes for padding, and the packet length. */
- packet = ofpbuf_new(sizeof *npi + sizeof(struct flow_metadata) * 2
- + 2 + send_len);
- cls_rule_init_catchall(&rule, 0);
- cls_rule_set_tun_id_masked(&rule, pin->fmd.tun_id,
- pin->fmd.tun_id_mask);
-
- for (i = 0; i < FLOW_N_REGS; i++) {
- cls_rule_set_reg_masked(&rule, i, pin->fmd.regs[i],
- pin->fmd.reg_masks[i]);
- }
-
- cls_rule_set_in_port(&rule, pin->fmd.in_port);
+ ofputil_packet_in_to_rule(pin, &rule);
+ /* The final argument is just an estimate of the space required. */
+ packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION,
+ htonl(0), (sizeof(struct flow_metadata) * 2
+ + 2 + send_len));
ofpbuf_put_zeros(packet, sizeof *npi);
match_len = nx_put_match(packet, &rule, 0, 0);
ofpbuf_put_zeros(packet, 2);
ofpbuf_put(packet, pin->packet, send_len);
- npi = packet->data;
- npi->nxh.header.version = OFP10_VERSION;
- npi->nxh.header.type = OFPT_VENDOR;
- npi->nxh.vendor = htonl(NX_VENDOR_ID);
- npi->nxh.subtype = htonl(NXT_PACKET_IN);
-
+ npi = packet->l3;
npi->buffer_id = htonl(pin->buffer_id);
npi->total_len = htons(pin->total_len);
npi->reason = pin->reason;
} else {
NOT_REACHED();
}
- update_openflow_length(packet);
+ ofpmsg_update_length(packet);
return packet;
}
return false;
}
+/* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in
+ * 'po'.
+ *
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out
+ * message's actions. The caller must initialize 'ofpacts' and retains
+ * ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer.
+ *
+ * Returns 0 if successful, otherwise an OFPERR_* value. */
enum ofperr
ofputil_decode_packet_out(struct ofputil_packet_out *po,
- const struct ofp_packet_out *opo)
+ const struct ofp_header *oh,
+ struct ofpbuf *ofpacts)
{
- enum ofperr error;
+ enum ofperr bad_in_port_err;
+ enum ofpraw raw;
struct ofpbuf b;
- po->buffer_id = ntohl(opo->buffer_id);
- po->in_port = ntohs(opo->in_port);
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ if (raw == OFPRAW_OFPT11_PACKET_OUT) {
+ enum ofperr error;
+ const struct ofp11_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+
+ po->buffer_id = ntohl(opo->buffer_id);
+ error = ofputil_port_from_ofp11(opo->in_port, &po->in_port);
+ if (error) {
+ return error;
+ }
+
+ error = ofpacts_pull_openflow11_actions(&b, ntohs(opo->actions_len),
+ ofpacts);
+ if (error) {
+ return error;
+ }
+
+ bad_in_port_err = OFPERR_OFPBMC_BAD_VALUE;
+ } else if (raw == OFPRAW_OFPT10_PACKET_OUT) {
+ enum ofperr error;
+ const struct ofp_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+
+ po->buffer_id = ntohl(opo->buffer_id);
+ po->in_port = ntohs(opo->in_port);
+
+ error = ofpacts_pull_openflow10(&b, ntohs(opo->actions_len), ofpacts);
+ if (error) {
+ return error;
+ }
+
+ bad_in_port_err = OFPERR_NXBRC_BAD_IN_PORT;
+ } else {
+ NOT_REACHED();
+ }
+
if (po->in_port >= OFPP_MAX && po->in_port != OFPP_LOCAL
- && po->in_port != OFPP_NONE) {
+ && po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) {
VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16,
po->in_port);
- return OFPERR_NXBRC_BAD_IN_PORT;
+ return bad_in_port_err;
}
- ofpbuf_use_const(&b, opo, ntohs(opo->header.length));
- ofpbuf_pull(&b, sizeof *opo);
-
- error = ofputil_pull_actions(&b, ntohs(opo->actions_len),
- &po->actions, &po->n_actions);
- if (error) {
- return error;
- }
+ po->ofpacts = ofpacts->data;
+ po->ofpacts_len = ofpacts->size;
if (po->buffer_id == UINT32_MAX) {
po->packet = b.data;
return 0;
}
-static int
-ofputil_pull_phy_port(uint8_t ofp_version, struct ofpbuf *b,
- struct ofputil_phy_port *pp)
+static size_t
+ofputil_get_phy_port_size(enum ofp_version ofp_version)
{
- if (ofp_version == OFP10_VERSION) {
- const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp);
- return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF;
- } else {
- const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op);
- return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
+ switch (ofp_version) {
+ case OFP10_VERSION:
+ return sizeof(struct ofp10_phy_port);
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ return sizeof(struct ofp11_port);
+ default:
+ NOT_REACHED();
}
}
}
static void
-ofputil_put_phy_port(uint8_t ofp_version, const struct ofputil_phy_port *pp,
- struct ofpbuf *b)
+ofputil_put_phy_port(enum ofp_version ofp_version,
+ const struct ofputil_phy_port *pp, struct ofpbuf *b)
{
- if (ofp_version == OFP10_VERSION) {
+ switch (ofp_version) {
+ case OFP10_VERSION: {
struct ofp10_phy_port *opp;
if (b->size + sizeof *opp <= UINT16_MAX) {
opp = ofpbuf_put_uninit(b, sizeof *opp);
ofputil_encode_ofp10_phy_port(pp, opp);
}
- } else {
+ break;
+ }
+
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
struct ofp11_port *op;
if (b->size + sizeof *op <= UINT16_MAX) {
op = ofpbuf_put_uninit(b, sizeof *op);
ofputil_encode_ofp11_port(pp, op);
}
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version,
+ const struct ofputil_phy_port *pp,
+ struct list *replies)
+{
+ switch (ofp_version) {
+ case OFP10_VERSION: {
+ struct ofp10_phy_port *opp;
+
+ opp = ofpmp_append(replies, sizeof *opp);
+ ofputil_encode_ofp10_phy_port(pp, opp);
+ break;
+ }
+
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ struct ofp11_port *op;
+
+ op = ofpmp_append(replies, sizeof *op);
+ ofputil_encode_ofp11_port(pp, op);
+ break;
+ }
+
+ default:
+ NOT_REACHED();
}
}
\f
/* ofputil_switch_features */
#define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \
- OFPC_IP_REASM | OFPC_QUEUE_STATS | OFPC_ARP_MATCH_IP)
+ OFPC_IP_REASM | OFPC_QUEUE_STATS)
BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_STATS == OFPC_FLOW_STATS);
BUILD_ASSERT_DECL((int) OFPUTIL_C_TABLE_STATS == OFPC_TABLE_STATS);
BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_STATS == OFPC_PORT_STATS);
{ 0, 0 },
};
-static const struct ofputil_action_bit_translation of11_action_bits[] = {
- { OFPUTIL_A_OUTPUT, OFPAT11_OUTPUT },
- { OFPUTIL_A_SET_VLAN_VID, OFPAT11_SET_VLAN_VID },
- { OFPUTIL_A_SET_VLAN_PCP, OFPAT11_SET_VLAN_PCP },
- { OFPUTIL_A_SET_DL_SRC, OFPAT11_SET_DL_SRC },
- { OFPUTIL_A_SET_DL_DST, OFPAT11_SET_DL_DST },
- { OFPUTIL_A_SET_NW_SRC, OFPAT11_SET_NW_SRC },
- { OFPUTIL_A_SET_NW_DST, OFPAT11_SET_NW_DST },
- { OFPUTIL_A_SET_NW_TOS, OFPAT11_SET_NW_TOS },
- { OFPUTIL_A_SET_NW_ECN, OFPAT11_SET_NW_ECN },
- { OFPUTIL_A_SET_TP_SRC, OFPAT11_SET_TP_SRC },
- { OFPUTIL_A_SET_TP_DST, OFPAT11_SET_TP_DST },
- { OFPUTIL_A_COPY_TTL_OUT, OFPAT11_COPY_TTL_OUT },
- { OFPUTIL_A_COPY_TTL_IN, OFPAT11_COPY_TTL_IN },
- { OFPUTIL_A_SET_MPLS_LABEL, OFPAT11_SET_MPLS_LABEL },
- { OFPUTIL_A_SET_MPLS_TC, OFPAT11_SET_MPLS_TC },
- { OFPUTIL_A_SET_MPLS_TTL, OFPAT11_SET_MPLS_TTL },
- { OFPUTIL_A_DEC_MPLS_TTL, OFPAT11_DEC_MPLS_TTL },
- { OFPUTIL_A_PUSH_VLAN, OFPAT11_PUSH_VLAN },
- { OFPUTIL_A_POP_VLAN, OFPAT11_POP_VLAN },
- { OFPUTIL_A_PUSH_MPLS, OFPAT11_PUSH_MPLS },
- { OFPUTIL_A_POP_MPLS, OFPAT11_POP_MPLS },
- { OFPUTIL_A_SET_QUEUE, OFPAT11_SET_QUEUE },
- { OFPUTIL_A_GROUP, OFPAT11_GROUP },
- { OFPUTIL_A_SET_NW_TTL, OFPAT11_SET_NW_TTL },
- { OFPUTIL_A_DEC_NW_TTL, OFPAT11_DEC_NW_TTL },
- { 0, 0 },
-};
-
static enum ofputil_action_bitmap
decode_action_bits(ovs_be32 of_actions,
const struct ofputil_action_bit_translation *x)
return ofputil_actions;
}
+static uint32_t
+ofputil_capabilities_mask(enum ofp_version ofp_version)
+{
+ /* Handle capabilities whose bit is unique for all Open Flow versions */
+ switch (ofp_version) {
+ case OFP10_VERSION:
+ case OFP11_VERSION:
+ return OFPC_COMMON | OFPC_ARP_MATCH_IP;
+ case OFP12_VERSION:
+ return OFPC_COMMON | OFPC12_PORT_BLOCKED;
+ default:
+ /* Caller needs to check osf->header.version itself */
+ return 0;
+ }
+}
+
/* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an
* abstract representation in '*features'. Initializes '*b' to iterate over
* the OpenFlow port structures following 'osf' with later calls to
- * ofputil_pull_switch_features_port(). Returns 0 if successful, otherwise an
+ * ofputil_pull_phy_port(). Returns 0 if successful, otherwise an
* OFPERR_* value. */
enum ofperr
-ofputil_decode_switch_features(const struct ofp_switch_features *osf,
+ofputil_decode_switch_features(const struct ofp_header *oh,
struct ofputil_switch_features *features,
struct ofpbuf *b)
{
- ofpbuf_use_const(b, osf, ntohs(osf->header.length));
- ofpbuf_pull(b, sizeof *osf);
- b->l2 = (struct ofputil_switch_features *) osf;
+ const struct ofp_switch_features *osf;
+ enum ofpraw raw;
+
+ ofpbuf_use_const(b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(b);
+ osf = ofpbuf_pull(b, sizeof *osf);
features->datapath_id = ntohll(osf->datapath_id);
features->n_buffers = ntohl(osf->n_buffers);
features->n_tables = osf->n_tables;
- features->capabilities = ntohl(osf->capabilities) & OFPC_COMMON;
- if (osf->header.version == OFP10_VERSION) {
- if (b->size % sizeof(struct ofp10_phy_port)) {
- return OFPERR_OFPBRC_BAD_LEN;
- }
+ features->capabilities = ntohl(osf->capabilities) &
+ ofputil_capabilities_mask(oh->version);
+
+ if (b->size % ofputil_get_phy_port_size(oh->version)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ if (raw == OFPRAW_OFPT10_FEATURES_REPLY) {
if (osf->capabilities & htonl(OFPC10_STP)) {
features->capabilities |= OFPUTIL_C_STP;
}
features->actions = decode_action_bits(osf->actions, of10_action_bits);
- } else if (osf->header.version == OFP11_VERSION) {
- if (b->size % sizeof(struct ofp11_port)) {
- return OFPERR_OFPBRC_BAD_LEN;
- }
-
+ } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY) {
if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) {
features->capabilities |= OFPUTIL_C_GROUP_STATS;
}
- features->actions = decode_action_bits(osf->actions, of11_action_bits);
+ features->actions = 0;
} else {
return OFPERR_OFPBRC_BAD_VERSION;
}
return 0;
}
-/* Given a buffer 'b' that was initialized by a previous successful call to
- * ofputil_decode_switch_features(), tries to decode an OpenFlow port structure
- * following the main switch features information. If successful, initializes
- * '*pp' with an abstract representation of the port and returns 0. If no
- * ports remained to be decoded, returns EOF. On an error, returns a positive
- * OFPERR_* value. */
-int
-ofputil_pull_switch_features_port(struct ofpbuf *b,
- struct ofputil_phy_port *pp)
+/* Returns true if the maximum number of ports are in 'oh'. */
+static bool
+max_ports_in_features(const struct ofp_header *oh)
{
- const struct ofp_switch_features *osf = b->l2;
- return ofputil_pull_phy_port(osf->header.version, b, pp);
+ size_t pp_size = ofputil_get_phy_port_size(oh->version);
+ return ntohs(oh->length) + pp_size > UINT16_MAX;
}
-/* Returns the number of OpenFlow port structures that follow the main switch
- * features information in '*osf'. The return value is only guaranteed to be
- * accurate if '*osf' is well-formed, that is, if
- * ofputil_decode_switch_features() can process '*osf' successfully. */
-size_t
-ofputil_count_phy_ports(const struct ofp_switch_features *osf)
+/* Given a buffer 'b' that contains a Features Reply message, checks if
+ * it contains the maximum number of ports that will fit. If so, it
+ * returns true and removes the ports from the message. The caller
+ * should then send an OFPST_PORT_DESC stats request to get the ports,
+ * since the switch may have more ports than could be represented in the
+ * Features Reply. Otherwise, returns false.
+ */
+bool
+ofputil_switch_features_ports_trunc(struct ofpbuf *b)
{
- size_t ports_len = ntohs(osf->header.length) - sizeof *osf;
- return (osf->header.version == OFP10_VERSION
- ? ports_len / sizeof(struct ofp10_phy_port)
- : ports_len / sizeof(struct ofp11_port));
+ struct ofp_header *oh = b->data;
+
+ if (max_ports_in_features(oh)) {
+ /* Remove all the ports. */
+ b->size = (sizeof(struct ofp_header)
+ + sizeof(struct ofp_switch_features));
+ ofpmsg_update_length(b);
+
+ return true;
+ }
+
+ return false;
}
static ovs_be32
{
struct ofp_switch_features *osf;
struct ofpbuf *b;
+ enum ofp_version version;
+ enum ofpraw raw;
- osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, xid, &b);
- osf->header.version = ofputil_protocol_to_ofp_version(protocol);
+ version = ofputil_protocol_to_ofp_version(protocol);
+ switch (version) {
+ case OFP10_VERSION:
+ raw = OFPRAW_OFPT10_FEATURES_REPLY;
+ break;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ raw = OFPRAW_OFPT11_FEATURES_REPLY;
+ break;
+ default:
+ NOT_REACHED();
+ }
+ b = ofpraw_alloc_xid(raw, version, xid, 0);
+ osf = ofpbuf_put_zeros(b, sizeof *osf);
osf->datapath_id = htonll(features->datapath_id);
osf->n_buffers = htonl(features->n_buffers);
osf->n_tables = features->n_tables;
osf->capabilities = htonl(features->capabilities & OFPC_COMMON);
- if (osf->header.version == OFP10_VERSION) {
+ osf->capabilities = htonl(features->capabilities &
+ ofputil_capabilities_mask(version));
+ switch (version) {
+ case OFP10_VERSION:
if (features->capabilities & OFPUTIL_C_STP) {
osf->capabilities |= htonl(OFPC10_STP);
}
osf->actions = encode_action_bits(features->actions, of10_action_bits);
- } else {
+ break;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
if (features->capabilities & OFPUTIL_C_GROUP_STATS) {
osf->capabilities |= htonl(OFPC11_GROUP_STATS);
}
- osf->actions = encode_action_bits(features->actions, of11_action_bits);
+ break;
+ default:
+ NOT_REACHED();
}
return b;
ofputil_put_switch_features_port(const struct ofputil_phy_port *pp,
struct ofpbuf *b)
{
- const struct ofp_switch_features *osf = b->data;
+ const struct ofp_header *oh = b->data;
- ofputil_put_phy_port(osf->header.version, pp, b);
+ ofputil_put_phy_port(oh->version, pp, b);
}
\f
/* ofputil_port_status */
/* Decodes the OpenFlow "port status" message in '*ops' into an abstract form
* in '*ps'. Returns 0 if successful, otherwise an OFPERR_* value. */
enum ofperr
-ofputil_decode_port_status(const struct ofp_port_status *ops,
+ofputil_decode_port_status(const struct ofp_header *oh,
struct ofputil_port_status *ps)
{
+ const struct ofp_port_status *ops;
struct ofpbuf b;
int retval;
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+ ops = ofpbuf_pull(&b, sizeof *ops);
+
if (ops->reason != OFPPR_ADD &&
ops->reason != OFPPR_DELETE &&
ops->reason != OFPPR_MODIFY) {
}
ps->reason = ops->reason;
- ofpbuf_use_const(&b, ops, ntohs(ops->header.length));
- ofpbuf_pull(&b, sizeof *ops);
- retval = ofputil_pull_phy_port(ops->header.version, &b, &ps->desc);
+ retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc);
assert(retval != EOF);
return retval;
}
{
struct ofp_port_status *ops;
struct ofpbuf *b;
+ enum ofp_version version;
+ enum ofpraw raw;
+
+ version = ofputil_protocol_to_ofp_version(protocol);
+ switch (version) {
+ case OFP10_VERSION:
+ raw = OFPRAW_OFPT10_PORT_STATUS;
+ break;
- b = ofpbuf_new(sizeof *ops + sizeof(struct ofp11_port));
- ops = put_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, htonl(0), b);
- ops->header.version = ofputil_protocol_to_ofp_version(protocol);
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ raw = OFPRAW_OFPT11_PORT_STATUS;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ b = ofpraw_alloc_xid(raw, version, htonl(0), 0);
+ ops = ofpbuf_put_zeros(b, sizeof *ops);
ops->reason = ps->reason;
- ofputil_put_phy_port(ops->header.version, &ps->desc, b);
- update_openflow_length(b);
+ ofputil_put_phy_port(version, &ps->desc, b);
+ ofpmsg_update_length(b);
return b;
}
\f
ofputil_decode_port_mod(const struct ofp_header *oh,
struct ofputil_port_mod *pm)
{
- if (oh->version == OFP10_VERSION) {
- const struct ofp10_port_mod *opm = (const struct ofp10_port_mod *) oh;
+ enum ofpraw raw;
+ struct ofpbuf b;
- if (oh->length != htons(sizeof *opm)) {
- return OFPERR_OFPBRC_BAD_LEN;
- }
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ if (raw == OFPRAW_OFPT10_PORT_MOD) {
+ const struct ofp10_port_mod *opm = b.data;
pm->port_no = ntohs(opm->port_no);
memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
pm->config = ntohl(opm->config) & OFPPC10_ALL;
pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
- } else if (oh->version == OFP11_VERSION) {
- const struct ofp11_port_mod *opm = (const struct ofp11_port_mod *) oh;
+ } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
+ const struct ofp11_port_mod *opm = b.data;
enum ofperr error;
- if (oh->length != htons(sizeof *opm)) {
- return OFPERR_OFPBRC_BAD_LEN;
- }
-
error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
if (error) {
return error;
pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
} else {
- return OFPERR_OFPBRC_BAD_VERSION;
+ return OFPERR_OFPBRC_BAD_TYPE;
}
pm->config &= pm->mask;
ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
enum ofputil_protocol protocol)
{
- uint8_t ofp_version = ofputil_protocol_to_ofp_version(protocol);
+ enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
struct ofpbuf *b;
- if (ofp_version == OFP10_VERSION) {
+ switch (ofp_version) {
+ case OFP10_VERSION: {
struct ofp10_port_mod *opm;
- opm = make_openflow(sizeof *opm, OFPT10_PORT_MOD, &b);
+ b = ofpraw_alloc(OFPRAW_OFPT10_PORT_MOD, ofp_version, 0);
+ opm = ofpbuf_put_zeros(b, sizeof *opm);
opm->port_no = htons(pm->port_no);
memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
opm->config = htonl(pm->config & OFPPC10_ALL);
opm->mask = htonl(pm->mask & OFPPC10_ALL);
opm->advertise = netdev_port_features_to_ofp10(pm->advertise);
- } else if (ofp_version == OFP11_VERSION) {
+ break;
+ }
+
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
struct ofp11_port_mod *opm;
- opm = make_openflow(sizeof *opm, OFPT11_PORT_MOD, &b);
- opm->port_no = htonl(pm->port_no);
+ b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0);
+ opm = ofpbuf_put_zeros(b, sizeof *opm);
+ opm->port_no = ofputil_port_to_ofp11(pm->port_no);
memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
opm->config = htonl(pm->config & OFPPC11_ALL);
opm->mask = htonl(pm->mask & OFPPC11_ALL);
opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
- } else {
+ break;
+ }
+
+ default:
NOT_REACHED();
}
return b;
}
+\f
+/* ofputil_flow_monitor_request */
-struct ofpbuf *
-ofputil_encode_packet_out(const struct ofputil_packet_out *po)
+/* Converts an NXST_FLOW_MONITOR request in 'msg' into an abstract
+ * ofputil_flow_monitor_request in 'rq'.
+ *
+ * Multiple NXST_FLOW_MONITOR requests can be packed into a single OpenFlow
+ * message. Calling this function multiple times for a single 'msg' iterates
+ * through the requests. The caller must initially leave 'msg''s layer
+ * pointers null and not modify them between calls.
+ *
+ * Returns 0 if successful, EOF if no requests were left in this 'msg',
+ * otherwise an OFPERR_* value. */
+int
+ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq,
+ struct ofpbuf *msg)
{
- struct ofp_packet_out *opo;
- size_t actions_len;
- struct ofpbuf *msg;
- size_t size;
+ struct nx_flow_monitor_request *nfmr;
+ uint16_t flags;
- actions_len = po->n_actions * sizeof *po->actions;
- size = sizeof *opo + actions_len;
- if (po->buffer_id == UINT32_MAX) {
- size += po->packet_len;
+ if (!msg->l2) {
+ msg->l2 = msg->data;
+ ofpraw_pull_assert(msg);
}
- msg = ofpbuf_new(size);
- opo = put_openflow(sizeof *opo, OFPT10_PACKET_OUT, msg);
- opo->buffer_id = htonl(po->buffer_id);
- opo->in_port = htons(po->in_port);
- opo->actions_len = htons(actions_len);
- ofpbuf_put(msg, po->actions, actions_len);
- if (po->buffer_id == UINT32_MAX) {
- ofpbuf_put(msg, po->packet, po->packet_len);
+ if (!msg->size) {
+ return EOF;
}
- update_openflow_length(msg);
- return msg;
-}
+ nfmr = ofpbuf_try_pull(msg, sizeof *nfmr);
+ if (!nfmr) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR request has %zu "
+ "leftover bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
-/* Returns a string representing the message type of 'type'. The string is the
- * enumeration constant for the type, e.g. "OFPT_HELLO". For statistics
- * messages, the constant is followed by "request" or "reply",
- * e.g. "OFPST_AGGREGATE reply". */
-const char *
-ofputil_msg_type_name(const struct ofputil_msg_type *type)
-{
- return type->name;
-}
-\f
-/* Allocates and stores in '*bufferp' a new ofpbuf with a size of
- * 'openflow_len', starting with an OpenFlow header with the given 'type' and
- * an arbitrary transaction id. Allocated bytes beyond the header, if any, are
- * zeroed.
- *
- * The caller is responsible for freeing '*bufferp' when it is no longer
- * needed.
- *
- * The OpenFlow header length is initially set to 'openflow_len'; if the
- * message is later extended, the length should be updated with
- * update_openflow_length() before sending.
- *
- * Returns the header. */
-void *
-make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **bufferp)
-{
- *bufferp = ofpbuf_new(openflow_len);
- return put_openflow_xid(openflow_len, type, alloc_xid(), *bufferp);
-}
+ flags = ntohs(nfmr->flags);
+ if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY))
+ || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE
+ | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16,
+ flags);
+ return OFPERR_NXBRC_FM_BAD_FLAGS;
+ }
-/* Similar to make_openflow() but creates a Nicira vendor extension message
- * with the specific 'subtype'. 'subtype' should be in host byte order. */
-void *
-make_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf **bufferp)
-{
- return make_nxmsg_xid(openflow_len, subtype, alloc_xid(), bufferp);
-}
+ if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) {
+ return OFPERR_NXBRC_MUST_BE_ZERO;
+ }
-/* Allocates and stores in '*bufferp' a new ofpbuf with a size of
- * 'openflow_len', starting with an OpenFlow header with the given 'type' and
- * transaction id 'xid'. Allocated bytes beyond the header, if any, are
- * zeroed.
- *
- * The caller is responsible for freeing '*bufferp' when it is no longer
- * needed.
- *
- * The OpenFlow header length is initially set to 'openflow_len'; if the
- * message is later extended, the length should be updated with
- * update_openflow_length() before sending.
- *
- * Returns the header. */
-void *
-make_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
- struct ofpbuf **bufferp)
-{
- *bufferp = ofpbuf_new(openflow_len);
- return put_openflow_xid(openflow_len, type, xid, *bufferp);
-}
+ rq->id = ntohl(nfmr->id);
+ rq->flags = flags;
+ rq->out_port = ntohs(nfmr->out_port);
+ rq->table_id = nfmr->table_id;
-/* Similar to make_openflow_xid() but creates a Nicira vendor extension message
- * with the specific 'subtype'. 'subtype' should be in host byte order. */
-void *
-make_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
- struct ofpbuf **bufferp)
-{
- *bufferp = ofpbuf_new(openflow_len);
- return put_nxmsg_xid(openflow_len, subtype, xid, *bufferp);
+ return nx_pull_match(msg, ntohs(nfmr->match_len), OFP_DEFAULT_PRIORITY,
+ &rq->match, NULL, NULL);
}
-/* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
- * with the given 'type' and an arbitrary transaction id. Allocated bytes
- * beyond the header, if any, are zeroed.
- *
- * The OpenFlow header length is initially set to 'openflow_len'; if the
- * message is later extended, the length should be updated with
- * update_openflow_length() before sending.
- *
- * Returns the header. */
-void *
-put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *buffer)
+void
+ofputil_append_flow_monitor_request(
+ const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg)
{
- return put_openflow_xid(openflow_len, type, alloc_xid(), buffer);
+ struct nx_flow_monitor_request *nfmr;
+ size_t start_ofs;
+ int match_len;
+
+ if (!msg->size) {
+ ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, OFP10_VERSION, msg);
+ }
+
+ start_ofs = msg->size;
+ ofpbuf_put_zeros(msg, sizeof *nfmr);
+ match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0));
+
+ nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr);
+ nfmr->id = htonl(rq->id);
+ nfmr->flags = htons(rq->flags);
+ nfmr->out_port = htons(rq->out_port);
+ nfmr->match_len = htons(match_len);
+ nfmr->table_id = rq->table_id;
}
-/* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
- * with the given 'type' and an transaction id 'xid'. Allocated bytes beyond
- * the header, if any, are zeroed.
+/* Converts an NXST_FLOW_MONITOR reply (also known as a flow update) in 'msg'
+ * into an abstract ofputil_flow_update in 'update'. The caller must have
+ * initialized update->match to point to space allocated for a cls_rule.
*
- * The OpenFlow header length is initially set to 'openflow_len'; if the
- * message is later extended, the length should be updated with
- * update_openflow_length() before sending.
+ * Uses 'ofpacts' to store the abstract OFPACT_* version of the update's
+ * actions (except for NXFME_ABBREV, which never includes actions). The caller
+ * must initialize 'ofpacts' and retains ownership of it. 'update->ofpacts'
+ * will point into the 'ofpacts' buffer.
*
- * Returns the header. */
-void *
-put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
- struct ofpbuf *buffer)
+ * Multiple flow updates can be packed into a single OpenFlow message. Calling
+ * this function multiple times for a single 'msg' iterates through the
+ * updates. The caller must initially leave 'msg''s layer pointers null and
+ * not modify them between calls.
+ *
+ * Returns 0 if successful, EOF if no updates were left in this 'msg',
+ * otherwise an OFPERR_* value. */
+int
+ofputil_decode_flow_update(struct ofputil_flow_update *update,
+ struct ofpbuf *msg, struct ofpbuf *ofpacts)
{
- struct ofp_header *oh;
+ struct nx_flow_update_header *nfuh;
+ unsigned int length;
- assert(openflow_len >= sizeof *oh);
- assert(openflow_len <= UINT16_MAX);
+ if (!msg->l2) {
+ msg->l2 = msg->data;
+ ofpraw_pull_assert(msg);
+ }
- oh = ofpbuf_put_uninit(buffer, openflow_len);
- oh->version = OFP10_VERSION;
- oh->type = type;
- oh->length = htons(openflow_len);
- oh->xid = xid;
- memset(oh + 1, 0, openflow_len - sizeof *oh);
- return oh;
-}
+ if (!msg->size) {
+ return EOF;
+ }
-/* Similar to put_openflow() but append a Nicira vendor extension message with
- * the specific 'subtype'. 'subtype' should be in host byte order. */
-void *
-put_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf *buffer)
-{
- return put_nxmsg_xid(openflow_len, subtype, alloc_xid(), buffer);
-}
+ if (msg->size < sizeof(struct nx_flow_update_header)) {
+ goto bad_len;
+ }
-/* Similar to put_openflow_xid() but append a Nicira vendor extension message
- * with the specific 'subtype'. 'subtype' should be in host byte order. */
-void *
-put_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
- struct ofpbuf *buffer)
-{
- struct nicira_header *nxh;
+ nfuh = msg->data;
+ update->event = ntohs(nfuh->event);
+ length = ntohs(nfuh->length);
+ if (length > msg->size || length % 8) {
+ goto bad_len;
+ }
- nxh = put_openflow_xid(openflow_len, OFPT_VENDOR, xid, buffer);
- nxh->vendor = htonl(NX_VENDOR_ID);
- nxh->subtype = htonl(subtype);
- return nxh;
-}
+ if (update->event == NXFME_ABBREV) {
+ struct nx_flow_update_abbrev *nfua;
-/* Updates the 'length' field of the OpenFlow message in 'buffer' to
- * 'buffer->size'. */
-void
-update_openflow_length(struct ofpbuf *buffer)
-{
- struct ofp_header *oh = ofpbuf_at_assert(buffer, 0, sizeof *oh);
- oh->length = htons(buffer->size);
-}
+ if (length != sizeof *nfua) {
+ goto bad_len;
+ }
-static void
-put_stats__(ovs_be32 xid, uint8_t ofp_type,
- ovs_be16 ofpst_type, ovs_be32 nxst_subtype,
- struct ofpbuf *msg)
-{
- if (ofpst_type == htons(OFPST_VENDOR)) {
- struct nicira_stats_msg *nsm;
+ nfua = ofpbuf_pull(msg, sizeof *nfua);
+ update->xid = nfua->xid;
+ return 0;
+ } else if (update->event == NXFME_ADDED
+ || update->event == NXFME_DELETED
+ || update->event == NXFME_MODIFIED) {
+ struct nx_flow_update_full *nfuf;
+ unsigned int actions_len;
+ unsigned int match_len;
+ enum ofperr error;
- nsm = put_openflow_xid(sizeof *nsm, ofp_type, xid, msg);
- nsm->vsm.osm.type = ofpst_type;
- nsm->vsm.vendor = htonl(NX_VENDOR_ID);
- nsm->subtype = nxst_subtype;
- } else {
- struct ofp_stats_msg *osm;
+ if (length < sizeof *nfuf) {
+ goto bad_len;
+ }
- osm = put_openflow_xid(sizeof *osm, ofp_type, xid, msg);
- osm->type = ofpst_type;
- }
-}
+ nfuf = ofpbuf_pull(msg, sizeof *nfuf);
+ match_len = ntohs(nfuf->match_len);
+ if (sizeof *nfuf + match_len > length) {
+ goto bad_len;
+ }
-/* Creates a statistics request message with total length 'openflow_len'
- * (including all headers) and the given 'ofpst_type', and stores the buffer
- * containing the new message in '*bufferp'. If 'ofpst_type' is OFPST_VENDOR
- * then 'nxst_subtype' is used as the Nicira vendor extension statistics
- * subtype (otherwise 'nxst_subtype' is ignored).
- *
- * Initializes bytes following the headers to all-bits-zero.
- *
- * Returns the first byte of the new message. */
-void *
-ofputil_make_stats_request(size_t openflow_len, uint16_t ofpst_type,
- uint32_t nxst_subtype, struct ofpbuf **bufferp)
-{
- struct ofpbuf *msg;
+ update->reason = ntohs(nfuf->reason);
+ update->idle_timeout = ntohs(nfuf->idle_timeout);
+ update->hard_timeout = ntohs(nfuf->hard_timeout);
+ update->table_id = nfuf->table_id;
+ update->cookie = nfuf->cookie;
+
+ error = nx_pull_match(msg, match_len, ntohs(nfuf->priority),
+ update->match, NULL, NULL);
+ if (error) {
+ return error;
+ }
+
+ actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
+ error = ofpacts_pull_openflow10(msg, actions_len, ofpacts);
+ if (error) {
+ return error;
+ }
- msg = *bufferp = ofpbuf_new(openflow_len);
- put_stats__(alloc_xid(), OFPT10_STATS_REQUEST,
- htons(ofpst_type), htonl(nxst_subtype), msg);
- ofpbuf_padto(msg, openflow_len);
+ update->ofpacts = ofpacts->data;
+ update->ofpacts_len = ofpacts->size;
+ return 0;
+ } else {
+ VLOG_WARN_RL(&bad_ofmsg_rl,
+ "NXST_FLOW_MONITOR reply has bad event %"PRIu16,
+ ntohs(nfuh->event));
+ return OFPERR_OFPET_BAD_REQUEST;
+ }
- return msg->data;
+bad_len:
+ VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR reply has %zu "
+ "leftover bytes at end", msg->size);
+ return OFPERR_OFPBRC_BAD_LEN;
}
-static void
-put_stats_reply__(const struct ofp_stats_msg *request, struct ofpbuf *msg)
+uint32_t
+ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh)
{
- assert(request->header.type == OFPT10_STATS_REQUEST ||
- request->header.type == OFPT10_STATS_REPLY);
- put_stats__(request->header.xid, OFPT10_STATS_REPLY, request->type,
- (request->type != htons(OFPST_VENDOR)
- ? htonl(0)
- : ((const struct nicira_stats_msg *) request)->subtype),
- msg);
+ const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh);
+
+ return ntohl(cancel->id);
}
-/* Creates a statistics reply message with total length 'openflow_len'
- * (including all headers) and the same type (either a standard OpenFlow
- * statistics type or a Nicira extension type and subtype) as 'request', and
- * stores the buffer containing the new message in '*bufferp'.
- *
- * Initializes bytes following the headers to all-bits-zero.
- *
- * Returns the first byte of the new message. */
-void *
-ofputil_make_stats_reply(size_t openflow_len,
- const struct ofp_stats_msg *request,
- struct ofpbuf **bufferp)
+struct ofpbuf *
+ofputil_encode_flow_monitor_cancel(uint32_t id)
{
+ struct nx_flow_monitor_cancel *nfmc;
struct ofpbuf *msg;
- msg = *bufferp = ofpbuf_new(openflow_len);
- put_stats_reply__(request, msg);
- ofpbuf_padto(msg, openflow_len);
-
- return msg->data;
+ msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, OFP10_VERSION, 0);
+ nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc);
+ nfmc->id = htonl(id);
+ return msg;
}
-/* Initializes 'replies' as a list of ofpbufs that will contain a series of
- * replies to 'request', which should be an OpenFlow or Nicira extension
- * statistics request. Initially 'replies' will have a single reply message
- * that has only a header. The functions ofputil_reserve_stats_reply() and
- * ofputil_append_stats_reply() may be used to add to the reply. */
void
-ofputil_start_stats_reply(const struct ofp_stats_msg *request,
- struct list *replies)
+ofputil_start_flow_update(struct list *replies)
{
struct ofpbuf *msg;
- msg = ofpbuf_new(1024);
- put_stats_reply__(request, msg);
+ msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, OFP10_VERSION,
+ htonl(0), 1024);
list_init(replies);
list_push_back(replies, &msg->list_node);
}
-/* Prepares to append up to 'len' bytes to the series of statistics replies in
- * 'replies', which should have been initialized with
- * ofputil_start_stats_reply(). Returns an ofpbuf with at least 'len' bytes of
- * tailroom. (The 'len' bytes have not actually be allocated; the caller must
- * do so with e.g. ofpbuf_put_uninit().) */
-struct ofpbuf *
-ofputil_reserve_stats_reply(size_t len, struct list *replies)
+void
+ofputil_append_flow_update(const struct ofputil_flow_update *update,
+ struct list *replies)
{
- struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
- struct ofp_stats_msg *osm = msg->data;
+ struct nx_flow_update_header *nfuh;
+ struct ofpbuf *msg;
+ size_t start_ofs;
+
+ msg = ofpbuf_from_list(list_back(replies));
+ start_ofs = msg->size;
- if (msg->size + len <= UINT16_MAX) {
- ofpbuf_prealloc_tailroom(msg, len);
+ if (update->event == NXFME_ABBREV) {
+ struct nx_flow_update_abbrev *nfua;
+
+ nfua = ofpbuf_put_zeros(msg, sizeof *nfua);
+ nfua->xid = update->xid;
} else {
- osm->flags |= htons(OFPSF_REPLY_MORE);
+ struct nx_flow_update_full *nfuf;
+ int match_len;
+
+ ofpbuf_put_zeros(msg, sizeof *nfuf);
+ match_len = nx_put_match(msg, update->match, htonll(0), htonll(0));
+ ofpacts_put_openflow10(update->ofpacts, update->ofpacts_len, msg);
- msg = ofpbuf_new(MAX(1024, sizeof(struct nicira_stats_msg) + len));
- put_stats_reply__(osm, msg);
- list_push_back(replies, &msg->list_node);
+ nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
+ nfuf->reason = htons(update->reason);
+ nfuf->priority = htons(update->match->priority);
+ nfuf->idle_timeout = htons(update->idle_timeout);
+ nfuf->hard_timeout = htons(update->hard_timeout);
+ nfuf->match_len = htons(match_len);
+ nfuf->table_id = update->table_id;
+ nfuf->cookie = update->cookie;
}
- return msg;
-}
-/* Appends 'len' bytes to the series of statistics replies in 'replies', and
- * returns the first byte. */
-void *
-ofputil_append_stats_reply(size_t len, struct list *replies)
-{
- return ofpbuf_put_uninit(ofputil_reserve_stats_reply(len, replies), len);
-}
+ nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh);
+ nfuh->length = htons(msg->size - start_ofs);
+ nfuh->event = htons(update->event);
-/* Returns the first byte past the ofp_stats_msg header in 'oh'. */
-const void *
-ofputil_stats_body(const struct ofp_header *oh)
-{
- assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
- return (const struct ofp_stats_msg *) oh + 1;
+ ofpmp_postappend(replies, start_ofs);
}
-
-/* Returns the number of bytes past the ofp_stats_msg header in 'oh'. */
-size_t
-ofputil_stats_body_len(const struct ofp_header *oh)
+\f
+struct ofpbuf *
+ofputil_encode_packet_out(const struct ofputil_packet_out *po,
+ enum ofputil_protocol protocol)
{
- assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
- return ntohs(oh->length) - sizeof(struct ofp_stats_msg);
-}
+ enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
+ struct ofpbuf *msg;
+ size_t size;
-/* Returns the first byte past the nicira_stats_msg header in 'oh'. */
-const void *
-ofputil_nxstats_body(const struct ofp_header *oh)
-{
- assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
- return ((const struct nicira_stats_msg *) oh) + 1;
-}
+ size = po->ofpacts_len;
+ if (po->buffer_id == UINT32_MAX) {
+ size += po->packet_len;
+ }
-/* Returns the number of bytes past the nicira_stats_msg header in 'oh'. */
-size_t
-ofputil_nxstats_body_len(const struct ofp_header *oh)
-{
- assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
- return ntohs(oh->length) - sizeof(struct nicira_stats_msg);
-}
+ switch (ofp_version) {
+ case OFP10_VERSION: {
+ struct ofp_packet_out *opo;
+ size_t actions_ofs;
-struct ofpbuf *
-make_flow_mod(uint16_t command, const struct cls_rule *rule,
- size_t actions_len)
-{
- struct ofp_flow_mod *ofm;
- size_t size = sizeof *ofm + actions_len;
- struct ofpbuf *out = ofpbuf_new(size);
- ofm = ofpbuf_put_zeros(out, sizeof *ofm);
- ofm->header.version = OFP10_VERSION;
- ofm->header.type = OFPT10_FLOW_MOD;
- ofm->header.length = htons(size);
- ofm->cookie = 0;
- ofm->priority = htons(MIN(rule->priority, UINT16_MAX));
- ofputil_cls_rule_to_match(rule, &ofm->match);
- ofm->command = htons(command);
- return out;
-}
+ msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size);
+ ofpbuf_put_zeros(msg, sizeof *opo);
+ actions_ofs = msg->size;
+ ofpacts_put_openflow10(po->ofpacts, po->ofpacts_len, msg);
-struct ofpbuf *
-make_add_flow(const struct cls_rule *rule, uint32_t buffer_id,
- uint16_t idle_timeout, size_t actions_len)
-{
- struct ofpbuf *out = make_flow_mod(OFPFC_ADD, rule, actions_len);
- struct ofp_flow_mod *ofm = out->data;
- ofm->idle_timeout = htons(idle_timeout);
- ofm->hard_timeout = htons(OFP_FLOW_PERMANENT);
- ofm->buffer_id = htonl(buffer_id);
- return out;
-}
+ opo = msg->l3;
+ opo->buffer_id = htonl(po->buffer_id);
+ opo->in_port = htons(po->in_port);
+ opo->actions_len = htons(msg->size - actions_ofs);
+ break;
+ }
-struct ofpbuf *
-make_del_flow(const struct cls_rule *rule)
-{
- struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, rule, 0);
- struct ofp_flow_mod *ofm = out->data;
- ofm->out_port = htons(OFPP_NONE);
- return out;
-}
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ struct ofp11_packet_out *opo;
+ size_t len;
-struct ofpbuf *
-make_add_simple_flow(const struct cls_rule *rule,
- uint32_t buffer_id, uint16_t out_port,
- uint16_t idle_timeout)
-{
- if (out_port != OFPP_NONE) {
- struct ofp_action_output *oao;
- struct ofpbuf *buffer;
+ msg = ofpraw_alloc(OFPRAW_OFPT11_PACKET_OUT, ofp_version, size);
+ ofpbuf_put_zeros(msg, sizeof *opo);
+ len = ofpacts_put_openflow11_actions(po->ofpacts, po->ofpacts_len, msg);
- buffer = make_add_flow(rule, buffer_id, idle_timeout, sizeof *oao);
- ofputil_put_OFPAT10_OUTPUT(buffer)->port = htons(out_port);
- return buffer;
- } else {
- return make_add_flow(rule, buffer_id, idle_timeout, 0);
+ opo = msg->l3;
+ opo->buffer_id = htonl(po->buffer_id);
+ opo->in_port = ofputil_port_to_ofp11(po->in_port);
+ opo->actions_len = htons(len);
+ break;
}
-}
-struct ofpbuf *
-make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason,
- const struct ofpbuf *payload, int max_send_len)
-{
- struct ofp_packet_in *opi;
- struct ofpbuf *buf;
- int send_len;
+ default:
+ NOT_REACHED();
+ }
+
+ if (po->buffer_id == UINT32_MAX) {
+ ofpbuf_put(msg, po->packet, po->packet_len);
+ }
- send_len = MIN(max_send_len, payload->size);
- buf = ofpbuf_new(sizeof *opi + send_len);
- opi = put_openflow_xid(offsetof(struct ofp_packet_in, data),
- OFPT_PACKET_IN, 0, buf);
- opi->buffer_id = htonl(buffer_id);
- opi->total_len = htons(payload->size);
- opi->in_port = htons(in_port);
- opi->reason = reason;
- ofpbuf_put(buf, payload->data, send_len);
- update_openflow_length(buf);
+ ofpmsg_update_length(msg);
- return buf;
+ return msg;
}
-
+\f
/* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
struct ofpbuf *
-make_echo_request(void)
+make_echo_request(enum ofp_version ofp_version)
{
- struct ofp_header *rq;
- struct ofpbuf *out = ofpbuf_new(sizeof *rq);
- rq = ofpbuf_put_uninit(out, sizeof *rq);
- rq->version = OFP10_VERSION;
- rq->type = OFPT_ECHO_REQUEST;
- rq->length = htons(sizeof *rq);
- rq->xid = htonl(0);
- return out;
+ return ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, ofp_version,
+ htonl(0), 0);
}
/* Creates and returns an OFPT_ECHO_REPLY message matching the
struct ofpbuf *
make_echo_reply(const struct ofp_header *rq)
{
- size_t size = ntohs(rq->length);
- struct ofpbuf *out = ofpbuf_new(size);
- struct ofp_header *reply = ofpbuf_put(out, rq, size);
- reply->type = OFPT_ECHO_REPLY;
- return out;
+ struct ofpbuf rq_buf;
+ struct ofpbuf *reply;
+
+ ofpbuf_use_const(&rq_buf, rq, ntohs(rq->length));
+ ofpraw_pull_assert(&rq_buf);
+
+ reply = ofpraw_alloc_reply(OFPRAW_OFPT_ECHO_REPLY, rq, rq_buf.size);
+ ofpbuf_put(reply, rq_buf.data, rq_buf.size);
+ return reply;
}
struct ofpbuf *
-ofputil_encode_barrier_request(void)
+ofputil_encode_barrier_request(enum ofp_version ofp_version)
{
- struct ofpbuf *msg;
+ enum ofpraw type;
- make_openflow(sizeof(struct ofp_header), OFPT10_BARRIER_REQUEST, &msg);
- return msg;
+ switch (ofp_version) {
+ case OFP12_VERSION:
+ case OFP11_VERSION:
+ type = OFPRAW_OFPT11_BARRIER_REQUEST;
+ break;
+
+ case OFP10_VERSION:
+ type = OFPRAW_OFPT10_BARRIER_REQUEST;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ return ofpraw_alloc(type, ofp_version, 0);
}
const char *
case OFPP_FLOOD:
case OFPP_ALL:
case OFPP_CONTROLLER:
+ case OFPP_NONE:
case OFPP_LOCAL:
return 0;
ds_put_cstr(s, name);
}
-static enum ofperr
-check_resubmit_table(const struct nx_action_resubmit *nar)
-{
- if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- return 0;
-}
-
-static enum ofperr
-check_output_reg(const struct nx_action_output_reg *naor,
- const struct flow *flow)
-{
- struct mf_subfield src;
- size_t i;
-
- for (i = 0; i < sizeof naor->zero; i++) {
- if (naor->zero[i]) {
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- }
-
- nxm_decode(&src, naor->src, naor->ofs_nbits);
- return mf_check_src(&src, flow);
-}
-
-enum ofperr
-validate_actions(const union ofp_action *actions, size_t n_actions,
- const struct flow *flow, int max_ports)
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', tries to pull the first element from the array. If
+ * successful, initializes '*pp' with an abstract representation of the
+ * port and returns 0. If no ports remain to be decoded, returns EOF.
+ * On an error, returns a positive OFPERR_* value. */
+int
+ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
+ struct ofputil_phy_port *pp)
{
- const union ofp_action *a;
- size_t left;
-
- OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
- enum ofperr error;
- uint16_t port;
- int code;
-
- code = ofputil_decode_action(a);
- if (code < 0) {
- error = -code;
- VLOG_WARN_RL(&bad_ofmsg_rl,
- "action decoding error at offset %td (%s)",
- (a - actions) * sizeof *a, ofperr_get_name(error));
-
- return error;
- }
-
- error = 0;
- switch ((enum ofputil_action_code) code) {
- case OFPUTIL_OFPAT10_OUTPUT:
- error = ofputil_check_output_port(ntohs(a->output.port),
- max_ports);
- break;
-
- case OFPUTIL_OFPAT10_SET_VLAN_VID:
- if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
- error = OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- break;
-
- case OFPUTIL_OFPAT10_SET_VLAN_PCP:
- if (a->vlan_pcp.vlan_pcp & ~7) {
- error = OFPERR_OFPBAC_BAD_ARGUMENT;
- }
- break;
-
- case OFPUTIL_OFPAT10_ENQUEUE:
- port = ntohs(((const struct ofp_action_enqueue *) a)->port);
- if (port >= max_ports && port != OFPP_IN_PORT
- && port != OFPP_LOCAL) {
- error = OFPERR_OFPBAC_BAD_OUT_PORT;
- }
- break;
-
- case OFPUTIL_NXAST_REG_MOVE:
- error = nxm_check_reg_move((const struct nx_action_reg_move *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_REG_LOAD:
- error = nxm_check_reg_load((const struct nx_action_reg_load *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_MULTIPATH:
- error = multipath_check((const struct nx_action_multipath *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_AUTOPATH:
- error = autopath_check((const struct nx_action_autopath *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_BUNDLE:
- case OFPUTIL_NXAST_BUNDLE_LOAD:
- error = bundle_check((const struct nx_action_bundle *) a,
- max_ports, flow);
- break;
-
- case OFPUTIL_NXAST_OUTPUT_REG:
- error = check_output_reg((const struct nx_action_output_reg *) a,
- flow);
- break;
-
- case OFPUTIL_NXAST_RESUBMIT_TABLE:
- error = check_resubmit_table(
- (const struct nx_action_resubmit *) a);
- break;
-
- case OFPUTIL_NXAST_LEARN:
- error = learn_check((const struct nx_action_learn *) a, flow);
- break;
-
- case OFPUTIL_NXAST_CONTROLLER:
- if (((const struct nx_action_controller *) a)->zero) {
- error = OFPERR_NXBAC_MUST_BE_ZERO;
- }
- break;
-
- case OFPUTIL_OFPAT10_STRIP_VLAN:
- case OFPUTIL_OFPAT10_SET_NW_SRC:
- case OFPUTIL_OFPAT10_SET_NW_DST:
- case OFPUTIL_OFPAT10_SET_NW_TOS:
- case OFPUTIL_OFPAT10_SET_TP_SRC:
- case OFPUTIL_OFPAT10_SET_TP_DST:
- case OFPUTIL_OFPAT10_SET_DL_SRC:
- case OFPUTIL_OFPAT10_SET_DL_DST:
- case OFPUTIL_NXAST_RESUBMIT:
- case OFPUTIL_NXAST_SET_TUNNEL:
- case OFPUTIL_NXAST_SET_QUEUE:
- case OFPUTIL_NXAST_POP_QUEUE:
- case OFPUTIL_NXAST_NOTE:
- case OFPUTIL_NXAST_SET_TUNNEL64:
- case OFPUTIL_NXAST_EXIT:
- case OFPUTIL_NXAST_DEC_TTL:
- case OFPUTIL_NXAST_FIN_TIMEOUT:
- break;
- }
-
- if (error) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)",
- (a - actions) * sizeof *a, ofperr_get_name(error));
- return error;
- }
- }
- if (left) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu",
- (n_actions - left) * sizeof *a);
- return OFPERR_OFPBAC_BAD_LEN;
+ switch (ofp_version) {
+ case OFP10_VERSION: {
+ const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp);
+ return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF;
}
- return 0;
-}
-
-struct ofputil_action {
- int code;
- unsigned int min_len;
- unsigned int max_len;
-};
-
-static const struct ofputil_action action_bad_type
- = { -OFPERR_OFPBAC_BAD_TYPE, 0, UINT_MAX };
-static const struct ofputil_action action_bad_len
- = { -OFPERR_OFPBAC_BAD_LEN, 0, UINT_MAX };
-static const struct ofputil_action action_bad_vendor
- = { -OFPERR_OFPBAC_BAD_VENDOR, 0, UINT_MAX };
-
-static const struct ofputil_action *
-ofputil_decode_ofpat_action(const union ofp_action *a)
-{
- enum ofp10_action_type type = ntohs(a->type);
-
- switch (type) {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
- case ENUM: { \
- static const struct ofputil_action action = { \
- OFPUTIL_##ENUM, \
- sizeof(struct STRUCT), \
- sizeof(struct STRUCT) \
- }; \
- return &action; \
- }
-#include "ofp-util.def"
-
- case OFPAT10_VENDOR:
- default:
- return &action_bad_type;
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op);
+ return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
}
-}
-
-static const struct ofputil_action *
-ofputil_decode_nxast_action(const union ofp_action *a)
-{
- const struct nx_action_header *nah = (const struct nx_action_header *) a;
- enum nx_action_subtype subtype = ntohs(nah->subtype);
-
- switch (subtype) {
-#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
- case ENUM: { \
- static const struct ofputil_action action = { \
- OFPUTIL_##ENUM, \
- sizeof(struct STRUCT), \
- EXTENSIBLE ? UINT_MAX : sizeof(struct STRUCT) \
- }; \
- return &action; \
- }
-#include "ofp-util.def"
-
- case NXAST_SNAT__OBSOLETE:
- case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
default:
- return &action_bad_type;
- }
-}
-
-/* Parses 'a' to determine its type. Returns a nonnegative OFPUTIL_OFPAT10_* or
- * OFPUTIL_NXAST_* constant if successful, otherwise a negative OFPERR_* error
- * code.
- *
- * The caller must have already verified that 'a''s length is correct (that is,
- * a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no
- * longer than the amount of space allocated to 'a').
- *
- * This function verifies that 'a''s length is correct for the type of action
- * that it represents. */
-int
-ofputil_decode_action(const union ofp_action *a)
-{
- const struct ofputil_action *action;
- uint16_t len = ntohs(a->header.len);
-
- if (a->type != htons(OFPAT10_VENDOR)) {
- action = ofputil_decode_ofpat_action(a);
- } else {
- switch (ntohl(a->vendor.vendor)) {
- case NX_VENDOR_ID:
- if (len < sizeof(struct nx_action_header)) {
- return -OFPERR_OFPBAC_BAD_LEN;
- }
- action = ofputil_decode_nxast_action(a);
- break;
- default:
- action = &action_bad_vendor;
- break;
- }
+ NOT_REACHED();
}
-
- return (len >= action->min_len && len <= action->max_len
- ? action->code
- : -OFPERR_OFPBAC_BAD_LEN);
}
-/* Parses 'a' and returns its type as an OFPUTIL_OFPAT10_* or OFPUTIL_NXAST_*
- * constant. The caller must have already validated that 'a' is a valid action
- * understood by Open vSwitch (e.g. by a previous successful call to
- * ofputil_decode_action()). */
-enum ofputil_action_code
-ofputil_decode_action_unsafe(const union ofp_action *a)
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', returns the number of elements. */
+size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b)
{
- const struct ofputil_action *action;
-
- if (a->type != htons(OFPAT10_VENDOR)) {
- action = ofputil_decode_ofpat_action(a);
- } else {
- action = ofputil_decode_nxast_action(a);
- }
-
- return action->code;
+ return b->size / ofputil_get_phy_port_size(ofp_version);
}
/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if
ofputil_action_code_from_name(const char *name)
{
static const char *names[OFPUTIL_N_ACTIONS] = {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME,
+ NULL,
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME,
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) NAME,
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
#include "ofp-util.def"
};
ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
{
switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+ NOT_REACHED();
+
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#define OFPAT11_ACTION OFPAT10_ACTION
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
#include "ofp-util.def"
ofputil_init_##ENUM(s); \
return s; \
}
+#define OFPAT11_ACTION OFPAT10_ACTION
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
void \
ofputil_init_##ENUM(struct STRUCT *s) \
}
#include "ofp-util.def"
-/* Returns true if 'action' outputs to 'port', false otherwise. */
-bool
-action_outputs_to_port(const union ofp_action *action, ovs_be16 port)
-{
- switch (ntohs(action->type)) {
- case OFPAT10_OUTPUT:
- return action->output.port == port;
- case OFPAT10_ENQUEUE:
- return ((const struct ofp_action_enqueue *) action)->port == port;
- default:
- return false;
- }
-}
-
-/* "Normalizes" the wildcards in 'rule'. That means:
- *
- * 1. If the type of level N is known, then only the valid fields for that
- * level may be specified. For example, ARP does not have a TOS field,
- * so nw_tos must be wildcarded if 'rule' specifies an ARP flow.
- * Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
- * ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an
- * IPv4 flow.
- *
- * 2. If the type of level N is not known (or not understood by Open
- * vSwitch), then no fields at all for that level may be specified. For
- * example, Open vSwitch does not understand SCTP, an L4 protocol, so the
- * L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
- * SCTP flow.
- */
-void
-ofputil_normalize_rule(struct cls_rule *rule)
+static void
+ofputil_normalize_rule__(struct cls_rule *rule, bool may_log)
{
enum {
MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */
wc.wildcards |= FWW_NW_TTL;
}
if (!(may_match & MAY_ARP_SHA)) {
- wc.wildcards |= FWW_ARP_SHA;
+ memset(wc.arp_sha_mask, 0, ETH_ADDR_LEN);
}
if (!(may_match & MAY_ARP_THA)) {
- wc.wildcards |= FWW_ARP_THA;
+ memset(wc.arp_tha_mask, 0, ETH_ADDR_LEN);
}
if (!(may_match & MAY_IPV6)) {
wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any;
- wc.wildcards |= FWW_IPV6_LABEL;
+ wc.ipv6_label_mask = htonl(0);
}
if (!(may_match & MAY_ND_TARGET)) {
wc.nd_target_mask = in6addr_any;
/* Log any changes. */
if (!flow_wildcards_equal(&wc, &rule->wc)) {
- bool log = !VLOG_DROP_INFO(&bad_ofmsg_rl);
+ bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl);
char *pre = log ? cls_rule_to_string(rule) : NULL;
rule->wc = wc;
}
}
-/* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if
- * successful, otherwise an OpenFlow error.
+/* "Normalizes" the wildcards in 'rule'. That means:
+ *
+ * 1. If the type of level N is known, then only the valid fields for that
+ * level may be specified. For example, ARP does not have a TOS field,
+ * so nw_tos must be wildcarded if 'rule' specifies an ARP flow.
+ * Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
+ * ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an
+ * IPv4 flow.
*
- * If successful, the first action is stored in '*actionsp' and the number of
- * "union ofp_action" size elements into '*n_actionsp'. Otherwise NULL and 0
- * are stored, respectively.
+ * 2. If the type of level N is not known (or not understood by Open
+ * vSwitch), then no fields at all for that level may be specified. For
+ * example, Open vSwitch does not understand SCTP, an L4 protocol, so the
+ * L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
+ * SCTP flow.
*
- * This function does not check that the actions are valid (the caller should
- * do so, with validate_actions()). The caller is also responsible for making
- * sure that 'b->data' is initially aligned appropriately for "union
- * ofp_action". */
-enum ofperr
-ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len,
- union ofp_action **actionsp, size_t *n_actionsp)
-{
- if (actions_len % OFP_ACTION_ALIGN != 0) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
- "is not a multiple of %d", actions_len, OFP_ACTION_ALIGN);
- goto error;
- }
-
- *actionsp = ofpbuf_try_pull(b, actions_len);
- if (*actionsp == NULL) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
- "exceeds remaining message length (%zu)",
- actions_len, b->size);
- goto error;
- }
-
- *n_actionsp = actions_len / OFP_ACTION_ALIGN;
- return 0;
-
-error:
- *actionsp = NULL;
- *n_actionsp = 0;
- return OFPERR_OFPBRC_BAD_LEN;
-}
-
-bool
-ofputil_actions_equal(const union ofp_action *a, size_t n_a,
- const union ofp_action *b, size_t n_b)
+ * If this function changes 'rule', it logs a rate-limited informational
+ * message. */
+void
+ofputil_normalize_rule(struct cls_rule *rule)
{
- return n_a == n_b && (!n_a || !memcmp(a, b, n_a * sizeof *a));
+ ofputil_normalize_rule__(rule, true);
}
-union ofp_action *
-ofputil_actions_clone(const union ofp_action *actions, size_t n)
+/* Same as ofputil_normalize_rule() without the logging. Thus, this function
+ * is suitable for a program's internal use, whereas ofputil_normalize_rule()
+ * sense for use on flows received from elsewhere (so that a bug in the program
+ * that sent them can be reported and corrected). */
+void
+ofputil_normalize_rule_quiet(struct cls_rule *rule)
{
- return n ? xmemdup(actions, n * sizeof *actions) : NULL;
+ ofputil_normalize_rule__(rule, false);
}
/* Parses a key or a key-value pair from '*stringp'.
for (value_len = 0; level > 0; value_len++) {
switch (value[value_len]) {
case '\0':
- ovs_fatal(0, "unbalanced parentheses in argument to %s", key);
+ level = 0;
+ break;
case '(':
level++;
#ifndef OFPAT10_ACTION
#define OFPAT10_ACTION(ENUM, STRUCT, NAME)
#endif
-OFPAT10_ACTION(OFPAT10_OUTPUT, ofp_action_output, "output")
+OFPAT10_ACTION(OFPAT10_OUTPUT, ofp10_action_output, "output")
OFPAT10_ACTION(OFPAT10_SET_VLAN_VID, ofp_action_vlan_vid, "mod_vlan_vid")
OFPAT10_ACTION(OFPAT10_SET_VLAN_PCP, ofp_action_vlan_pcp, "mod_vlan_pcp")
OFPAT10_ACTION(OFPAT10_STRIP_VLAN, ofp_action_header, "strip_vlan")
OFPAT10_ACTION(OFPAT10_SET_TP_SRC, ofp_action_tp_port, "mod_tp_src")
OFPAT10_ACTION(OFPAT10_SET_TP_DST, ofp_action_tp_port, "mod_tp_dst")
OFPAT10_ACTION(OFPAT10_ENQUEUE, ofp_action_enqueue, "enqueue")
-#undef OFPAT10_ACTION
+
+#ifndef OFPAT11_ACTION
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME)
+#endif
+OFPAT11_ACTION(OFPAT11_OUTPUT, ofp11_action_output, "output")
+OFPAT11_ACTION(OFPAT11_SET_VLAN_VID, ofp_action_vlan_vid, "mod_vlan_vid")
+OFPAT11_ACTION(OFPAT11_SET_VLAN_PCP, ofp_action_vlan_pcp, "mod_vlan_pcp")
+OFPAT11_ACTION(OFPAT11_SET_DL_SRC, ofp_action_dl_addr, "mod_dl_src")
+OFPAT11_ACTION(OFPAT11_SET_DL_DST, ofp_action_dl_addr, "mod_dl_dst")
+OFPAT11_ACTION(OFPAT11_SET_NW_SRC, ofp_action_nw_addr, "mod_nw_src")
+OFPAT11_ACTION(OFPAT11_SET_NW_DST, ofp_action_nw_addr, "mod_nw_dst")
+OFPAT11_ACTION(OFPAT11_SET_NW_TOS, ofp_action_nw_tos, "mod_nw_tos")
+//OFPAT11_ACTION(OFPAT11_SET_NW_ECN, ofp11_action_nw_ecn, "mod_nw_ecn")
+OFPAT11_ACTION(OFPAT11_SET_TP_SRC, ofp_action_tp_port, "mod_tp_src")
+OFPAT11_ACTION(OFPAT11_SET_TP_DST, ofp_action_tp_port, "mod_tp_dst")
+//OFPAT11_ACTION(OFPAT11_PUSH_VLAN, ofp11_action_push, "push_vlan")
+//OFPAT11_ACTION(OFPAT11_POP_VLAN, ofp_action_header, "pop_vlan")
+//OFPAT11_ACTION(OFPAT11_SET_QUEUE, ofp11_action_set_queue, "set_queue")
+//OFPAT11_ACTION(OFPAT11_SET_NW_TTL, ofp11_action_nw_ttl, "set_nw_ttl")
+//OFPAT11_ACTION(OFPAT11_DEC_NW_TTL, ofp_action_header, "dec_ttl")
#ifndef NXAST_ACTION
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)
#endif
-NXAST_ACTION(NXAST_RESUBMIT, nx_action_resubmit, 0, "resubmit")
-NXAST_ACTION(NXAST_SET_TUNNEL, nx_action_set_tunnel, 0, "set_tunnel")
-NXAST_ACTION(NXAST_SET_QUEUE, nx_action_set_queue, 0, "set_queue")
-NXAST_ACTION(NXAST_POP_QUEUE, nx_action_pop_queue, 0, "pop_queue")
-NXAST_ACTION(NXAST_REG_MOVE, nx_action_reg_move, 0, "move")
-NXAST_ACTION(NXAST_REG_LOAD, nx_action_reg_load, 0, "load")
-NXAST_ACTION(NXAST_NOTE, nx_action_note, 1, "note")
-NXAST_ACTION(NXAST_SET_TUNNEL64, nx_action_set_tunnel64, 0, "set_tunnel64")
-NXAST_ACTION(NXAST_MULTIPATH, nx_action_multipath, 0, "multipath")
-NXAST_ACTION(NXAST_AUTOPATH, nx_action_autopath, 0, "autopath")
-NXAST_ACTION(NXAST_BUNDLE, nx_action_bundle, 1, "bundle")
-NXAST_ACTION(NXAST_BUNDLE_LOAD, nx_action_bundle, 1, "bundle_load")
-NXAST_ACTION(NXAST_RESUBMIT_TABLE, nx_action_resubmit, 0, NULL)
-NXAST_ACTION(NXAST_OUTPUT_REG, nx_action_output_reg, 0, NULL)
-NXAST_ACTION(NXAST_LEARN, nx_action_learn, 1, "learn")
-NXAST_ACTION(NXAST_EXIT, nx_action_header, 0, "exit")
-NXAST_ACTION(NXAST_DEC_TTL, nx_action_header, 0, "dec_ttl")
-NXAST_ACTION(NXAST_FIN_TIMEOUT, nx_action_fin_timeout, 0, "fin_timeout")
-NXAST_ACTION(NXAST_CONTROLLER, nx_action_controller, 0, "controller")
+NXAST_ACTION(NXAST_RESUBMIT, nx_action_resubmit, 0, "resubmit")
+NXAST_ACTION(NXAST_SET_TUNNEL, nx_action_set_tunnel, 0, "set_tunnel")
+NXAST_ACTION(NXAST_SET_QUEUE, nx_action_set_queue, 0, "set_queue")
+NXAST_ACTION(NXAST_POP_QUEUE, nx_action_pop_queue, 0, "pop_queue")
+NXAST_ACTION(NXAST_REG_MOVE, nx_action_reg_move, 0, "move")
+NXAST_ACTION(NXAST_REG_LOAD, nx_action_reg_load, 0, "load")
+NXAST_ACTION(NXAST_NOTE, nx_action_note, 1, "note")
+NXAST_ACTION(NXAST_SET_TUNNEL64, nx_action_set_tunnel64, 0, "set_tunnel64")
+NXAST_ACTION(NXAST_MULTIPATH, nx_action_multipath, 0, "multipath")
+NXAST_ACTION(NXAST_AUTOPATH__DEPRECATED,nx_action_autopath, 0, "autopath")
+NXAST_ACTION(NXAST_BUNDLE, nx_action_bundle, 1, "bundle")
+NXAST_ACTION(NXAST_BUNDLE_LOAD, nx_action_bundle, 1, "bundle_load")
+NXAST_ACTION(NXAST_RESUBMIT_TABLE, nx_action_resubmit, 0, NULL)
+NXAST_ACTION(NXAST_OUTPUT_REG, nx_action_output_reg, 0, NULL)
+NXAST_ACTION(NXAST_LEARN, nx_action_learn, 1, "learn")
+NXAST_ACTION(NXAST_EXIT, nx_action_header, 0, "exit")
+NXAST_ACTION(NXAST_DEC_TTL, nx_action_header, 0, "dec_ttl")
+NXAST_ACTION(NXAST_FIN_TIMEOUT, nx_action_fin_timeout, 0, "fin_timeout")
+NXAST_ACTION(NXAST_CONTROLLER, nx_action_controller, 0, "controller")
+NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids, 1, NULL)
+
+#undef OFPAT10_ACTION
+#undef OFPAT11_ACTION
#undef NXAST_ACTION
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stddef.h>
#include <stdint.h>
#include "classifier.h"
+#include "compiler.h"
#include "flow.h"
#include "netdev.h"
#include "openflow/nicira-ext.h"
struct cls_rule;
struct ofpbuf;
-/* Basic decoding and length validation of OpenFlow messages. */
-enum ofputil_msg_code {
- OFPUTIL_MSG_INVALID,
-
- /* OFPT_* messages. */
- OFPUTIL_OFPT_HELLO,
- OFPUTIL_OFPT_ERROR,
- OFPUTIL_OFPT_ECHO_REQUEST,
- OFPUTIL_OFPT_ECHO_REPLY,
- OFPUTIL_OFPT_FEATURES_REQUEST,
- OFPUTIL_OFPT_FEATURES_REPLY,
- OFPUTIL_OFPT_GET_CONFIG_REQUEST,
- OFPUTIL_OFPT_GET_CONFIG_REPLY,
- OFPUTIL_OFPT_SET_CONFIG,
- OFPUTIL_OFPT_PACKET_IN,
- OFPUTIL_OFPT_FLOW_REMOVED,
- OFPUTIL_OFPT_PORT_STATUS,
- OFPUTIL_OFPT_PACKET_OUT,
- OFPUTIL_OFPT_FLOW_MOD,
- OFPUTIL_OFPT_PORT_MOD,
- OFPUTIL_OFPT_BARRIER_REQUEST,
- OFPUTIL_OFPT_BARRIER_REPLY,
- OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST,
- OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY,
-
- /* OFPST_* stat requests. */
- OFPUTIL_OFPST_DESC_REQUEST,
- OFPUTIL_OFPST_FLOW_REQUEST,
- OFPUTIL_OFPST_AGGREGATE_REQUEST,
- OFPUTIL_OFPST_TABLE_REQUEST,
- OFPUTIL_OFPST_PORT_REQUEST,
- OFPUTIL_OFPST_QUEUE_REQUEST,
-
- /* OFPST_* stat replies. */
- OFPUTIL_OFPST_DESC_REPLY,
- OFPUTIL_OFPST_FLOW_REPLY,
- OFPUTIL_OFPST_QUEUE_REPLY,
- OFPUTIL_OFPST_PORT_REPLY,
- OFPUTIL_OFPST_TABLE_REPLY,
- OFPUTIL_OFPST_AGGREGATE_REPLY,
-
- /* NXT_* messages. */
- OFPUTIL_NXT_ROLE_REQUEST,
- OFPUTIL_NXT_ROLE_REPLY,
- OFPUTIL_NXT_SET_FLOW_FORMAT,
- OFPUTIL_NXT_FLOW_MOD_TABLE_ID,
- OFPUTIL_NXT_FLOW_MOD,
- OFPUTIL_NXT_FLOW_REMOVED,
- OFPUTIL_NXT_SET_PACKET_IN_FORMAT,
- OFPUTIL_NXT_PACKET_IN,
- OFPUTIL_NXT_FLOW_AGE,
- OFPUTIL_NXT_SET_ASYNC_CONFIG,
- OFPUTIL_NXT_SET_CONTROLLER_ID,
-
- /* NXST_* stat requests. */
- OFPUTIL_NXST_FLOW_REQUEST,
- OFPUTIL_NXST_AGGREGATE_REQUEST,
-
- /* NXST_* stat replies. */
- OFPUTIL_NXST_FLOW_REPLY,
- OFPUTIL_NXST_AGGREGATE_REPLY
-};
-
-struct ofputil_msg_type;
-enum ofperr ofputil_decode_msg_type(const struct ofp_header *,
- const struct ofputil_msg_type **);
-enum ofperr ofputil_decode_msg_type_partial(const struct ofp_header *,
- size_t length,
- const struct ofputil_msg_type **);
-enum ofputil_msg_code ofputil_msg_type_code(const struct ofputil_msg_type *);
-const char *ofputil_msg_type_name(const struct ofputil_msg_type *);
-
/* Port numbers. */
enum ofperr ofputil_port_from_ofp11(ovs_be32 ofp11_port, uint16_t *ofp10_port);
ovs_be32 ofputil_port_to_ofp11(uint16_t ofp10_port);
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);
OFPUTIL_P_NXM_TID = 1 << 3, /* NXM + flow_mod_table_id extension. */
#define OFPUTIL_P_NXM_ANY (OFPUTIL_P_NXM | OFPUTIL_P_NXM_TID)
+ /* OpenFlow 1.2 */
+ OFPUTIL_P_OF12 = 1 << 4, /* OpenFlow 1.2 flow format. */
+
/* All protocols. */
#define OFPUTIL_P_ANY (OFPUTIL_P_OF10_ANY | OFPUTIL_P_NXM_ANY)
extern enum ofputil_protocol ofputil_flow_dump_protocols[];
extern size_t ofputil_n_flow_dump_protocols;
-enum ofputil_protocol ofputil_protocol_from_ofp_version(int version);
-uint8_t ofputil_protocol_to_ofp_version(enum ofputil_protocol);
+enum ofputil_protocol
+ofputil_protocol_from_ofp_version(enum ofp_version version);
+enum ofp_version ofputil_protocol_to_ofp_version(enum ofputil_protocol);
bool ofputil_protocol_is_valid(enum ofputil_protocol);
enum ofputil_protocol ofputil_protocol_set_tid(enum ofputil_protocol,
bool ofputil_nx_flow_format_is_valid(enum nx_flow_format);
const char *ofputil_nx_flow_format_to_string(enum nx_flow_format);
-/* Work with OpenFlow 1.0 ofp_match. */
-void ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *);
-void ofputil_cls_rule_from_match(const struct ofp_match *,
- unsigned int priority, struct cls_rule *);
+/* Work with ofp10_match. */
+void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *);
+void ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *,
+ unsigned int priority,
+ struct cls_rule *);
void ofputil_normalize_rule(struct cls_rule *);
-void ofputil_cls_rule_to_match(const struct cls_rule *, struct ofp_match *);
+void ofputil_normalize_rule_quiet(struct cls_rule *);
+void ofputil_cls_rule_to_ofp10_match(const struct cls_rule *,
+ struct ofp10_match *);
+
+/* Work with ofp11_match. */
+enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, unsigned int priority,
+ struct cls_rule *,
+ uint16_t *padded_match_len);
+enum ofperr ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *,
+ unsigned int priority,
+ struct cls_rule *);
+void ofputil_cls_rule_to_ofp11_match(const struct cls_rule *,
+ struct ofp11_match *);
/* dl_type translation between OpenFlow and 'struct flow' format. */
ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type);
/* 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;
uint32_t buffer_id;
uint16_t out_port;
uint16_t flags;
- union ofp_action *actions;
- size_t n_actions;
+ struct ofpact *ofpacts; /* Series of "struct ofpact"s. */
+ size_t ofpacts_len; /* Length of ofpacts, in bytes. */
};
enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
const struct ofp_header *,
- enum ofputil_protocol);
+ enum ofputil_protocol,
+ struct ofpbuf *ofpacts);
struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
enum ofputil_protocol);
int hard_age; /* Seconds since last change, -1 if unknown. */
uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */
uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */
- union ofp_action *actions;
- size_t n_actions;
+ struct ofpact *ofpacts;
+ size_t ofpacts_len;
};
int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *,
struct ofpbuf *msg,
- bool flow_age_extension);
+ bool flow_age_extension,
+ struct ofpbuf *ofpacts);
void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *,
struct list *replies);
struct ofpbuf *ofputil_encode_aggregate_stats_reply(
const struct ofputil_aggregate_stats *stats,
- const struct ofp_stats_msg *request);
+ const struct ofp_header *request);
+enum ofperr ofputil_decode_aggregate_stats_reply(
+ struct ofputil_aggregate_stats *,
+ const struct ofp_header *reply);
/* Flow removed message, independent of protocol. */
struct ofputil_flow_removed {
uint32_t duration_sec;
uint32_t duration_nsec;
uint16_t idle_timeout;
+ uint16_t hard_timeout;
uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */
uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */
};
struct flow_metadata fmd; /* Metadata at creation time. */
};
-int ofputil_decode_packet_in(struct ofputil_packet_in *,
- const struct ofp_header *);
+enum ofperr ofputil_decode_packet_in(struct ofputil_packet_in *,
+ const struct ofp_header *);
struct ofpbuf *ofputil_encode_packet_in(const struct ofputil_packet_in *,
+ enum ofputil_protocol protocol,
enum nx_packet_in_format);
const char *ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason);
bool ofputil_packet_in_reason_from_string(const char *,
enum ofp_packet_in_reason *);
-/* Abstract packet-out message. */
+/* Abstract packet-out message.
+ *
+ * ofputil_decode_packet_out() will ensure that 'in_port' is a physical port
+ * (OFPP_MAX or less) or one of OFPP_LOCAL, OFPP_NONE, or OFPP_CONTROLLER. */
struct ofputil_packet_out {
const void *packet; /* Packet data, if buffer_id == UINT32_MAX. */
size_t packet_len; /* Length of packet data in bytes. */
uint32_t buffer_id; /* Buffer id or UINT32_MAX if no buffer. */
- uint16_t in_port; /* Packet's input port or OFPP_NONE. */
- union ofp_action *actions; /* Actions. */
- size_t n_actions; /* Number of elements in 'actions' array. */
+ uint16_t in_port; /* Packet's input port. */
+ struct ofpact *ofpacts; /* Actions. */
+ size_t ofpacts_len; /* Size of ofpacts in bytes. */
};
enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *,
- const struct ofp_packet_out *);
-struct ofpbuf *ofputil_encode_packet_out(const struct ofputil_packet_out *);
+ const struct ofp_header *,
+ struct ofpbuf *ofpacts);
+struct ofpbuf *ofputil_encode_packet_out(const struct ofputil_packet_out *,
+ enum ofputil_protocol protocol);
enum ofputil_port_config {
/* OpenFlow 1.0 and 1.1 share these values for these port config bits. */
};
enum ofputil_capabilities {
- /* OpenFlow 1.0 and 1.1 share these values for these capabilities. */
+ /* OpenFlow 1.0, 1.1 and 1.2 share these values for these capabilities. */
OFPUTIL_C_FLOW_STATS = 1 << 0, /* Flow statistics. */
OFPUTIL_C_TABLE_STATS = 1 << 1, /* Table statistics. */
OFPUTIL_C_PORT_STATS = 1 << 2, /* Port statistics. */
OFPUTIL_C_IP_REASM = 1 << 5, /* Can reassemble IP fragments. */
OFPUTIL_C_QUEUE_STATS = 1 << 6, /* Queue statistics. */
+
+ /* OpenFlow 1.0 and 1.1 share this capability. */
OFPUTIL_C_ARP_MATCH_IP = 1 << 7, /* Match IP addresses in ARP pkts. */
/* OpenFlow 1.0 only. */
OFPUTIL_C_STP = 1 << 3, /* 802.1d spanning tree. */
- /* OpenFlow 1.1 only. */
+ /* OpenFlow 1.1 and 1.2 share this capability. */
OFPUTIL_C_GROUP_STATS = 1 << 4, /* Group statistics. */
+
+ /* OpenFlow 1.2 only */
+ OFPUTIL_C_PORT_BLOCKED = 1 << 8, /* Switch will block looping ports */
};
enum ofputil_action_bitmap {
enum ofputil_action_bitmap actions;
};
-enum ofperr ofputil_decode_switch_features(const struct ofp_switch_features *,
+enum ofperr ofputil_decode_switch_features(const struct ofp_header *,
struct ofputil_switch_features *,
struct ofpbuf *);
-int ofputil_pull_switch_features_port(struct ofpbuf *,
- struct ofputil_phy_port *);
-size_t ofputil_count_phy_ports(const struct ofp_switch_features *);
struct ofpbuf *ofputil_encode_switch_features(
const struct ofputil_switch_features *, enum ofputil_protocol,
ovs_be32 xid);
void ofputil_put_switch_features_port(const struct ofputil_phy_port *,
struct ofpbuf *);
+bool ofputil_switch_features_ports_trunc(struct ofpbuf *b);
+
+/* phy_port helper functions. */
+int ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *,
+ struct ofputil_phy_port *);
+size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *);
/* Abstract ofp_port_status. */
struct ofputil_port_status {
struct ofputil_phy_port desc;
};
-enum ofperr ofputil_decode_port_status(const struct ofp_port_status *,
+enum ofperr ofputil_decode_port_status(const struct ofp_header *,
struct ofputil_port_status *);
struct ofpbuf *ofputil_encode_port_status(const struct ofputil_port_status *,
enum ofputil_protocol);
struct ofpbuf *ofputil_encode_port_mod(const struct ofputil_port_mod *,
enum ofputil_protocol);
-/* OpenFlow protocol utility functions. */
-void *make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **);
-void *make_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf **);
-
-void *make_openflow_xid(size_t openflow_len, uint8_t type,
- ovs_be32 xid, struct ofpbuf **);
-void *make_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
- struct ofpbuf **);
-
-void *put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *);
-void *put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
- struct ofpbuf *);
-
-void *put_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf *);
-void *put_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
- struct ofpbuf *);
-
-void update_openflow_length(struct ofpbuf *);
-
-void *ofputil_make_stats_request(size_t openflow_len, uint16_t type,
- uint32_t subtype, struct ofpbuf **);
-void *ofputil_make_stats_reply(size_t openflow_len,
- const struct ofp_stats_msg *request,
- struct ofpbuf **);
-
-void ofputil_start_stats_reply(const struct ofp_stats_msg *request,
- struct list *);
-struct ofpbuf *ofputil_reserve_stats_reply(size_t len, struct list *);
-void *ofputil_append_stats_reply(size_t len, struct list *);
-
-const void *ofputil_stats_body(const struct ofp_header *);
-size_t ofputil_stats_body_len(const struct ofp_header *);
-
-const void *ofputil_nxstats_body(const struct ofp_header *);
-size_t ofputil_nxstats_body_len(const struct ofp_header *);
-
-struct ofpbuf *make_flow_mod(uint16_t command, const struct cls_rule *,
- size_t actions_len);
-struct ofpbuf *make_add_flow(const struct cls_rule *, uint32_t buffer_id,
- uint16_t max_idle, size_t actions_len);
-struct ofpbuf *make_del_flow(const struct cls_rule *);
-struct ofpbuf *make_add_simple_flow(const struct cls_rule *,
- uint32_t buffer_id, uint16_t out_port,
- uint16_t max_idle);
-struct ofpbuf *make_packet_in(uint32_t buffer_id, uint16_t in_port,
- uint8_t reason,
- const struct ofpbuf *payload, int max_send_len);
-struct ofpbuf *make_echo_request(void);
+/* Abstract nx_flow_monitor_request. */
+struct ofputil_flow_monitor_request {
+ uint32_t id;
+ enum nx_flow_monitor_flags flags;
+ uint16_t out_port;
+ uint8_t table_id;
+ struct cls_rule match;
+};
+
+int ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *,
+ struct ofpbuf *msg);
+void ofputil_append_flow_monitor_request(
+ const struct ofputil_flow_monitor_request *, struct ofpbuf *msg);
+
+/* Abstract nx_flow_update. */
+struct ofputil_flow_update {
+ enum nx_flow_update_event event;
+
+ /* Used only for NXFME_ADDED, NXFME_DELETED, NXFME_MODIFIED. */
+ enum ofp_flow_removed_reason reason;
+ uint16_t idle_timeout;
+ uint16_t hard_timeout;
+ uint8_t table_id;
+ ovs_be64 cookie;
+ struct cls_rule *match;
+ struct ofpact *ofpacts;
+ size_t ofpacts_len;
+
+ /* Used only for NXFME_ABBREV. */
+ ovs_be32 xid;
+};
+
+int ofputil_decode_flow_update(struct ofputil_flow_update *,
+ struct ofpbuf *msg, struct ofpbuf *ofpacts);
+void ofputil_start_flow_update(struct list *replies);
+void ofputil_append_flow_update(const struct ofputil_flow_update *,
+ struct list *replies);
+
+/* Abstract nx_flow_monitor_cancel. */
+uint32_t ofputil_decode_flow_monitor_cancel(const struct ofp_header *);
+struct ofpbuf *ofputil_encode_flow_monitor_cancel(uint32_t id);
+
+/* Encoding OpenFlow stats messages. */
+void ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version,
+ const struct ofputil_phy_port *pp,
+ struct list *replies);
+
+/* Encoding simple OpenFlow messages. */
+struct ofpbuf *make_echo_request(enum ofp_version);
struct ofpbuf *make_echo_reply(const struct ofp_header *rq);
-struct ofpbuf *ofputil_encode_barrier_request(void);
+struct ofpbuf *ofputil_encode_barrier_request(enum ofp_version);
const char *ofputil_frag_handling_to_string(enum ofp_config_flags);
bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *);
+
\f
/* Actions. */
*
* (The above list helps developers who want to "grep" for these definitions.)
*/
-enum ofputil_action_code {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME) OFPUTIL_##ENUM,
+enum OVS_PACKED_ENUM ofputil_action_code {
+ OFPUTIL_ACTION_INVALID,
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) OFPUTIL_##ENUM,
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) OFPUTIL_##ENUM,
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM,
#include "ofp-util.def"
};
/* The number of values of "enum ofputil_action_code". */
enum {
-#define OFPAT10_ACTION(ENUM, STRUCT, NAME) + 1
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) + 1
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) + 1
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) + 1
- OFPUTIL_N_ACTIONS = 0
+ OFPUTIL_N_ACTIONS = 1
#include "ofp-util.def"
};
-int ofputil_decode_action(const union ofp_action *);
-enum ofputil_action_code ofputil_decode_action_unsafe(
- const union ofp_action *);
-
int ofputil_action_code_from_name(const char *);
void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
void ofputil_init_##ENUM(struct STRUCT *); \
struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
+#define OFPAT11_ACTION(ENUM, STRUCT, NAME) \
+ void ofputil_init_##ENUM(struct STRUCT *); \
+ struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
void ofputil_init_##ENUM(struct STRUCT *); \
struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *);
#define OFP_ACTION_ALIGN 8 /* Alignment of ofp_actions. */
-static inline union ofp_action *
-ofputil_action_next(const union ofp_action *a)
-{
- return ((union ofp_action *) (void *)
- ((uint8_t *) a + ntohs(a->header.len)));
-}
-
-static inline bool
-ofputil_action_is_valid(const union ofp_action *a, size_t n_actions)
-{
- uint16_t len = ntohs(a->header.len);
- return (!(len % OFP_ACTION_ALIGN)
- && len >= sizeof *a
- && len / sizeof *a <= n_actions);
-}
-
-/* This macro is careful to check for actions with bad lengths. */
-#define OFPUTIL_ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS) \
- for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS); \
- (LEFT) > 0 && ofputil_action_is_valid(ITER, LEFT); \
- ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
- (ITER) = ofputil_action_next(ITER)))
-
-/* This macro does not check for actions with bad lengths. It should only be
- * used with actions from trusted sources or with actions that have already
- * been validated (e.g. with OFPUTIL_ACTION_FOR_EACH). */
-#define OFPUTIL_ACTION_FOR_EACH_UNSAFE(ITER, LEFT, ACTIONS, N_ACTIONS) \
- for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS); \
- (LEFT) > 0; \
- ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
- (ITER) = ofputil_action_next(ITER)))
-
enum ofperr validate_actions(const union ofp_action *, size_t n_actions,
const struct flow *, int max_ports);
bool action_outputs_to_port(const union ofp_action *, ovs_be16 port);
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void
ofpbuf_use_const(struct ofpbuf *b, const void *data, size_t size)
{
- ofpbuf_use__(b, (void *) data, size, OFPBUF_STACK);
+ ofpbuf_use__(b, CONST_CAST(void *, data), size, OFPBUF_STACK);
b->size = size;
}
if (n) {
*n = b->size - initial_size;
}
- return (char *) s;
+ return CONST_CAST(char *, s);
}
ofpbuf_put(b, &byte, 1);
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "ovsdb-parser.h"
#include "json.h"
#include "shash.h"
+#include "smap.h"
#include "sort.h"
#include "unicode.h"
d = &default_data[kt][vt];
if (!d->n) {
d->n = 1;
- d->keys = (union ovsdb_atom *) ovsdb_atom_default(kt);
+ d->keys = CONST_CAST(union ovsdb_atom *, ovsdb_atom_default(kt));
if (vt != OVSDB_TYPE_VOID) {
- d->values = (union ovsdb_atom *) ovsdb_atom_default(vt);
+ d->values = CONST_CAST(union ovsdb_atom *,
+ ovsdb_atom_default(vt));
}
}
return d;
}
/* Initializes 'datum' as a string-to-string map whose contents are taken from
- * 'sh'. Destroys 'sh'. */
+ * 'smap'. Destroys 'smap'. */
void
-ovsdb_datum_from_shash(struct ovsdb_datum *datum, struct shash *sh)
+ovsdb_datum_from_smap(struct ovsdb_datum *datum, struct smap *smap)
{
- struct shash_node *node, *next;
+ struct smap_node *node, *next;
size_t i;
- datum->n = shash_count(sh);
+ datum->n = smap_count(smap);
datum->keys = xmalloc(datum->n * sizeof *datum->keys);
datum->values = xmalloc(datum->n * sizeof *datum->values);
i = 0;
- SHASH_FOR_EACH_SAFE (node, next, sh) {
- datum->keys[i].string = node->name;
- datum->values[i].string = node->data;
- shash_steal(sh, node);
+ SMAP_FOR_EACH_SAFE (node, next, smap) {
+ smap_steal(smap, node,
+ &datum->keys[i].string, &datum->values[i].string);
i++;
}
assert(i == datum->n);
- shash_destroy(sh);
+ smap_destroy(smap);
ovsdb_datum_sort_unique(datum, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING);
}
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ds;
struct ovsdb_symbol_table;
+struct smap;
/* One value of an atomic type (given by enum ovs_atomic_type). */
union ovsdb_atom {
void ovsdb_datum_to_bare(const struct ovsdb_datum *,
const struct ovsdb_type *, struct ds *);
-void ovsdb_datum_from_shash(struct ovsdb_datum *, struct shash *);
+void ovsdb_datum_from_smap(struct ovsdb_datum *, struct smap *);
/* Comparison. */
uint32_t ovsdb_datum_hash(const struct ovsdb_datum *,
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks.
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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 {
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
json_destroy(idl->monitor_request_id);
free(idl->lock_name);
json_destroy(idl->lock_request_id);
+ hmap_destroy(&idl->outstanding_txns);
free(idl);
}
}
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);
const struct ovsdb_idl_column *column,
struct ovsdb_datum *datum)
{
- struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_;
+ struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
const struct ovsdb_idl_table_class *class;
size_t column_idx;
ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_,
const struct ovsdb_idl_column *column)
{
- struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_;
+ struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
const struct ovsdb_idl_table_class *class;
size_t column_idx;
void
ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_)
{
- struct ovsdb_idl_row *row = (struct ovsdb_idl_row *) row_;
+ struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
if (ovsdb_idl_row_is_synthetic(row)) {
return;
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2011 Nicira Networks
+/* Copyright (c) 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "byte-order.h"
#include "csum.h"
#include "flow.h"
+#include "hmap.h"
#include "dynamic-string.h"
#include "ofpbuf.h"
return *dpidp != 0;
}
+/* Returns true if 'ea' is a reserved address, that a bridge must never
+ * forward, false otherwise.
+ *
+ * If you change this function's behavior, please update corresponding
+ * documentation in vswitch.xml at the same time. */
+bool
+eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN])
+{
+ struct eth_addr_node {
+ struct hmap_node hmap_node;
+ uint64_t ea64;
+ };
+
+ static struct eth_addr_node nodes[] = {
+ /* STP, IEEE pause frames, and other reserved protocols. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000000ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000001ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000002ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000003ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000004ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000005ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000006ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000007ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000008ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000009ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000aULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000bULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000cULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000dULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000eULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000fULL },
+
+ /* Extreme protocols. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000000ULL }, /* EDP. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000004ULL }, /* EAPS. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000006ULL }, /* EAPS. */
+
+ /* Cisco protocols. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000c000000ULL }, /* ISL. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccccULL }, /* PAgP, UDLD, CDP,
+ * DTP, VTP. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000ccccccdULL }, /* PVST+. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000ccdcdcdULL }, /* STP Uplink Fast,
+ * FlexLink. */
+
+ /* Cisco CFM. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc0ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc1ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc2ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc3ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc4ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc5ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc6ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc7ULL },
+ };
+
+ static struct hmap addrs = HMAP_INITIALIZER(&addrs);
+ struct eth_addr_node *node;
+ uint64_t ea64;
+
+ if (hmap_is_empty(&addrs)) {
+ for (node = nodes; node < &nodes[ARRAY_SIZE(nodes)]; node++) {
+ hmap_insert(&addrs, &node->hmap_node,
+ hash_2words(node->ea64, node->ea64 >> 32));
+ }
+ }
+
+ ea64 = eth_addr_to_uint64(ea);
+ HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash_2words(ea64, ea64 >> 32),
+ &addrs) {
+ if (node->ea64 == ea64) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool
eth_addr_from_string(const char *s, uint8_t ea[ETH_ADDR_LEN])
{
}
}
-/* Fills 'b' with an 802.2 SNAP packet with Ethernet source address 'eth_src',
- * the Nicira OUI as SNAP organization and 'snap_type' as SNAP type. The text
- * string in 'tag' is enclosed as the packet payload.
- *
+/* Fills 'b' with a Reverse ARP packet with Ethernet source address 'eth_src'.
* This function is used by Open vSwitch to compose packets in cases where
- * context is important but content doesn't (or shouldn't) matter. For this
- * purpose, 'snap_type' should be a random number and 'tag' should be an
- * English phrase that explains the purpose of the packet. (The English phrase
- * gives hapless admins running Wireshark the opportunity to figure out what's
- * going on.) */
+ * context is important but content doesn't (or shouldn't) matter.
+ *
+ * The returned packet has enough headroom to insert an 802.1Q VLAN header if
+ * desired. */
void
-compose_benign_packet(struct ofpbuf *b, const char *tag, uint16_t snap_type,
- const uint8_t eth_src[ETH_ADDR_LEN])
+compose_rarp(struct ofpbuf *b, const uint8_t eth_src[ETH_ADDR_LEN])
{
- size_t tag_size = strlen(tag) + 1;
- char *payload;
+ struct eth_header *eth;
+ struct arp_eth_header *arp;
- payload = snap_compose(b, eth_addr_broadcast, eth_src, 0x002320, snap_type,
- tag_size + ETH_ADDR_LEN);
- memcpy(payload, tag, tag_size);
- memcpy(payload + tag_size, eth_src, ETH_ADDR_LEN);
+ ofpbuf_clear(b);
+ ofpbuf_prealloc_tailroom(b, ETH_HEADER_LEN + VLAN_HEADER_LEN
+ + ARP_ETH_HEADER_LEN);
+ ofpbuf_reserve(b, VLAN_HEADER_LEN);
+ eth = ofpbuf_put_uninit(b, sizeof *eth);
+ memcpy(eth->eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
+ memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
+ eth->eth_type = htons(ETH_TYPE_RARP);
+
+ arp = ofpbuf_put_uninit(b, sizeof *arp);
+ arp->ar_hrd = htons(ARP_HRD_ETHERNET);
+ arp->ar_pro = htons(ARP_PRO_IP);
+ arp->ar_hln = sizeof arp->ar_sha;
+ arp->ar_pln = sizeof arp->ar_spa;
+ arp->ar_op = htons(ARP_OP_RARP);
+ memcpy(arp->ar_sha, eth_src, ETH_ADDR_LEN);
+ arp->ar_spa = htonl(0);
+ memcpy(arp->ar_tha, eth_src, ETH_ADDR_LEN);
+ arp->ar_tpa = htonl(0);
}
/* Insert VLAN header according to given TCI. Packet passed must be Ethernet
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));
}
/* 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)
{
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;
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)
{
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
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])
{
{
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)
/* 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:
*
#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. */
#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
#define ARP_PRO_IP 0x0800
#define ARP_OP_REQUEST 1
#define ARP_OP_REPLY 2
+#define ARP_OP_RARP 3
#define ARP_ETH_HEADER_LEN 28
struct arp_eth_header {
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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;
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Create notification pipe. */
xpipe(fds);
- set_nonblocking(fds[0]);
- set_nonblocking(fds[1]);
+ xset_nonblocking(fds[0]);
+ xset_nonblocking(fds[1]);
/* Set up child termination signal handler. */
memset(&sa, 0, sizeof sa);
/*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdlib.h>
#include <string.h>
#include "coverage.h"
+#include "ofp-msgs.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
int backoff;
int max_backoff;
time_t backoff_deadline;
- time_t last_received;
time_t last_connected;
time_t last_disconnected;
unsigned int packets_sent;
time_t creation_time;
unsigned long int total_time_connected;
- /* Throughout this file, "probe" is shorthand for "inactivity probe".
- * When nothing has been received from the peer for a while, we send out
- * an echo request as an inactivity probe packet. We should receive back
- * a response. */
+ /* Throughout this file, "probe" is shorthand for "inactivity probe". When
+ * no activity has been observed from the peer for a while, we send out an
+ * echo request as an inactivity probe packet. We should receive back a
+ * response.
+ *
+ * "Activity" is defined as either receiving an OpenFlow message from the
+ * peer or successfully sending a message that had been in 'txq'. */
int probe_interval; /* Secs of inactivity before sending probe. */
+ time_t last_activity; /* Last time we saw some activity. */
/* When we create a vconn we obtain these values, to save them past the end
* of the vconn's lifetime. Otherwise, in-band control will only allow
rc->backoff = 0;
rc->max_backoff = max_backoff ? max_backoff : 8;
rc->backoff_deadline = TIME_MIN;
- rc->last_received = time_now();
rc->last_connected = TIME_MIN;
rc->last_disconnected = TIME_MIN;
rc->seqno = 0;
rc->creation_time = time_now();
rc->total_time_connected = 0;
+ rc->last_activity = time_now();
+
rconn_set_probe_interval(rc, probe_interval);
rconn_set_dscp(rc, dscp);
rc->dscp = dscp;
}
+uint8_t
+rconn_get_dscp(const struct rconn *rc)
+{
+ return rc->dscp;
+}
+
void
rconn_set_probe_interval(struct rconn *rc, int probe_interval)
{
if (error) {
break;
}
+ rc->last_activity = time_now();
}
if (list_is_empty(&rc->txq)) {
poll_immediate_wake();
timeout_ACTIVE(const struct rconn *rc)
{
if (rc->probe_interval) {
- unsigned int base = MAX(rc->last_received, rc->state_entered);
+ unsigned int base = MAX(rc->last_activity, rc->state_entered);
unsigned int arg = base + rc->probe_interval - rc->state_entered;
return arg;
}
run_ACTIVE(struct rconn *rc)
{
if (timed_out(rc)) {
- unsigned int base = MAX(rc->last_received, rc->state_entered);
+ unsigned int base = MAX(rc->last_activity, rc->state_entered);
+ int version;
+
VLOG_DBG("%s: idle %u seconds, sending inactivity probe",
rc->name, (unsigned int) (time_now() - base));
+ version = rconn_get_version(rc);
+ assert(version >= 0 && version <= 0xff);
+
/* Ordering is important here: rconn_send() can transition to BACKOFF,
* and we don't want to transition back to IDLE if so, because then we
* can end up queuing a packet with vconn == NULL and then *boom*. */
state_transition(rc, S_IDLE);
- rconn_send(rc, make_echo_request(), NULL);
+ rconn_send(rc, make_echo_request(version), NULL);
return;
}
rc->probably_admitted = true;
rc->last_admitted = time_now();
}
- rc->last_received = time_now();
+ rc->last_activity = time_now();
rc->packets_received++;
if (rc->state == S_IDLE) {
state_transition(rc, S_ACTIVE);
copy_to_monitor(rc, b);
b->private_p = counter;
if (counter) {
- rconn_packet_counter_inc(counter);
+ rconn_packet_counter_inc(counter, b->size);
}
list_push_back(&rc->txq, &b->list_node);
struct rconn_packet_counter *counter, int queue_limit)
{
int retval;
- retval = counter->n >= queue_limit ? EAGAIN : rconn_send(rc, b, counter);
+ retval = (counter->n_packets >= queue_limit
+ ? EAGAIN
+ : rconn_send(rc, b, counter));
if (retval) {
COVERAGE_INC(rconn_overflow);
}
return state_name(rc->state);
}
-/* Returns the number of connection attempts made by 'rc', including any
- * ongoing attempt that has not yet succeeded or failed. */
-unsigned int
-rconn_get_attempted_connections(const struct rconn *rc)
-{
- return rc->n_attempted_connections;
-}
-
-/* Returns the number of successful connection attempts made by 'rc'. */
-unsigned int
-rconn_get_successful_connections(const struct rconn *rc)
-{
- return rc->n_successful_connections;
-}
-
/* Returns the time at which the last successful connection was made by
* 'rc'. Returns TIME_MIN if never connected. */
time_t
return rc->last_disconnected;
}
-/* Returns the time at which the last OpenFlow message was received by 'rc'.
- * If no packets have been received on 'rc', returns the time at which 'rc'
- * was created. */
-time_t
-rconn_get_last_received(const struct rconn *rc)
-{
- return rc->last_received;
-}
-
-/* Returns the time at which 'rc' was created. */
-time_t
-rconn_get_creation_time(const struct rconn *rc)
-{
- return rc->creation_time;
-}
-
-/* Returns the approximate number of seconds that 'rc' has been connected. */
-unsigned long int
-rconn_get_total_time_connected(const struct rconn *rc)
-{
- return (rc->total_time_connected
- + (rconn_is_connected(rc) ? elapsed_in_this_state(rc) : 0));
-}
-
-/* Returns the current amount of backoff, in seconds. This is the amount of
- * time after which the rconn will transition from BACKOFF to CONNECTING. */
-int
-rconn_get_backoff(const struct rconn *rc)
-{
- return rc->backoff;
-}
-
-/* Returns the number of seconds spent in this state so far. */
-unsigned int
-rconn_get_state_elapsed(const struct rconn *rc)
-{
- return elapsed_in_this_state(rc);
-}
-
/* Returns 'rc''s current connection sequence number, a number that changes
* every time that 'rconn' connects or disconnects. */
unsigned int
{
return rc->last_error;
}
+
+/* Returns the number of messages queued for transmission on 'rc'. */
+unsigned int
+rconn_count_txqlen(const struct rconn *rc)
+{
+ return list_size(&rc->txq);
+}
\f
struct rconn_packet_counter *
rconn_packet_counter_create(void)
{
- struct rconn_packet_counter *c = xmalloc(sizeof *c);
- c->n = 0;
+ struct rconn_packet_counter *c = xzalloc(sizeof *c);
c->ref_cnt = 1;
return c;
}
{
if (c) {
assert(c->ref_cnt > 0);
- if (!--c->ref_cnt && !c->n) {
+ if (!--c->ref_cnt && !c->n_packets) {
free(c);
}
}
}
void
-rconn_packet_counter_inc(struct rconn_packet_counter *c)
+rconn_packet_counter_inc(struct rconn_packet_counter *c, unsigned int n_bytes)
{
- c->n++;
+ c->n_packets++;
+ c->n_bytes += n_bytes;
}
void
-rconn_packet_counter_dec(struct rconn_packet_counter *c)
+rconn_packet_counter_dec(struct rconn_packet_counter *c, unsigned int n_bytes)
{
- assert(c->n > 0);
- if (!--c->n && !c->ref_cnt) {
- free(c);
+ assert(c->n_packets > 0);
+ assert(c->n_bytes >= n_bytes);
+
+ c->n_bytes -= n_bytes;
+ c->n_packets--;
+ if (!c->n_packets) {
+ assert(!c->n_bytes);
+ if (!c->ref_cnt) {
+ free(c);
+ }
}
}
\f
try_send(struct rconn *rc)
{
struct ofpbuf *msg = ofpbuf_from_list(rc->txq.next);
+ unsigned int n_bytes = msg->size;
struct rconn_packet_counter *counter = msg->private_p;
int retval;
COVERAGE_INC(rconn_sent);
rc->packets_sent++;
if (counter) {
- rconn_packet_counter_dec(counter);
+ rconn_packet_counter_dec(counter, n_bytes);
}
return 0;
}
struct ofpbuf *b = ofpbuf_from_list(list_pop_front(&rc->txq));
struct rconn_packet_counter *counter = b->private_p;
if (counter) {
- rconn_packet_counter_dec(counter);
+ rconn_packet_counter_dec(counter, b->size);
}
COVERAGE_INC(rconn_discarded);
ofpbuf_delete(b);
static bool
is_admitted_msg(const struct ofpbuf *b)
{
- struct ofp_header *oh = b->data;
- uint8_t type = oh->type;
- return !(type < 32
- && (1u << type) & ((1u << OFPT_HELLO) |
- (1u << OFPT_ERROR) |
- (1u << OFPT_ECHO_REQUEST) |
- (1u << OFPT_ECHO_REPLY) |
- (1u << OFPT_VENDOR) |
- (1u << OFPT_FEATURES_REQUEST) |
- (1u << OFPT_FEATURES_REPLY) |
- (1u << OFPT_GET_CONFIG_REQUEST) |
- (1u << OFPT_GET_CONFIG_REPLY) |
- (1u << OFPT_SET_CONFIG)));
+ enum ofptype type;
+ enum ofperr error;
+
+ error = ofptype_decode(&type, b->data);
+ if (error) {
+ return false;
+ }
+
+ switch (type) {
+ case OFPTYPE_HELLO:
+ case OFPTYPE_ERROR:
+ case OFPTYPE_ECHO_REQUEST:
+ case OFPTYPE_ECHO_REPLY:
+ case OFPTYPE_FEATURES_REQUEST:
+ case OFPTYPE_FEATURES_REPLY:
+ case OFPTYPE_GET_CONFIG_REQUEST:
+ case OFPTYPE_GET_CONFIG_REPLY:
+ case OFPTYPE_SET_CONFIG:
+ return false;
+
+ case OFPTYPE_PACKET_IN:
+ case OFPTYPE_FLOW_REMOVED:
+ case OFPTYPE_PORT_STATUS:
+ case OFPTYPE_PACKET_OUT:
+ case OFPTYPE_FLOW_MOD:
+ case OFPTYPE_PORT_MOD:
+ case OFPTYPE_BARRIER_REQUEST:
+ case OFPTYPE_BARRIER_REPLY:
+ case OFPTYPE_DESC_STATS_REQUEST:
+ case OFPTYPE_DESC_STATS_REPLY:
+ case OFPTYPE_FLOW_STATS_REQUEST:
+ case OFPTYPE_FLOW_STATS_REPLY:
+ case OFPTYPE_AGGREGATE_STATS_REQUEST:
+ case OFPTYPE_AGGREGATE_STATS_REPLY:
+ case OFPTYPE_TABLE_STATS_REQUEST:
+ case OFPTYPE_TABLE_STATS_REPLY:
+ case OFPTYPE_PORT_STATS_REQUEST:
+ case OFPTYPE_PORT_STATS_REPLY:
+ case OFPTYPE_QUEUE_STATS_REQUEST:
+ case OFPTYPE_QUEUE_STATS_REPLY:
+ case OFPTYPE_PORT_DESC_STATS_REQUEST:
+ case OFPTYPE_PORT_DESC_STATS_REPLY:
+ case OFPTYPE_ROLE_REQUEST:
+ case OFPTYPE_ROLE_REPLY:
+ case OFPTYPE_SET_FLOW_FORMAT:
+ case OFPTYPE_FLOW_MOD_TABLE_ID:
+ case OFPTYPE_SET_PACKET_IN_FORMAT:
+ case OFPTYPE_FLOW_AGE:
+ case OFPTYPE_SET_ASYNC_CONFIG:
+ case OFPTYPE_SET_CONTROLLER_ID:
+ case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+ case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
+ case OFPTYPE_FLOW_MONITOR_CANCEL:
+ case OFPTYPE_FLOW_MONITOR_PAUSED:
+ case OFPTYPE_FLOW_MONITOR_RESUMED:
+ default:
+ return true;
+ }
}
/* Returns true if 'rc' is currently logging information about connection
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct rconn *rconn_create(int inactivity_probe_interval,
int max_backoff, uint8_t dscp);
void rconn_set_dscp(struct rconn *rc, uint8_t dscp);
+uint8_t rconn_get_dscp(const struct rconn *rc);
void rconn_set_max_backoff(struct rconn *, int max_backoff);
int rconn_get_max_backoff(const struct rconn *);
void rconn_set_probe_interval(struct rconn *, int inactivity_probe_interval);
int rconn_get_version(const struct rconn *);
const char *rconn_get_state(const struct rconn *);
-unsigned int rconn_get_attempted_connections(const struct rconn *);
-unsigned int rconn_get_successful_connections(const struct rconn *);
time_t rconn_get_last_connection(const struct rconn *);
time_t rconn_get_last_disconnect(const struct rconn *);
-time_t rconn_get_last_received(const struct rconn *);
-time_t rconn_get_creation_time(const struct rconn *);
-unsigned long int rconn_get_total_time_connected(const struct rconn *);
-int rconn_get_backoff(const struct rconn *);
-unsigned int rconn_get_state_elapsed(const struct rconn *);
unsigned int rconn_get_connection_seqno(const struct rconn *);
int rconn_get_last_error(const struct rconn *);
+unsigned int rconn_count_txqlen(const struct rconn *);
-/* Counts the number of packets queued into an rconn by a given source. */
+/* Counts packets and bytes queued into an rconn by a given source. */
struct rconn_packet_counter {
- int n; /* Number of packets queued. */
+ unsigned int n_packets; /* Number of packets queued. */
+ unsigned int n_bytes; /* Number of bytes queued. */
int ref_cnt; /* Number of owners. */
};
struct rconn_packet_counter *rconn_packet_counter_create(void);
void rconn_packet_counter_destroy(struct rconn_packet_counter *);
-void rconn_packet_counter_inc(struct rconn_packet_counter *);
-void rconn_packet_counter_dec(struct rconn_packet_counter *);
-
-static inline int
-rconn_packet_counter_read(const struct rconn_packet_counter *counter)
-{
- return counter->n;
-}
+void rconn_packet_counter_inc(struct rconn_packet_counter *, unsigned n_bytes);
+void rconn_packet_counter_dec(struct rconn_packet_counter *, unsigned n_bytes);
#endif /* rconn.h */
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
--- /dev/null
+/*
+ * Copyright (c) 2012 Ed Maste. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "route-table.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(route_table);
+
+static int pid;
+static unsigned int register_count = 0;
+
+bool
+route_table_get_name(ovs_be32 ip, char name[IFNAMSIZ])
+{
+ struct {
+ struct rt_msghdr rtm;
+ char space[512];
+ } rtmsg;
+
+ struct rt_msghdr *rtm = &rtmsg.rtm;
+ struct sockaddr_dl *ifp = NULL;
+ struct sockaddr_in *sin;
+ struct sockaddr *sa;
+ static int seq;
+ int i, len, namelen, rtsock;
+
+ rtsock = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (rtsock < 0)
+ return false;
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+
+ rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_type = RTM_GET;
+ rtm->rtm_addrs = RTA_DST | RTA_IFP;
+ rtm->rtm_seq = ++seq;
+
+ sin = (struct sockaddr_in *)(rtm + 1);
+ sin->sin_len = len = sizeof(struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = ip;
+
+ if ((write(rtsock, (char *)&rtmsg, rtm->rtm_msglen)) < 0) {
+ close(rtsock);
+ return false;
+ }
+
+ do {
+ len = read(rtsock, (char *)&rtmsg, sizeof(rtmsg));
+ } while (len > 0 && (rtmsg.rtm.rtm_seq != seq ||
+ rtmsg.rtm.rtm_pid != pid));
+
+ close(rtsock);
+
+ if (len < 0) {
+ return false;
+ }
+
+ sa = (struct sockaddr *)(rtm + 1);
+ for (i = 1; i; i <<= 1) {
+ if (rtm->rtm_addrs & i) {
+ if (i == RTA_IFP && sa->sa_family == AF_LINK &&
+ ((struct sockaddr_dl *)sa)->sdl_nlen) {
+ ifp = (struct sockaddr_dl *)sa;
+ namelen = ifp->sdl_nlen;
+ if (namelen > IFNAMSIZ - 1)
+ namelen = IFNAMSIZ - 1;
+ memcpy(name, ifp->sdl_data, namelen);
+ name[namelen] = '\0';
+ return true;
+ }
+ sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa));
+ }
+ }
+ return false;
+}
+
+void
+route_table_register(void)
+{
+ if (!register_count)
+ {
+ pid = getpid();
+ }
+
+ register_count++;
+}
+
+void
+route_table_unregister(void)
+{
+ register_count--;
+}
+
+void
+route_table_run(void)
+{
+}
+
+void
+route_table_wait(void)
+{
+}
/*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#ifndef ROUTE_TABLE_H
#define ROUTE_TABLE_H 1
+#include <sys/socket.h>
#include <net/if.h>
#include <stdbool.h>
#include <stdint.h>
--- /dev/null
+/*
+ * Copyright (c) 2011 Gaetano Catalli.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <poll.h>
+
+#include "coverage.h"
+#include "socket-util.h"
+#include "poll-loop.h"
+#include "vlog.h"
+#include "rtbsd.h"
+
+VLOG_DEFINE_THIS_MODULE(rtbsd);
+COVERAGE_DEFINE(rtbsd_changed);
+
+/* PF_ROUTE socket. */
+static int notify_sock = -1;
+
+/* All registered notifiers. */
+static struct list all_notifiers = LIST_INITIALIZER(&all_notifiers);
+
+static void rtbsd_report_change(const struct if_msghdr *);
+static void rtbsd_report_notify_error(void);
+
+/* Registers 'cb' to be called with auxiliary data 'aux' with network device
+ * change notifications. The notifier is stored in 'notifier', which the
+ * caller must not modify or free.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+rtbsd_notifier_register(struct rtbsd_notifier *notifier,
+ rtbsd_notify_func *cb, void *aux)
+{
+ if (notify_sock < 0) {
+ int error;
+ notify_sock = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (notify_sock < 0) {
+ VLOG_WARN("could not create PF_ROUTE socket: %s",
+ strerror(errno));
+ return errno;
+ }
+ error = set_nonblocking(notify_sock);
+ if (error) {
+ VLOG_WARN("error set_nonblocking PF_ROUTE socket: %s",
+ strerror(error));
+ return error;
+ }
+ } else {
+ /* Catch up on notification work so that the new notifier won't
+ * receive any stale notifications. XXX*/
+ rtbsd_notifier_run();
+ }
+
+ list_push_back(&all_notifiers, ¬ifier->node);
+ notifier->cb = cb;
+ notifier->aux = aux;
+ return 0;
+}
+
+/* Cancels notification on 'notifier', which must have previously been
+ * registered with rtbsd_notifier_register(). */
+void
+rtbsd_notifier_unregister(struct rtbsd_notifier *notifier)
+{
+ list_remove(¬ifier->node);
+ if (list_is_empty(&all_notifiers)) {
+ close(notify_sock);
+ notify_sock = -1;
+ }
+}
+
+/* Calls all of the registered notifiers, passing along any as-yet-unreported
+ * netdev change events. */
+void
+rtbsd_notifier_run(void)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ struct if_msghdr msg;
+ if (notify_sock < 0) {
+ return;
+ }
+
+ for (;;) {
+ int retval;
+
+ msg.ifm_type = RTM_IFINFO;
+ msg.ifm_version = RTM_VERSION; //XXX check if necessary
+
+ /* read from PF_ROUTE socket */
+ retval = read(notify_sock, (char *)&msg, sizeof(msg));
+ if (retval >= 0) {
+ /* received packet from PF_ROUTE socket
+ * XXX check for bad packets */
+ if (msg.ifm_type == RTM_IFINFO) {
+ rtbsd_report_change(&msg);
+ }
+ } else if (errno == EAGAIN) {
+ return;
+ } else {
+ if (errno == ENOBUFS) {
+ VLOG_WARN_RL(&rl, "PF_ROUTE receive buffer overflowed");
+ } else {
+ VLOG_WARN_RL(&rl, "error reading PF_ROUTE socket: %s",
+ strerror(errno));
+ }
+ rtbsd_report_notify_error();
+ }
+ }
+}
+
+/* Causes poll_block() to wake up when network device change notifications are
+ * ready. */
+void
+rtbsd_notifier_wait(void)
+{
+ if (notify_sock >= 0) {
+ poll_fd_wait(notify_sock, POLLIN);
+ }
+}
+
+static void
+rtbsd_report_change(const struct if_msghdr *msg)
+{
+ struct rtbsd_notifier *notifier;
+ struct rtbsd_change change;
+
+ COVERAGE_INC(rtbsd_changed);
+
+ change.msg_type = msg->ifm_type; //XXX
+ change.if_index = msg->ifm_index;
+ if_indextoname(msg->ifm_index, change.if_name);
+ change.master_ifindex = 0; //XXX
+
+ LIST_FOR_EACH (notifier, node, &all_notifiers) {
+ notifier->cb(&change, notifier->aux);
+ }
+}
+
+/* If an error occurs the notifiers' callbacks are called with NULL changes */
+static void
+rtbsd_report_notify_error(void)
+{
+ struct rtbsd_notifier *notifier;
+
+ LIST_FOR_EACH (notifier, node, &all_notifiers) {
+ notifier->cb(NULL, notifier->aux);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 Gaetano Catalli.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RTBSD_H
+#define RTBSD_H 1
+
+#include "list.h"
+
+/*
+ * A digested version of a message received from a PF_ROUTE socket which
+ * indicates that a network device has been created or destroyed or changed.
+ */
+struct rtbsd_change {
+ /* Copied from struct if_msghdr. */
+ int msg_type; /* e.g. XXX. */
+
+ /* Copied from struct if_msghdr. */
+ int if_index; /* Index of network device. */
+
+ char if_name[IF_NAMESIZE]; /* Name of network device. */
+ int master_ifindex; /* Ifindex of datapath master (0 if none). */
+};
+
+/*
+ * Function called to report that a netdev has changed. 'change' describes the
+ * specific change. It may be null if the buffer of change information
+ * overflowed, in which case the function must assume that every device may
+ * have changed. 'aux' is as specified in the call to
+ * rtbsd_notifier_register().
+ */
+typedef void rtbsd_notify_func(const struct rtbsd_change *, void *aux);
+
+struct rtbsd_notifier {
+ struct list node;
+ rtbsd_notify_func *cb;
+ void *aux;
+};
+
+int rtbsd_notifier_register(struct rtbsd_notifier *,
+ rtbsd_notify_func *, void *aux);
+void rtbsd_notifier_unregister(struct rtbsd_notifier *);
+void rtbsd_notifier_run(void);
+void rtbsd_notifier_wait(void);
+
+#endif /* rtbsd.h */
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008 Nicira Networks.
+ * Copyright (c) 2008, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return x >= y ? x - y : 0;
}
-/* Saturating multiplication: overflow yields UINT_MAX. */
+/* Saturating multiplication of "unsigned int"s: overflow yields UINT_MAX. */
+#define SAT_MUL(X, Y) \
+ ((Y) == 0 ? 0 \
+ : (X) <= UINT_MAX / (Y) ? (unsigned int) (X) * (unsigned int) (Y) \
+ : UINT_MAX)
static inline unsigned int
sat_mul(unsigned int x, unsigned int y)
{
- return (!y ? 0
- : x <= UINT_MAX / y ? x * y
- : UINT_MAX);
+ return SAT_MUL(x, y);
}
#endif /* sat-math.h */
/*
* This file is from the Apache Portable Runtime Library.
* The full upstream copyright and license statement is included below.
- * Modifications copyright (c) 2009, 2010 Nicira Networks.
+ * Modifications copyright (c) 2009, 2010 Nicira, Inc.
*/
/* Licensed to the Apache Software Foundation (ASF) under one or more
/*
* This file is from the Apache Portable Runtime Library.
* The full upstream copyright and license statement is included below.
- * Modifications copyright (c) 2009 Nicira Networks.
+ * Modifications copyright (c) 2009 Nicira, Inc.
*/
/* Licensed to the Apache Software Foundation (ASF) under one or more
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
struct shash_node *node = xmalloc(sizeof *node);
node->name = name;
- node->data = (void *) data;
+ node->data = CONST_CAST(void *, data);
hmap_insert(&sh->map, &node->node, hash);
return node;
}
return NULL;
} else {
void *old_data = node->data;
- node->data = (void *) data;
+ node->data = CONST_CAST(void *, data);
return old_data;
}
}
{
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));
-}
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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
/*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
if (!inited) {
inited = true;
xpipe(fds);
- set_nonblocking(fds[0]);
- set_nonblocking(fds[1]);
+ xset_nonblocking(fds[0]);
+ xset_nonblocking(fds[1]);
}
}
/*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/* Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#include <config.h>
+#include "smap.h"
+
+#include <assert.h>
+
+#include "hash.h"
+#include "json.h"
+
+static struct smap_node *smap_add__(struct smap *, char *, void *,
+ size_t hash);
+static struct smap_node *smap_find__(const struct smap *, const char *key,
+ size_t key_len, size_t hash);
+static int compare_nodes_by_key(const void *, const void *);
+\f
+/* Public Functions. */
+
+void
+smap_init(struct smap *smap)
+{
+ hmap_init(&smap->map);
+}
+
+void
+smap_destroy(struct smap *smap)
+{
+ if (smap) {
+ smap_clear(smap);
+ hmap_destroy(&smap->map);
+ }
+}
+
+/* Adds 'key' paired with 'value' to 'smap'. It is the caller's responsibility
+ * to avoid duplicate keys if desirable. */
+struct smap_node *
+smap_add(struct smap *smap, const char *key, const char *value)
+{
+ size_t key_len = strlen(key);
+ return smap_add__(smap, xmemdup0(key, key_len), xstrdup(value),
+ hash_bytes(key, key_len, 0));
+}
+
+/* Attempts to add 'key' to 'smap' associated with 'value'. If 'key' already
+ * exists in 'smap', does nothing and returns false. Otherwise, performs the
+ * addition and returns true. */
+bool
+smap_add_once(struct smap *smap, const char *key, const char *value)
+{
+ if (!smap_get(smap, key)) {
+ smap_add(smap, key, value);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/* Adds 'key' paired with a value derived from 'format' (similar to printf).
+ * It is the caller's responsibility to avoid duplicate keys if desirable. */
+void
+smap_add_format(struct smap *smap, const char *key, const char *format, ...)
+{
+ size_t key_len;
+ va_list args;
+ char *value;
+
+ va_start(args, format);
+ value = xvasprintf(format, args);
+ va_end(args);
+
+ key_len = strlen(key);
+ smap_add__(smap, xmemdup0(key, key_len), value,
+ hash_bytes(key, key_len, 0));
+}
+
+/* Searches for 'key' in 'smap'. If it does not already exists, adds it.
+ * Otherwise, changes its value to 'value'. */
+void
+smap_replace(struct smap *smap, const char *key, const char *value)
+{
+ size_t key_len = strlen(key);
+ size_t hash = hash_bytes(key, key_len, 0);
+
+ struct smap_node *node;
+
+ node = smap_find__(smap, key, key_len, hash);
+ if (node) {
+ free(node->value);
+ node->value = xstrdup(value);
+ } else {
+ smap_add__(smap, xmemdup0(key, key_len), xstrdup(value), hash);
+ }
+}
+
+/* If 'key' is in 'smap', removes it. Otherwise does nothing. */
+void
+smap_remove(struct smap *smap, const char *key)
+{
+ struct smap_node *node = smap_get_node(smap, key);
+
+ if (node) {
+ smap_remove_node(smap, node);
+ }
+}
+
+/* Removes 'node' from 'smap'. */
+void
+smap_remove_node(struct smap *smap, struct smap_node *node)
+{
+ hmap_remove(&smap->map, &node->node);
+ free(node->key);
+ free(node->value);
+ free(node);
+}
+
+/* Deletes 'node' from 'smap'.
+ *
+ * If 'keyp' is nonnull, stores the node's key in '*keyp' and transfers
+ * ownership to the caller. Otherwise, frees the node's key. Similarly for
+ * 'valuep' and the node's value. */
+void
+smap_steal(struct smap *smap, struct smap_node *node,
+ char **keyp, char **valuep)
+{
+ if (keyp) {
+ *keyp = node->key;
+ } else {
+ free(node->key);
+ }
+
+ if (valuep) {
+ *valuep = node->value;
+ } else {
+ free(node->value);
+ }
+
+ hmap_remove(&smap->map, &node->node);
+ free(node);
+}
+
+/* Removes all key-value pairs from 'smap'. */
+void
+smap_clear(struct smap *smap)
+{
+ struct smap_node *node, *next;
+
+ SMAP_FOR_EACH_SAFE (node, next, smap) {
+ smap_remove_node(smap, node);
+ }
+}
+
+/* Returns the value associated with 'key' in 'smap', or NULL. */
+const char *
+smap_get(const struct smap *smap, const char *key)
+{
+ struct smap_node *node = smap_get_node(smap, key);
+ return node ? node->value : NULL;
+}
+
+/* Returns the node associated with 'key' in 'smap', or NULL. */
+struct smap_node *
+smap_get_node(const struct smap *smap, const char *key)
+{
+ size_t key_len = strlen(key);
+ return smap_find__(smap, key, key_len, hash_bytes(key, key_len, 0));
+}
+
+/* Gets the value associated with 'key' in 'smap' and converts it to a boolean.
+ * If 'key' is not in 'smap', or its value is neither "true" nor "false",
+ * returns 'def'. */
+bool
+smap_get_bool(const struct smap *smap, const char *key, bool def)
+{
+ const char *value = smap_get(smap, key);
+
+ if (!value) {
+ return def;
+ }
+
+ if (def) {
+ return strcasecmp("false", value) != 0;
+ } else {
+ return !strcasecmp("true", value);
+ }
+}
+
+/* Gets the value associated with 'key' in 'smap' and converts it to an int
+ * using atoi(). If 'key' is not in 'smap', returns 'def'. */
+int
+smap_get_int(const struct smap *smap, const char *key, int def)
+{
+ const char *value = smap_get(smap, key);
+
+ return value ? atoi(value) : def;
+}
+
+/* Returns true of there are no elements in 'smap'. */
+bool
+smap_is_empty(const struct smap *smap)
+{
+ return hmap_is_empty(&smap->map);
+}
+
+/* Returns the number of elements in 'smap'. */
+size_t
+smap_count(const struct smap *smap)
+{
+ return hmap_count(&smap->map);
+}
+
+/* Initializes 'dst' as a clone of 'src. */
+void
+smap_clone(struct smap *dst, const struct smap *src)
+{
+ const struct smap_node *node;
+
+ smap_init(dst);
+ SMAP_FOR_EACH (node, src) {
+ smap_add__(dst, xstrdup(node->key), xstrdup(node->value),
+ node->node.hash);
+ }
+}
+
+/* Returns an array of nodes sorted on key or NULL if 'smap' is empty. The
+ * caller is responsible for freeing this array. */
+const struct smap_node **
+smap_sort(const struct smap *smap)
+{
+ if (smap_is_empty(smap)) {
+ return NULL;
+ } else {
+ const struct smap_node **nodes;
+ struct smap_node *node;
+ size_t i, n;
+
+ n = smap_count(smap);
+ nodes = xmalloc(n * sizeof *nodes);
+ i = 0;
+ SMAP_FOR_EACH (node, smap) {
+ nodes[i++] = node;
+ }
+ assert(i == n);
+
+ qsort(nodes, n, sizeof *nodes, compare_nodes_by_key);
+
+ return nodes;
+ }
+}
+
+/* Adds each of the key-value pairs from 'json' (which must be a JSON object
+ * whose values are strings) to 'smap'.
+ *
+ * The caller must have initialized 'smap'.
+ *
+ * The caller retains ownership of 'json' and everything in it. */
+void
+smap_from_json(struct smap *smap, const struct json *json)
+{
+ const struct shash_node *node;
+
+ SHASH_FOR_EACH (node, json_object(json)) {
+ const struct json *value = node->data;
+ smap_add(smap, node->name, json_string(value));
+ }
+}
+
+/* Returns a JSON object that maps from the keys in 'smap' to their values.
+ *
+ * The caller owns the returned value and must eventually json_destroy() it.
+ *
+ * The caller retains ownership of 'smap' and everything in it. */
+struct json *
+smap_to_json(const struct smap *smap)
+{
+ const struct smap_node *node;
+ struct json *json;
+
+ json = json_object_create();
+ SMAP_FOR_EACH (node, smap) {
+ json_object_put_string(json, node->key, node->value);
+ }
+ return json;
+}
+\f
+/* Private Helpers. */
+
+static struct smap_node *
+smap_add__(struct smap *smap, char *key, void *value, size_t hash)
+{
+ struct smap_node *node = xmalloc(sizeof *node);
+ node->key = key;
+ node->value = value;
+ hmap_insert(&smap->map, &node->node, hash);
+ return node;
+}
+
+static struct smap_node *
+smap_find__(const struct smap *smap, const char *key, size_t key_len,
+ size_t hash)
+{
+ struct smap_node *node;
+
+ HMAP_FOR_EACH_WITH_HASH (node, node, hash, &smap->map) {
+ if (!strncmp(node->key, key, key_len) && !node->key[key_len]) {
+ return node;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+compare_nodes_by_key(const void *a_, const void *b_)
+{
+ const struct smap_node *const *a = a_;
+ const struct smap_node *const *b = b_;
+ return strcmp((*a)->key, (*b)->key);
+}
--- /dev/null
+/* Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#ifndef SMAP_H
+#define SMAP_H 1
+
+#include "hmap.h"
+
+struct json;
+
+/* A map from string to string. */
+struct smap {
+ struct hmap map; /* Contains "struct smap_node"s. */
+};
+
+struct smap_node {
+ struct hmap_node node; /* In struct smap's 'map' hmap. */
+ char *key;
+ char *value;
+};
+
+#define SMAP_INITIALIZER(SMAP) { HMAP_INITIALIZER(&(SMAP)->map) }
+
+#define SMAP_FOR_EACH(SMAP_NODE, SMAP) \
+ HMAP_FOR_EACH (SMAP_NODE, node, &(SMAP)->map)
+
+#define SMAP_FOR_EACH_SAFE(SMAP_NODE, NEXT, SMAP) \
+ HMAP_FOR_EACH_SAFE (SMAP_NODE, NEXT, node, &(SMAP)->map)
+
+void smap_init(struct smap *);
+void smap_destroy(struct smap *);
+
+struct smap_node *smap_add(struct smap *, const char *, const char *);
+bool smap_add_once(struct smap *, const char *, const char *);
+void smap_add_format(struct smap *, const char *key, const char *, ...)
+ PRINTF_FORMAT(3, 4);
+void smap_replace(struct smap *, const char *, const char *);
+
+void smap_remove(struct smap *, const char *);
+void smap_remove_node(struct smap *, struct smap_node *);
+void smap_steal(struct smap *, struct smap_node *, char **keyp, char **valuep);
+void smap_clear(struct smap *);
+
+const char *smap_get(const struct smap *, const char *);
+struct smap_node *smap_get_node(const struct smap *, const char *);
+bool smap_get_bool(const struct smap *smap, const char *key, bool def);
+int smap_get_int(const struct smap *smap, const char *key, int def);
+
+bool smap_is_empty(const struct smap *);
+size_t smap_count(const struct smap *);
+
+void smap_clone(struct smap *dst, const struct smap *src);
+const struct smap_node **smap_sort(const struct smap *);
+
+void smap_from_json(struct smap *, const struct json *);
+struct json *smap_to_json(const struct smap *);
+
+#endif /* smap.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/uio.h>
#include <sys/un.h>
#include <unistd.h>
#include "dynamic-string.h"
#include "fatal-signal.h"
#include "packets.h"
+#include "poll-loop.h"
#include "util.h"
#include "vlog.h"
#if AF_PACKET && __linux__
}
}
+void
+xset_nonblocking(int fd)
+{
+ if (set_nonblocking(fd)) {
+ exit(EXIT_FAILURE);
+ }
+}
+
static int
set_dscp(int fd, uint8_t dscp)
{
+ int val;
+
if (dscp > 63) {
return EINVAL;
}
- dscp = dscp << 2;
- if (setsockopt(fd, IPPROTO_IP, IP_TOS, &dscp, sizeof dscp)) {
+ val = dscp << 2;
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof val)) {
return errno;
}
* Most Open vSwitch code should not use this because it causes deadlocks:
* gethostbyname() sends out a DNS request but that starts a new flow for which
* OVS must set up a flow, but it can't because it's waiting for a DNS reply.
- * The synchronous lookup also delays other activty. (Of course we can solve
+ * The synchronous lookup also delays other activity. (Of course we can solve
* this but it doesn't seem worthwhile quite yet.) */
int
lookup_hostname(const char *host_name, struct in_addr *addr)
/* Creates a Unix domain socket in the given 'style' (either SOCK_DGRAM or
* SOCK_STREAM) that is bound to '*bind_path' (if 'bind_path' is non-null) and
* connected to '*connect_path' (if 'connect_path' is non-null). If 'nonblock'
- * is true, the socket is made non-blocking. If 'passcred' is true, the socket
- * is configured to receive SCM_CREDENTIALS control messages.
+ * is true, the socket is made non-blocking.
*
* Returns the socket's fd if successful, otherwise a negative errno value. */
int
-make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED,
+make_unix_socket(int style, bool nonblock,
const char *bind_path, const char *connect_path)
{
int error;
}
}
-#ifdef SCM_CREDENTIALS
- if (passcred) {
- int enable = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable))) {
- error = errno;
- goto error;
- }
- }
-#endif
-
return fd;
error:
}
}
+void
+xsocketpair(int domain, int type, int protocol, int fds[2])
+{
+ if (socketpair(domain, type, protocol, fds)) {
+ VLOG_FATAL("failed to create socketpair (%s)", strerror(errno));
+ }
+}
+
static int
getsockopt_int(int fd, int level, int option, const char *optname, int *valuep)
{
}
return ds_steal_cstr(&string);
}
+
+/* Returns the total of the 'iov_len' members of the 'n_iovs' in 'iovs'.
+ * The caller must ensure that the total does not exceed SIZE_MAX. */
+size_t
+iovec_len(const struct iovec iovs[], size_t n_iovs)
+{
+ size_t len = 0;
+ size_t i;
+
+ for (i = 0; i < n_iovs; i++) {
+ len += iovs[i].iov_len;
+ }
+ return len;
+}
+
+/* Returns true if all of the 'n_iovs' iovecs in 'iovs' have length zero. */
+bool
+iovec_is_empty(const struct iovec iovs[], size_t n_iovs)
+{
+ size_t i;
+
+ for (i = 0; i < n_iovs; i++) {
+ if (iovs[i].iov_len) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
+ * in 'fds' on Unix domain socket 'sock'. Returns the number of bytes
+ * successfully sent or -1 if an error occurred. On error, sets errno
+ * appropriately. */
+int
+send_iovec_and_fds(int sock,
+ const struct iovec *iovs, size_t n_iovs,
+ const int fds[], size_t n_fds)
+{
+ assert(sock >= 0);
+ if (n_fds > 0) {
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(SOUTIL_MAX_FDS * sizeof *fds)];
+ } cmsg;
+ struct msghdr msg;
+
+ assert(!iovec_is_empty(iovs, n_iovs));
+ assert(n_fds <= SOUTIL_MAX_FDS);
+
+ memset(&cmsg, 0, sizeof cmsg);
+ cmsg.cm.cmsg_len = CMSG_LEN(n_fds * sizeof *fds);
+ cmsg.cm.cmsg_level = SOL_SOCKET;
+ cmsg.cm.cmsg_type = SCM_RIGHTS;
+ memcpy(CMSG_DATA(&cmsg.cm), fds, n_fds * sizeof *fds);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = (struct iovec *) iovs;
+ msg.msg_iovlen = n_iovs;
+ msg.msg_control = &cmsg.cm;
+ msg.msg_controllen = CMSG_SPACE(n_fds * sizeof *fds);
+ msg.msg_flags = 0;
+
+ return sendmsg(sock, &msg, 0);
+ } else {
+ return writev(sock, iovs, n_iovs);
+ }
+}
+
+/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
+ * in 'fds' on Unix domain socket 'sock'. If 'skip_bytes' is nonzero, then the
+ * first 'skip_bytes' of data in the iovecs are not sent, and none of the file
+ * descriptors are sent. The function continues to retry sending until an
+ * error (other than EINTR) occurs or all the data and fds are sent.
+ *
+ * Returns 0 if all the data and fds were successfully sent, otherwise a
+ * positive errno value. Regardless of success, stores the number of bytes
+ * sent (always at least 'skip_bytes') in '*bytes_sent'. (If at least one byte
+ * is sent, then all the fds have been sent.)
+ *
+ * 'skip_bytes' must be less than or equal to iovec_len(iovs, n_iovs). */
+int
+send_iovec_and_fds_fully(int sock,
+ const struct iovec iovs[], size_t n_iovs,
+ const int fds[], size_t n_fds,
+ size_t skip_bytes, size_t *bytes_sent)
+{
+ *bytes_sent = 0;
+ while (n_iovs > 0) {
+ int retval;
+
+ if (skip_bytes) {
+ retval = skip_bytes;
+ skip_bytes = 0;
+ } else if (!*bytes_sent) {
+ retval = send_iovec_and_fds(sock, iovs, n_iovs, fds, n_fds);
+ } else {
+ retval = writev(sock, iovs, n_iovs);
+ }
+
+ if (retval > 0) {
+ *bytes_sent += retval;
+ while (retval > 0) {
+ const uint8_t *base = iovs->iov_base;
+ size_t len = iovs->iov_len;
+
+ if (retval < len) {
+ size_t sent;
+ int error;
+
+ error = write_fully(sock, base + retval, len - retval,
+ &sent);
+ *bytes_sent += sent;
+ retval += sent;
+ if (error) {
+ return error;
+ }
+ }
+ retval -= len;
+ iovs++;
+ n_iovs--;
+ }
+ } else if (retval == 0) {
+ if (iovec_is_empty(iovs, n_iovs)) {
+ break;
+ }
+ VLOG_WARN("send returned 0");
+ return EPROTO;
+ } else if (errno != EINTR) {
+ return errno;
+ }
+ }
+
+ return 0;
+}
+
+/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
+ * in 'fds' on Unix domain socket 'sock'. The function continues to retry
+ * sending until an error (other than EAGAIN or EINTR) occurs or all the data
+ * and fds are sent. Upon EAGAIN, the function blocks until the socket is
+ * ready for more data.
+ *
+ * Returns 0 if all the data and fds were successfully sent, otherwise a
+ * positive errno value. */
+int
+send_iovec_and_fds_fully_block(int sock,
+ const struct iovec iovs[], size_t n_iovs,
+ const int fds[], size_t n_fds)
+{
+ size_t sent = 0;
+
+ for (;;) {
+ int error;
+
+ error = send_iovec_and_fds_fully(sock, iovs, n_iovs,
+ fds, n_fds, sent, &sent);
+ if (error != EAGAIN) {
+ return error;
+ }
+ poll_fd_wait(sock, POLLOUT);
+ poll_block();
+ }
+}
+
+/* Attempts to receive from Unix domain socket 'sock' up to 'size' bytes of
+ * data into 'data' and up to SOUTIL_MAX_FDS file descriptors into 'fds'.
+ *
+ * - Upon success, returns the number of bytes of data copied into 'data'
+ * and stores the number of received file descriptors into '*n_fdsp'.
+ *
+ * - On failure, returns a negative errno value and stores 0 in
+ * '*n_fdsp'.
+ *
+ * - On EOF, returns 0 and stores 0 in '*n_fdsp'. */
+int
+recv_data_and_fds(int sock,
+ void *data, size_t size,
+ int fds[SOUTIL_MAX_FDS], size_t *n_fdsp)
+{
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(SOUTIL_MAX_FDS * sizeof *fds)];
+ } cmsg;
+ struct msghdr msg;
+ int retval;
+ struct cmsghdr *p;
+ size_t i;
+
+ *n_fdsp = 0;
+
+ do {
+ struct iovec iov;
+
+ iov.iov_base = data;
+ iov.iov_len = size;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsg.cm;
+ msg.msg_controllen = sizeof cmsg.control;
+ msg.msg_flags = 0;
+
+ retval = recvmsg(sock, &msg, 0);
+ } while (retval < 0 && errno == EINTR);
+ if (retval <= 0) {
+ return retval < 0 ? -errno : 0;
+ }
+
+ for (p = CMSG_FIRSTHDR(&msg); p; p = CMSG_NXTHDR(&msg, p)) {
+ if (p->cmsg_level != SOL_SOCKET || p->cmsg_type != SCM_RIGHTS) {
+ VLOG_ERR("unexpected control message %d:%d",
+ p->cmsg_level, p->cmsg_type);
+ goto error;
+ } else if (*n_fdsp) {
+ VLOG_ERR("multiple SCM_RIGHTS received");
+ goto error;
+ } else {
+ size_t n_fds = (p->cmsg_len - CMSG_LEN(0)) / sizeof *fds;
+ const int *fds_data = (const int *) CMSG_DATA(p);
+
+ assert(n_fds > 0);
+ if (n_fds > SOUTIL_MAX_FDS) {
+ VLOG_ERR("%zu fds received but only %d supported",
+ n_fds, SOUTIL_MAX_FDS);
+ for (i = 0; i < n_fds; i++) {
+ close(fds_data[i]);
+ }
+ goto error;
+ }
+
+ *n_fdsp = n_fds;
+ memcpy(fds, fds_data, n_fds * sizeof *fds);
+ }
+ }
+
+ return retval;
+
+error:
+ for (i = 0; i < *n_fdsp; i++) {
+ close(fds[i]);
+ }
+ *n_fdsp = 0;
+ return EPROTO;
+}
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <netinet/ip.h>
int set_nonblocking(int fd);
+void xset_nonblocking(int fd);
+
int get_max_fds(void);
int lookup_ip(const char *host_name, struct in_addr *address);
int check_connection_completion(int fd);
int drain_rcvbuf(int fd);
void drain_fd(int fd, size_t n_packets);
-int make_unix_socket(int style, bool nonblock, bool passcred,
+int make_unix_socket(int style, bool nonblock,
const char *bind_path, const char *connect_path);
int get_unix_name_len(socklen_t sun_len);
ovs_be32 guess_netmask(ovs_be32 ip);
* in <netinet/ip.h> is used. */
#define DSCP_DEFAULT (IPTOS_PREC_INTERNETCONTROL >> 2)
+/* Maximum number of fds that we support sending or receiving at one time
+ * across a Unix domain socket. */
+#define SOUTIL_MAX_FDS 8
+
+/* Iovecs. */
+size_t iovec_len(const struct iovec *iovs, size_t n_iovs);
+bool iovec_is_empty(const struct iovec *iovs, size_t n_iovs);
+
+/* Functions particularly useful for Unix domain sockets. */
+void xsocketpair(int domain, int type, int protocol, int fds[2]);
+int send_iovec_and_fds(int sock,
+ const struct iovec *iovs, size_t n_iovs,
+ const int fds[], size_t n_fds);
+int send_iovec_and_fds_fully(int sock,
+ const struct iovec *iovs, size_t n_iovs,
+ const int fds[], size_t n_fds,
+ size_t skip_bytes, size_t *bytes_sent);
+int send_iovec_and_fds_fully_block(int sock,
+ const struct iovec *iovs, size_t n_iovs,
+ const int fds[], size_t n_fds);
+int recv_data_and_fds(int sock,
+ void *data, size_t size,
+ int fds[SOUTIL_MAX_FDS], size_t *n_fdsp);
+
#endif /* socket-util.h */
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#define SSET_H
#include "hmap.h"
+#include "util.h"
#ifdef __cplusplus
extern "C" {
#define SSET_NODE_FROM_HMAP_NODE(HMAP_NODE) \
CONTAINER_OF(HMAP_NODE, struct sset_node, hmap_node)
#define SSET_NAME_FROM_HMAP_NODE(HMAP_NODE) \
- ((const char *) (SSET_NODE_FROM_HMAP_NODE(HMAP_NODE)->name))
+ (CONST_CAST(const char *, (SSET_NODE_FROM_HMAP_NODE(HMAP_NODE)->name)))
#define SSET_NODE_FROM_NAME(NAME) CONTAINER_OF(NAME, struct sset_node, name)
#define SSET_FIRST(SSET) SSET_NAME_FROM_HMAP_NODE(hmap_first(&(SSET)->map))
#define SSET_NEXT(SSET, NAME) \
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
for (; port < &stp->ports[ARRAY_SIZE(stp->ports)]; port++) {
if (port->state != STP_DISABLED) {
- return (struct stp_port *) port;
+ return CONST_CAST(struct stp_port *, port);
}
}
return NULL;
/*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Prefix for connection names, e.g. "tcp", "ssl", "unix". */
const char *name;
- /* True if this stream needs periodic probes to verify connectivty. For
+ /* True if this stream needs periodic probes to verify connectivity. For
* streams which need probes, it can take a long time to notice the
* connection was dropped. */
bool needs_probes;
/* Prefix for connection names, e.g. "ptcp", "pssl", "punix". */
const char *name;
- /* True if this pstream needs periodic probes to verify connectivty. For
+ /* True if this pstream needs periodic probes to verify connectivity. For
* pstreams which need probes, it can take a long time to notice the
* connection was dropped. */
bool needs_probes;
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <openssl/err.h>
+#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <poll.h>
#include <unistd.h>
#include "coverage.h"
#include "dynamic-string.h"
+#include "entropy.h"
#include "leak-checker.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
VLOG_ERR("CA certificate must be configured to use SSL");
retval = ENOPROTOOPT;
}
- if (!SSL_CTX_check_private_key(ctx)) {
+ if (!retval && !SSL_CTX_check_private_key(ctx)) {
VLOG_ERR("Private key does not match certificate public key: %s",
ERR_error_string(ERR_get_error(), NULL));
retval = ENOPROTOOPT;
* certificate, but that's more trouble than it's worth. These
* connections will succeed the next time they retry, assuming that
* they have a certificate against the correct CA.) */
- VLOG_ERR("rejecting SSL connection during bootstrap race window");
+ VLOG_INFO("rejecting SSL connection during bootstrap race window");
return EPROTO;
} else {
return 0;
SSL_library_init();
SSL_load_error_strings();
+ if (!RAND_status()) {
+ /* We occasionally see OpenSSL fail to seed its random number generator
+ * in heavily loaded hypervisors. I suspect the following scenario:
+ *
+ * 1. OpenSSL calls read() to get 32 bytes from /dev/urandom.
+ * 2. The kernel generates 10 bytes of randomness and copies it out.
+ * 3. A signal arrives (perhaps SIGALRM).
+ * 4. The kernel interrupts the system call to service the signal.
+ * 5. Userspace gets 10 bytes of entropy.
+ * 6. OpenSSL doesn't read again to get the final 22 bytes. Therefore
+ * OpenSSL doesn't have enough entropy to consider itself
+ * initialized.
+ *
+ * The only part I'm not entirely sure about is #6, because the OpenSSL
+ * code is so hard to read. */
+ uint8_t seed[32];
+ int retval;
+
+ VLOG_WARN("OpenSSL random seeding failed, reseeding ourselves");
+
+ retval = get_entropy(seed, sizeof seed);
+ if (retval) {
+ VLOG_ERR("failed to obtain entropy (%s)",
+ ovs_retval_to_string(retval));
+ return retval > 0 ? retval : ENOPROTOOPT;
+ }
+
+ RAND_seed(seed, sizeof seed);
+ }
+
/* New OpenSSL changed TLSv1_method() to return a "const" pointer, so the
* cast is needed to avoid a warning with those newer versions. */
- method = (SSL_METHOD *) TLSv1_method();
+ method = CONST_CAST(SSL_METHOD *, TLSv1_method());
if (method == NULL) {
VLOG_ERR("TLSv1_method: %s", ERR_error_string(ERR_get_error(), NULL));
return ENOPROTOOPT;
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
const char *connect_path = suffix;
int fd;
- fd = make_unix_socket(SOCK_STREAM, true, false, NULL, connect_path);
+ fd = make_unix_socket(SOCK_STREAM, true, NULL, connect_path);
if (fd < 0) {
VLOG_ERR("%s: connection failed (%s)", connect_path, strerror(-fd));
return -fd;
{
int fd, error;
- fd = make_unix_socket(SOCK_STREAM, true, true, suffix, NULL);
+ fd = make_unix_socket(SOCK_STREAM, true, suffix, NULL);
if (fd < 0) {
VLOG_ERR("%s: binding failed: %s", suffix, strerror(errno));
return errno;
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return STREAM_SSL;
case PAIR('{', '"'):
return STREAM_JSONRPC;
- case PAIR(OFP10_VERSION, OFPT_HELLO):
+ case PAIR(OFP10_VERSION, 0 /* OFPT_HELLO */):
return STREAM_OPENFLOW;
}
}
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
table_format_timestamp__(char *s, size_t size)
{
time_t now = time_wall();
- strftime(s, size, "%Y-%m-%d %H:%M:%S", localtime(&now));
+ strftime(s, size, "%Y-%m-%d %H:%M:%S", gmtime(&now));
}
static void
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* to CLOCK_REALTIME. */
static clockid_t monotonic_clock;
-/* Has a timer tick occurred?
+/* Has a timer tick occurred? Only relevant if CACHE_TIME is 1.
*
* We initialize these to true to force time_init() to get called on the first
* call to time_msec() or another function that queries the current time. */
VLOG_DBG("monotonic timer not available");
}
- set_up_signal(SA_RESTART);
- set_up_timer();
+ if (CACHE_TIME) {
+ set_up_signal(SA_RESTART);
+ set_up_timer();
+ }
+
boot_time = time_msec();
}
time_postfork(void)
{
time_init();
- set_up_timer();
+
+ if (CACHE_TIME) {
+ set_up_timer();
+ } else {
+ /* If we are not caching kernel time, the only reason the timer should
+ * exist is if time_alarm() was called and deadline is set */
+ if (deadline != TIME_MIN) {
+ set_up_timer();
+ }
+ }
}
static void
/* Forces a refresh of the current time from the kernel. It is not usually
* necessary to call this function, since the time will be refreshed
- * automatically at least every TIME_UPDATE_INTERVAL milliseconds. */
+ * automatically at least every TIME_UPDATE_INTERVAL milliseconds. If
+ * CACHE_TIME is 0, we will always refresh the current time so this
+ * function has no effect. */
void
time_refresh(void)
{
sigset_t oldsigs;
time_init();
+
block_sigalrm(&oldsigs);
deadline = secs ? time_add(time_now(), secs) : TIME_MIN;
unblock_sigalrm(&oldsigs);
+
+ if (!CACHE_TIME) {
+ /* If we aren't timing the gaps between kernel time refreshes we need to
+ * to start the timer up now */
+ set_up_signal(SA_RESTART);
+ set_up_timer();
+ }
}
/* Like poll(), except:
static void
refresh_wall_if_ticked(void)
{
- if (wall_tick) {
+ if (!CACHE_TIME || wall_tick) {
refresh_wall();
}
}
static void
refresh_monotonic_if_ticked(void)
{
- if (monotonic_tick) {
+ if (!CACHE_TIME || monotonic_tick) {
refresh_monotonic();
}
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* ever encounter such a platform. */
BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t));
+/* On x86-64 systems, Linux avoids using syscalls for clock_gettime().
+ *
+ * For systems which do invoke a system call we wait at least
+ * TIME_UPDATE_INTERVAL ms between clock_gettime() calls and cache the time for
+ * the interim.
+ *
+ * For systems which do not invoke a system call, we just call clock_gettime()
+ * whenever the time is requested. As a result we don't start the background
+ * SIGALRM timer unless explicitly needed by time_alarm() */
+#if defined __x86_64__ && defined __linux__
+#define CACHE_TIME 0
+#else
+#define CACHE_TIME 1
+#endif
+
#define TIME_MAX TYPE_MAXIMUM(time_t)
#define TIME_MIN TYPE_MINIMUM(time_t)
--- /dev/null
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "token-bucket.h"
+
+#include "poll-loop.h"
+#include "sat-math.h"
+#include "timeval.h"
+#include "util.h"
+
+/* Initializes 'tb' to accumulate 'rate' tokens per millisecond, with a
+ * maximum of 'burst' tokens.
+ *
+ * The token bucket is initially full.
+ *
+ * It may be more convenient to use TOKEN_BUCKET_INIT. */
+void
+token_bucket_init(struct token_bucket *tb,
+ unsigned int rate, unsigned int burst)
+{
+ tb->rate = rate;
+ tb->burst = burst;
+ tb->tokens = 0;
+ tb->last_fill = LLONG_MIN;
+}
+
+/* Changes 'tb' to accumulate 'rate' tokens per millisecond, with a maximum of
+ * 'burst' tokens.
+ *
+ * 'tb' must already have been initialized with TOKEN_BUCKET_INIT or
+ * token_bucket_init(). */
+void
+token_bucket_set(struct token_bucket *tb,
+ unsigned int rate, unsigned int burst)
+{
+ tb->rate = rate;
+ tb->burst = burst;
+ if (burst > tb->tokens) {
+ tb->tokens = burst;
+ }
+}
+
+/* Attempts to remove 'n' tokens from 'tb'. Returns true if successful, false
+ * if 'tb' contained fewer than 'n' tokens (and thus 'n' tokens could not be
+ * removed) . */
+bool
+token_bucket_withdraw(struct token_bucket *tb, unsigned int n)
+{
+ if (tb->tokens < n) {
+ long long int now = time_msec();
+ if (now > tb->last_fill) {
+ unsigned long long int elapsed_ull
+ = (unsigned long long int) now - tb->last_fill;
+ unsigned int elapsed = MIN(UINT_MAX, elapsed_ull);
+ unsigned int add = sat_mul(tb->rate, elapsed);
+ unsigned int tokens = sat_add(tb->tokens, add);
+ tb->tokens = MIN(tokens, tb->burst);
+ tb->last_fill = now;
+ }
+
+ if (tb->tokens < n) {
+ return false;
+ }
+ }
+
+ tb->tokens -= n;
+ return true;
+}
+
+/* Causes the poll loop to wake up when at least 'n' tokens will be available
+ * for withdrawal from 'tb'. */
+void
+token_bucket_wait(struct token_bucket *tb, unsigned int n)
+{
+ if (tb->tokens >= n) {
+ poll_immediate_wake();
+ } else {
+ unsigned int need = n - tb->tokens;
+ poll_timer_wait_until(tb->last_fill + need / tb->rate + 1);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TOKEN_BUCKET_H
+#define TOKEN_BUCKET_H 1
+
+#include <limits.h>
+#include <stdbool.h>
+
+struct token_bucket {
+ /* Configuration settings. */
+ unsigned int rate; /* Tokens added per millisecond. */
+ unsigned int burst; /* Max cumulative tokens credit. */
+
+ /* Current status. */
+ unsigned int tokens; /* Current number of tokens. */
+ long long int last_fill; /* Last time tokens added. */
+};
+
+#define TOKEN_BUCKET_INIT(RATE, BURST) { RATE, BURST, 0, LLONG_MIN }
+
+void token_bucket_init(struct token_bucket *,
+ unsigned int rate, unsigned int burst);
+void token_bucket_set(struct token_bucket *,
+ unsigned int rate, unsigned int burst);
+bool token_bucket_withdraw(struct token_bucket *, unsigned int n);
+void token_bucket_wait(struct token_bucket *, unsigned int n);
+
+#endif /* token-bucket.h */
/*
- * Copyright (c) 2008, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
#include <unistd.h>
#include "byte-order.h"
#include "coverage.h"
COVERAGE_DEFINE(util_xalloc);
+/* argv[0] without directory names. */
const char *program_name;
+
+/* Ordinarily "" but set to "monitor" for a monitor process or "worker" for a
+ * worker process. */
+const char *subprogram_name = "";
+
+/* --version option output. */
static char *program_version;
void
va_list args;
va_start(args, format);
- ovs_error_valist(err_no, format, args);
- va_end(args);
+ ovs_abort_valist(err_no, format, args);
+}
+/* Same as ovs_abort() except that the arguments are supplied as a va_list. */
+void
+ovs_abort_valist(int err_no, const char *format, va_list args)
+{
+ ovs_error_valist(err_no, format, args);
abort();
}
{
int save_errno = errno;
- fprintf(stderr, "%s: ", program_name);
+ if (subprogram_name[0]) {
+ fprintf(stderr, "%s(%s): ", program_name, subprogram_name);
+ } else {
+ fprintf(stderr, "%s: ", program_name);
+ }
+
vfprintf(stderr, format, args);
if (err_no != 0) {
fprintf(stderr, " (%s)", ovs_retval_to_string(err_no));
}
}
+/* Like readlink(), but returns the link name as a null-terminated string in
+ * allocated memory that the caller must eventually free (with free()).
+ * Returns NULL on error, in which case errno is set appropriately. */
+char *
+xreadlink(const char *filename)
+{
+ size_t size;
+
+ for (size = 64; ; size *= 2) {
+ char *buf = xmalloc(size);
+ ssize_t retval = readlink(filename, buf, size);
+ int error = errno;
+
+ if (retval >= 0 && retval < size) {
+ buf[retval] = '\0';
+ return buf;
+ }
+
+ free(buf);
+ if (retval < 0) {
+ errno = error;
+ return NULL;
+ }
+ }
+}
+
+/* Returns a version of 'filename' with symlinks in the final component
+ * dereferenced. This differs from realpath() in that:
+ *
+ * - 'filename' need not exist.
+ *
+ * - If 'filename' does exist as a symlink, its referent need not exist.
+ *
+ * - Only symlinks in the final component of 'filename' are dereferenced.
+ *
+ * The caller must eventually free the returned string (with free()). */
+char *
+follow_symlinks(const char *filename)
+{
+ struct stat s;
+ char *fn;
+ int i;
+
+ fn = xstrdup(filename);
+ for (i = 0; i < 10; i++) {
+ char *linkname;
+ char *next_fn;
+
+ if (lstat(fn, &s) != 0 || !S_ISLNK(s.st_mode)) {
+ return fn;
+ }
+
+ linkname = xreadlink(fn);
+ if (!linkname) {
+ VLOG_WARN("%s: readlink failed (%s)", filename, strerror(errno));
+ return fn;
+ }
+
+ if (linkname[0] == '/') {
+ /* Target of symlink is absolute so use it raw. */
+ next_fn = linkname;
+ } else {
+ /* Target of symlink is relative so add to 'fn''s directory. */
+ char *dir = dir_name(fn);
+
+ if (!strcmp(dir, ".")) {
+ next_fn = linkname;
+ } else {
+ char *separator = dir[strlen(dir) - 1] == '/' ? "" : "/";
+ next_fn = xasprintf("%s%s%s", dir, separator, linkname);
+ free(linkname);
+ }
+
+ free(dir);
+ }
+
+ free(fn);
+ fn = next_fn;
+ }
+
+ VLOG_WARN("%s: too many levels of symlinks", filename);
+ free(fn);
+ return xstrdup(filename);
+}
/* Pass a value to this function if it is marked with
* __attribute__((warn_unused_result)) and you genuinely want to ignore
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#define BUILD_ASSERT_DECL_GCCONLY(EXPR) ((void) 0)
#endif
+/* Casts 'pointer' to 'type' and issues a compiler warning if the cast changes
+ * anything other than an outermost "const" or "volatile" qualifier.
+ *
+ * The cast to int is present only to suppress an "expression using sizeof
+ * bool" warning from "sparse" (see
+ * http://permalink.gmane.org/gmane.comp.parsers.sparse/2967). */
+#define CONST_CAST(TYPE, POINTER) \
+ ((void) sizeof ((int) ((POINTER) == (TYPE) (POINTER))), \
+ (TYPE) (POINTER))
+
extern const char *program_name;
+extern const char *subprogram_name;
/* Returns the number of elements in ARRAY. */
#define ARRAY_SIZE(ARRAY) (sizeof ARRAY / sizeof *ARRAY)
void ovs_abort(int err_no, const char *format, ...)
PRINTF_FORMAT(2, 3) NO_RETURN;
+void ovs_abort_valist(int err_no, const char *format, va_list)
+ PRINTF_FORMAT(2, 0) NO_RETURN;
void ovs_fatal(int err_no, const char *format, ...)
PRINTF_FORMAT(2, 3) NO_RETURN;
void ovs_fatal_valist(int err_no, const char *format, va_list)
char *base_name(const char *file_name);
char *abs_file_name(const char *dir, const char *file_name);
+char *xreadlink(const char *filename);
+char *follow_symlinks(const char *filename);
+
void ignore(bool x OVS_UNUSED);
int log_2_floor(uint32_t);
int log_2_ceil(uint32_t);
-/* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2008, 2009, 2010 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <assert.h>
#include "vconn.h"
+#include "openflow/openflow-common.h"
\f
/* Active virtual connection to an OpenFlow device. */
struct vconn_class *class;
int state;
int error;
- int min_version;
- int version;
+ enum ofp_version min_version;
+ enum ofp_version version;
ovs_be32 remote_ip;
ovs_be16 remote_port;
ovs_be32 local_ip;
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "fatal-signal.h"
#include "flow.h"
#include "ofp-errors.h"
+#include "ofp-msgs.h"
#include "ofp-print.h"
#include "ofp-util.h"
#include "ofpbuf.h"
printf("\n");
if (active) {
printf("Active OpenFlow connection methods:\n");
- printf(" tcp:IP[:PORT] "
+ printf(" tcp:IP[:PORT] "
"PORT (default: %d) at remote IP\n", OFP_TCP_PORT);
#ifdef HAVE_OPENSSL
- printf(" ssl:IP[:PORT] "
+ printf(" ssl:IP[:PORT] "
"SSL PORT (default: %d) at remote IP\n", OFP_SSL_PORT);
#endif
printf(" unix:FILE Unix domain socket named FILE\n");
void
vconn_run(struct vconn *vconn)
{
+ if (vconn->state == VCS_CONNECTING ||
+ vconn->state == VCS_SEND_HELLO ||
+ vconn->state == VCS_RECV_HELLO) {
+ vconn_connect(vconn);
+ }
+
if (vconn->class->run) {
(vconn->class->run)(vconn);
}
void
vconn_run_wait(struct vconn *vconn)
{
+ if (vconn->state == VCS_CONNECTING ||
+ vconn->state == VCS_SEND_HELLO ||
+ vconn->state == VCS_RECV_HELLO) {
+ vconn_connect_wait(vconn);
+ }
+
if (vconn->class->run_wait) {
(vconn->class->run_wait)(vconn);
}
}
int
-vconn_open_block(const char *name, int min_version, struct vconn **vconnp)
+vconn_open_block(const char *name, enum ofp_version min_version,
+ struct vconn **vconnp)
{
struct vconn *vconn;
int error;
error = vconn_open(name, min_version, &vconn, DSCP_DEFAULT);
if (!error) {
- while ((error = vconn_connect(vconn)) == EAGAIN) {
- vconn_run(vconn);
- vconn_run_wait(vconn);
- vconn_connect_wait(vconn);
- poll_block();
- }
- assert(error != EINPROGRESS);
+ error = vconn_connect_block(vconn);
}
if (error) {
int
vconn_get_version(const struct vconn *vconn)
{
- return vconn->version;
+ return vconn->version ? vconn->version : -1;
}
static void
struct ofpbuf *b;
int retval;
- make_openflow(sizeof(struct ofp_header), OFPT_HELLO, &b);
+ b = ofpraw_alloc(OFPRAW_OFPT_HELLO, OFP10_VERSION, 0);
retval = do_send(vconn, b);
if (!retval) {
vconn->state = VCS_RECV_HELLO;
retval = do_recv(vconn, &b);
if (!retval) {
- struct ofp_header *oh = b->data;
+ const struct ofp_header *oh = b->data;
+ enum ofptype type;
+ enum ofperr error;
- if (oh->type == OFPT_HELLO) {
+ error = ofptype_decode(&type, b->data);
+ if (!error && type == OFPTYPE_HELLO) {
if (b->size > sizeof *oh) {
struct ds msg = DS_EMPTY_INITIALIZER;
ds_put_format(&msg, "%s: extra-long hello:\n", vconn->name);
snprintf(s, sizeof s, "We support versions 0x%02x to 0x%02x inclusive but "
"you support no later than version 0x%02"PRIx8".",
- vconn->min_version, OFP10_VERSION, vconn->version);
- b = ofperr_encode_hello(OFPERR_OFPHFC_INCOMPATIBLE,
- ofperr_domain_from_version(vconn->version), s);
+ vconn->min_version, OFP12_VERSION, vconn->version);
+ b = ofperr_encode_hello(OFPERR_OFPHFC_INCOMPATIBLE, vconn->version, s);
retval = do_send(vconn, b);
if (retval) {
ofpbuf_delete(b);
int
vconn_recv(struct vconn *vconn, struct ofpbuf **msgp)
{
- int retval = vconn_connect(vconn);
+ struct ofpbuf *msg;
+ int retval;
+
+ retval = vconn_connect(vconn);
+ if (!retval) {
+ retval = do_recv(vconn, &msg);
+ }
if (!retval) {
- retval = do_recv(vconn, msgp);
+ const struct ofp_header *oh = msg->data;
+ if (oh->version != vconn->version) {
+ enum ofptype type;
+
+ if (ofptype_decode(&type, msg->data)
+ || (type != OFPTYPE_HELLO &&
+ type != OFPTYPE_ERROR &&
+ type != OFPTYPE_ECHO_REQUEST &&
+ type != OFPTYPE_ECHO_REPLY)) {
+ VLOG_ERR_RL(&bad_ofmsg_rl, "%s: received OpenFlow version "
+ "0x%02"PRIx8" != expected %02x",
+ vconn->name, oh->version, vconn->version);
+ ofpbuf_delete(msg);
+ retval = EPROTO;
+ }
+ }
}
+
+ *msgp = retval ? NULL : msg;
return retval;
}
{
int retval = (vconn->class->recv)(vconn, msgp);
if (!retval) {
- struct ofp_header *oh;
-
COVERAGE_INC(vconn_received);
if (VLOG_IS_DBG_ENABLED()) {
char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
VLOG_DBG_RL(&ofmsg_rl, "%s: received: %s", vconn->name, s);
free(s);
}
-
- oh = ofpbuf_at_assert(*msgp, 0, sizeof *oh);
- if ((oh->version != vconn->version || oh->version == 0)
- && oh->type != OFPT_HELLO
- && oh->type != OFPT_ERROR
- && oh->type != OFPT_ECHO_REQUEST
- && oh->type != OFPT_ECHO_REPLY
- && oh->type != OFPT_VENDOR)
- {
- if (vconn->version == 0) {
- VLOG_ERR_RL(&bad_ofmsg_rl,
- "%s: received OpenFlow message type %"PRIu8" "
- "before version negotiation complete",
- vconn->name, oh->type);
- } else {
- VLOG_ERR_RL(&bad_ofmsg_rl,
- "%s: received OpenFlow version 0x%02"PRIx8" "
- "!= expected %02x",
- vconn->name, oh->version, vconn->version);
- }
- ofpbuf_delete(*msgp);
- retval = EPROTO;
- }
- }
- if (retval) {
- *msgp = NULL;
}
return retval;
}
int retval;
assert(msg->size >= sizeof(struct ofp_header));
- assert(((struct ofp_header *) msg->data)->length == htons(msg->size));
+
+ ofpmsg_update_length(msg);
if (!VLOG_IS_DBG_ENABLED()) {
COVERAGE_INC(vconn_sent);
retval = (vconn->class->send)(vconn, msg);
return retval;
}
+/* Same as vconn_connect(), except that it waits until the connection on
+ * 'vconn' completes or fails. Thus, it will never return EAGAIN. */
+int
+vconn_connect_block(struct vconn *vconn)
+{
+ int error;
+
+ while ((error = vconn_connect(vconn)) == EAGAIN) {
+ vconn_run(vconn);
+ vconn_run_wait(vconn);
+ vconn_connect_wait(vconn);
+ poll_block();
+ }
+ assert(error != EINPROGRESS);
+
+ return error;
+}
+
/* Same as vconn_send, except that it waits until 'msg' can be transmitted. */
int
vconn_send_block(struct vconn *vconn, struct ofpbuf *msg)
}
/* Send barrier. */
- barrier = ofputil_encode_barrier_request();
+ barrier = ofputil_encode_barrier_request(vconn_get_version(vconn));
barrier_xid = ((struct ofp_header *) barrier->data)->xid;
error = vconn_send_block(vconn, barrier);
if (error) {
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdbool.h>
#include "openvswitch/types.h"
+#include "openflow/openflow.h"
#ifdef __cplusplus
extern "C" {
void vconn_run(struct vconn *);
void vconn_run_wait(struct vconn *);
-int vconn_open_block(const char *name, int min_version, struct vconn **);
+int vconn_open_block(const char *name, enum ofp_version min_version,
+ struct vconn **);
+int vconn_connect_block(struct vconn *);
int vconn_send_block(struct vconn *, struct ofpbuf *);
int vconn_recv_block(struct vconn *, struct ofpbuf **);
-/* Copyright (c) 2011 Nicira Networks
+/* Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2011 Nicira Networks
+/* Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <sys/stat.h>
#include "hash.h"
-#include "rtnetlink-link.h"
#include "shash.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(vlandev);
#ifdef __linux__
+#include "rtnetlink-link.h"
#include <linux/if_vlan.h>
#include <linux/sockios.h>
#include "netdev-linux.h"
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dirs.h"
#include "dynamic-string.h"
+#include "ofpbuf.h"
#include "sat-math.h"
#include "svec.h"
#include "timeval.h"
#include "unixctl.h"
#include "util.h"
+#include "worker.h"
VLOG_DEFINE_THIS_MODULE(vlog);
/* VLF_FILE configuration. */
static char *log_file_name;
-static FILE *log_file;
+static int log_fd = -1;
/* vlog initialized? */
static bool vlog_inited;
enum vlog_facility, unsigned int msg_num,
const char *message, va_list, struct ds *)
PRINTF_FORMAT(5, 0);
+static void vlog_write_file(struct ds *);
+static void vlog_update_async_log_fd(void);
/* Searches the 'n_names' in 'names'. Returns the index of a match for
* 'target', or 'n_names' if no name matches. */
module->min_level = VLL_OFF;
for (facility = 0; facility < VLF_N_FACILITIES; facility++) {
- if (log_file || facility != VLF_FILE) {
+ if (log_fd >= 0 || facility != VLF_FILE) {
enum vlog_level level = module->levels[facility];
if (level > module->min_level) {
module->min_level = level;
int error;
/* Close old log file. */
- if (log_file) {
+ if (log_fd >= 0) {
VLOG_INFO("closing log file");
- fclose(log_file);
- log_file = NULL;
+ close(log_fd);
+ log_fd = -1;
}
/* Update log file name and free old name. The ordering is important
/* Open new log file and update min_levels[] to reflect whether we actually
* have a log_file. */
- log_file = fopen(log_file_name, "a");
+ log_fd = open(log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
+ if (log_fd >= 0) {
+ vlog_update_async_log_fd();
+ }
for (mp = vlog_modules; mp < &vlog_modules[n_vlog_modules]; mp++) {
update_min_level(*mp);
}
/* Log success or failure. */
- if (!log_file) {
+ if (log_fd < 0) {
VLOG_WARN("failed to open %s for logging: %s",
log_file_name, strerror(errno));
error = errno;
/* Skip re-opening if it would be a no-op because the old and new files are
* the same. (This avoids writing "closing log file" followed immediately
* by "opened log file".) */
- if (log_file
- && !fstat(fileno(log_file), &old)
+ if (log_fd >= 0
+ && !fstat(log_fd, &old)
&& !stat(log_file_name, &new)
&& old.st_dev == new.st_dev
&& old.st_ino == new.st_ino) {
void
vlog_init(void)
{
+ static char *program_name_copy;
time_t now;
if (vlog_inited) {
}
vlog_inited = true;
- openlog(program_name, LOG_NDELAY, LOG_DAEMON);
+ /* openlog() is allowed to keep the pointer passed in, without making a
+ * copy. The daemonize code sometimes frees and replaces 'program_name',
+ * so make a private copy just for openlog(). (We keep a pointer to the
+ * private copy to suppress memory leak warnings in case openlog() does
+ * make its own copy.) */
+ program_name_copy = program_name ? xstrdup(program_name) : NULL;
+ openlog(program_name_copy, LOG_NDELAY, LOG_DAEMON);
now = time_wall();
if (now < 0) {
struct tm tm;
char s[128];
- localtime_r(&now, &tm);
- strftime(s, sizeof s, "%a, %d %b %Y %H:%M:%S %z", &tm);
+ gmtime_r(&now, &tm);
+ strftime(s, sizeof s, "%a, %d %b %Y %H:%M:%S", &tm);
VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now);
}
case 'r':
ds_put_format(s, "%lld", time_msec() - time_boot_msec());
break;
+ case 't':
+ ds_put_cstr(s, subprogram_name[0] ? subprogram_name : "main");
+ break;
+ case 'T':
+ if (subprogram_name[0]) {
+ ds_put_format(s, "(%s)", subprogram_name);
+ }
+ break;
default:
ds_put_char(s, p[-1]);
break;
{
bool log_to_console = module->levels[VLF_CONSOLE] >= level;
bool log_to_syslog = module->levels[VLF_SYSLOG] >= level;
- bool log_to_file = module->levels[VLF_FILE] >= level && log_file;
+ bool log_to_file = module->levels[VLF_FILE] >= level && log_fd >= 0;
if (log_to_console || log_to_syslog || log_to_file) {
int save_errno = errno;
static unsigned int msg_num;
format_log_message(module, level, VLF_FILE, msg_num,
message, args, &s);
ds_put_char(&s, '\n');
- fputs(ds_cstr(&s), log_file);
- fflush(log_file);
+ vlog_write_file(&s);
}
ds_destroy(&s);
va_end(args);
}
+/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure
+ * exit code. Always writes the message to stderr, even if the console
+ * facility is disabled.
+ *
+ * Choose this function instead of vlog_abort_valist() if the daemon monitoring
+ * facility shouldn't automatically restart the current daemon. */
void
vlog_fatal_valist(const struct vlog_module *module_,
const char *message, va_list args)
{
- struct vlog_module *module = (struct vlog_module *) module_;
+ struct vlog_module *module = CONST_CAST(struct vlog_module *, module_);
/* Don't log this message to the console to avoid redundancy with the
* message written by the later ovs_fatal_valist(). */
ovs_fatal_valist(0, message, args);
}
+/* Logs 'message' to 'module' at maximum verbosity, then exits with a failure
+ * exit code. Always writes the message to stderr, even if the console
+ * facility is disabled.
+ *
+ * Choose this function instead of vlog_abort() if the daemon monitoring
+ * facility shouldn't automatically restart the current daemon. */
void
vlog_fatal(const struct vlog_module *module, const char *message, ...)
{
va_end(args);
}
+/* Logs 'message' to 'module' at maximum verbosity, then calls abort(). Always
+ * writes the message to stderr, even if the console facility is disabled.
+ *
+ * Choose this function instead of vlog_fatal_valist() if the daemon monitoring
+ * facility should automatically restart the current daemon. */
+void
+vlog_abort_valist(const struct vlog_module *module_,
+ const char *message, va_list args)
+{
+ struct vlog_module *module = (struct vlog_module *) module_;
+
+ /* Don't log this message to the console to avoid redundancy with the
+ * message written by the later ovs_abort_valist(). */
+ module->levels[VLF_CONSOLE] = VLL_OFF;
+
+ vlog_valist(module, VLL_EMER, message, args);
+ ovs_abort_valist(0, message, args);
+}
+
+/* Logs 'message' to 'module' at maximum verbosity, then calls abort(). Always
+ * writes the message to stderr, even if the console facility is disabled.
+ *
+ * Choose this function instead of vlog_fatal() if the daemon monitoring
+ * facility should automatically restart the current daemon. */
+void
+vlog_abort(const struct vlog_module *module, const char *message, ...)
+{
+ va_list args;
+
+ va_start(args, message);
+ vlog_abort_valist(module, message, args);
+ va_end(args);
+}
+
bool
vlog_should_drop(const struct vlog_module *module, enum vlog_level level,
struct vlog_rate_limit *rl)
return true;
}
- if (rl->tokens < VLOG_MSG_TOKENS) {
+ if (!token_bucket_withdraw(&rl->token_bucket, VLOG_MSG_TOKENS)) {
time_t now = time_now();
- if (rl->last_fill > now) {
- /* Last filled in the future? Time must have gone backward, or
- * 'rl' has not been used before. */
- rl->tokens = rl->burst;
- } else if (rl->last_fill < now) {
- unsigned int add = sat_mul(rl->rate, now - rl->last_fill);
- unsigned int tokens = sat_add(rl->tokens, add);
- rl->tokens = MIN(tokens, rl->burst);
- rl->last_fill = now;
- }
- if (rl->tokens < VLOG_MSG_TOKENS) {
- if (!rl->n_dropped) {
- rl->first_dropped = now;
- }
- rl->last_dropped = now;
- rl->n_dropped++;
- return true;
+ if (!rl->n_dropped) {
+ rl->first_dropped = now;
}
+ rl->last_dropped = now;
+ rl->n_dropped++;
+ return true;
}
- rl->tokens -= VLOG_MSG_TOKENS;
if (rl->n_dropped) {
time_t now = time_now();
" (default: %s/%s.log)\n",
ovs_logdir(), program_name);
}
+\f
+static bool vlog_async_inited = false;
+
+static worker_request_func vlog_async_write_request_cb;
+
+static void
+vlog_write_file(struct ds *s)
+{
+ if (worker_is_running()) {
+ worker_request(s->string, s->length,
+ &log_fd, vlog_async_inited ? 0 : 1,
+ vlog_async_write_request_cb, NULL, NULL);
+ vlog_async_inited = true;
+ } else {
+ ignore(write(log_fd, s->string, s->length));
+ }
+}
+
+static void
+vlog_update_async_log_fd(void)
+{
+ if (worker_is_running()) {
+ worker_request(NULL, 0, &log_fd, 1, vlog_async_write_request_cb,
+ NULL, NULL);
+ vlog_async_inited = true;
+ }
+}
+
+static void
+vlog_async_write_request_cb(struct ofpbuf *request,
+ const int *fd, size_t n_fds)
+{
+ if (n_fds > 0) {
+ if (log_fd >= 0) {
+ close(log_fd);
+ }
+ log_fd = *fd;
+ }
+
+ if (request->size > 0) {
+ ignore(write(log_fd, request->data, request->size));
+ }
+}
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdbool.h>
#include <time.h>
#include "compiler.h"
+#include "sat-math.h"
+#include "token-bucket.h"
#include "util.h"
#ifdef __cplusplus
enum vlog_level vlog_get_level_val(const char *name);
/* Facilities that we can log to. */
-#define VLOG_FACILITIES \
- VLOG_FACILITY(SYSLOG, "%05N|%c|%p|%m") \
- VLOG_FACILITY(CONSOLE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c|%p|%m") \
- VLOG_FACILITY(FILE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c|%p|%m")
+#define VLOG_FACILITIES \
+ VLOG_FACILITY(SYSLOG, "%05N|%c%T|%p|%m") \
+ VLOG_FACILITY(CONSOLE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m") \
+ VLOG_FACILITY(FILE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m")
enum vlog_facility {
#define VLOG_FACILITY(NAME, PATTERN) VLF_##NAME,
VLOG_FACILITIES
/* Rate-limiter for log messages. */
struct vlog_rate_limit {
- /* Configuration settings. */
- unsigned int rate; /* Tokens per second. */
- unsigned int burst; /* Max cumulative tokens credit. */
-
- /* Current status. */
- unsigned int tokens; /* Current number of tokens. */
- time_t last_fill; /* Last time tokens added. */
+ struct token_bucket token_bucket;
time_t first_dropped; /* Time first message was dropped. */
time_t last_dropped; /* Time of most recent message drop. */
unsigned int n_dropped; /* Number of messages dropped. */
};
-/* Number of tokens to emit a message. We add 'rate' tokens per second, which
- * is 60 times the unit used for 'rate', thus 60 tokens are required to emit
- * one message. */
-#define VLOG_MSG_TOKENS 60
+/* Number of tokens to emit a message. We add 'rate' tokens per millisecond,
+ * thus 60,000 tokens are required to emit one message per minute. */
+#define VLOG_MSG_TOKENS (60 * 1000)
/* Initializer for a struct vlog_rate_limit, to set up a maximum rate of RATE
* messages per minute and a maximum burst size of BURST messages. */
-#define VLOG_RATE_LIMIT_INIT(RATE, BURST) \
- { \
- RATE, /* rate */ \
- (MIN(BURST, UINT_MAX / VLOG_MSG_TOKENS) \
- * VLOG_MSG_TOKENS), /* burst */ \
- 0, /* tokens */ \
- 0, /* last_fill */ \
- 0, /* first_dropped */ \
- 0, /* last_dropped */ \
- 0, /* n_dropped */ \
+#define VLOG_RATE_LIMIT_INIT(RATE, BURST) \
+ { \
+ TOKEN_BUCKET_INIT(RATE, SAT_MUL(BURST, VLOG_MSG_TOKENS)), \
+ 0, /* first_dropped */ \
+ 0, /* last_dropped */ \
+ 0, /* n_dropped */ \
}
/* Configuring how each module logs messages. */
void vlog_fatal_valist(const struct vlog_module *, const char *format, va_list)
PRINTF_FORMAT (2, 0) NO_RETURN;
+void vlog_abort(const struct vlog_module *, const char *format, ...)
+ PRINTF_FORMAT (2, 3) NO_RETURN;
+void vlog_abort_valist(const struct vlog_module *, const char *format, va_list)
+ PRINTF_FORMAT (2, 0) NO_RETURN;
+
void vlog_rate_limit(const struct vlog_module *, enum vlog_level,
struct vlog_rate_limit *, const char *, ...)
PRINTF_FORMAT (4, 5);
* Guaranteed to preserve errno.
*/
#define VLOG_FATAL(...) vlog_fatal(THIS_MODULE, __VA_ARGS__)
+#define VLOG_ABORT(...) vlog_abort(THIS_MODULE, __VA_ARGS__)
#define VLOG_EMER(...) VLOG(VLL_EMER, __VA_ARGS__)
#define VLOG_ERR(...) VLOG(VLL_ERR, __VA_ARGS__)
#define VLOG_WARN(...) VLOG(VLL_WARN, __VA_ARGS__)
.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]
--- /dev/null
+/* Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "worker.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "command-line.h"
+#include "daemon.h"
+#include "ofpbuf.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "util.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(worker);
+
+/* Header for an RPC request. */
+struct worker_request {
+ size_t request_len; /* Length of the payload in bytes. */
+ worker_request_func *request_cb; /* Function to call in worker process. */
+ worker_reply_func *reply_cb; /* Function to call in main process. */
+ void *reply_aux; /* Auxiliary data for 'reply_cb'. */
+};
+
+/* Header for an RPC reply. */
+struct worker_reply {
+ size_t reply_len; /* Length of the payload in bytes. */
+ worker_reply_func *reply_cb; /* Function to call in main process. */
+ void *reply_aux; /* Auxiliary data for 'reply_cb'. */
+};
+
+/* Receive buffer for a RPC request or reply. */
+struct rxbuf {
+ /* Header. */
+ struct ofpbuf header; /* Header data. */
+ int fds[SOUTIL_MAX_FDS]; /* File descriptors. */
+ size_t n_fds;
+
+ /* Payload. */
+ struct ofpbuf payload; /* Payload data. */
+};
+
+static int client_sock = -1;
+static struct rxbuf client_rx;
+
+static void rxbuf_init(struct rxbuf *);
+static void rxbuf_clear(struct rxbuf *);
+static int rxbuf_run(struct rxbuf *, int sock, size_t header_len);
+
+static struct iovec *prefix_iov(void *data, size_t len,
+ const struct iovec *iovs, size_t n_iovs);
+
+static void worker_broke(void);
+
+static void worker_main(int fd) NO_RETURN;
+
+/* Starts a worker process as a subprocess of the current process. Currently
+ * only a single worker process is supported, so this function may only be
+ * called once.
+ *
+ * The client should call worker_run() and worker_wait() from its main loop.
+ *
+ * Call this function between daemonize_start() and daemonize_complete(). */
+void
+worker_start(void)
+{
+ int work_fds[2];
+
+ assert(client_sock < 0);
+
+ /* Create non-blocking socket pair. */
+ xsocketpair(AF_UNIX, SOCK_STREAM, 0, work_fds);
+ xset_nonblocking(work_fds[0]);
+ xset_nonblocking(work_fds[1]);
+
+ if (!fork_and_clean_up()) {
+ /* In child (worker) process. */
+ daemonize_post_detach();
+ close(work_fds[0]);
+ worker_main(work_fds[1]);
+ NOT_REACHED();
+ }
+
+ /* In parent (main) process. */
+ close(work_fds[1]);
+ client_sock = work_fds[0];
+ rxbuf_init(&client_rx);
+}
+
+/* Returns true if this process has started a worker and the worker is not
+ * known to have malfunctioned. */
+bool
+worker_is_running(void)
+{
+ return client_sock >= 0;
+}
+
+/* If a worker process was started, processes RPC replies from it, calling the
+ * registered 'reply_cb' callbacks.
+ *
+ * If the worker process died or malfunctioned, aborts. */
+void
+worker_run(void)
+{
+ if (worker_is_running()) {
+ int error;
+
+ error = rxbuf_run(&client_rx, client_sock,
+ sizeof(struct worker_reply));
+ if (!error) {
+ struct worker_reply *reply = client_rx.header.data;
+ reply->reply_cb(&client_rx.payload, client_rx.fds,
+ client_rx.n_fds, reply->reply_aux);
+ rxbuf_clear(&client_rx);
+ } else if (error != EAGAIN) {
+ worker_broke();
+ VLOG_ABORT("receive from worker failed (%s)",
+ ovs_retval_to_string(error));
+ }
+ }
+}
+
+/* Causes the poll loop to wake up if we need to process RPC replies. */
+void
+worker_wait(void)
+{
+ if (worker_is_running()) {
+ poll_fd_wait(client_sock, POLLIN);
+ }
+}
+\f
+/* Interface for main process to interact with the worker. */
+
+/* Sends an RPC request to the worker process. The worker process will call
+ * 'request_cb' passing the 'size' (zero or more) bytes of data in 'data' as
+ * arguments as well as the 'n_fds' (SOUTIL_MAX_FDS or fewer) file descriptors
+ * in 'fds'.
+ *
+ * If and only if 'reply_cb' is nonnull, 'request_cb' must call worker_reply()
+ * or worker_reply_iovec() with a reply. The main process will later call
+ * 'reply_cb' with the reply data (if any) and file descriptors (if any).
+ *
+ * 'request_cb' receives copies (as if by dup()) of the file descriptors in
+ * fds[]. 'request_cb' takes ownership of these copies, and the caller of
+ * worker_request() retains its ownership of the originals.
+ *
+ * This function may block until the RPC request has been sent (if the socket
+ * buffer fills up) but it does not wait for the reply (if any). If this
+ * function blocks, it may invoke reply callbacks for previous requests.
+ *
+ * The worker process executes RPC requests in strict order of submission and
+ * runs each request to completion before beginning the next request. The main
+ * process invokes reply callbacks in strict order of request submission. */
+void
+worker_request(const void *data, size_t size,
+ const int fds[], size_t n_fds,
+ worker_request_func *request_cb,
+ worker_reply_func *reply_cb, void *aux)
+{
+ if (size > 0) {
+ struct iovec iov;
+
+ iov.iov_base = (void *) data;
+ iov.iov_len = size;
+ worker_request_iovec(&iov, 1, fds, n_fds, request_cb, reply_cb, aux);
+ } else {
+ worker_request_iovec(NULL, 0, fds, n_fds, request_cb, reply_cb, aux);
+ }
+}
+
+static int
+worker_send_iovec(const struct iovec iovs[], size_t n_iovs,
+ const int fds[], size_t n_fds)
+{
+ size_t sent = 0;
+
+ for (;;) {
+ int error;
+
+ /* Try to send the rest of the request. */
+ error = send_iovec_and_fds_fully(client_sock, iovs, n_iovs,
+ fds, n_fds, sent, &sent);
+ if (error != EAGAIN) {
+ return error;
+ }
+
+ /* Process replies to avoid deadlock. */
+ worker_run();
+
+ poll_fd_wait(client_sock, POLLIN | POLLOUT);
+ poll_block();
+ }
+}
+
+/* Same as worker_request() except that the data to send is specified as an
+ * array of iovecs. */
+void
+worker_request_iovec(const struct iovec iovs[], size_t n_iovs,
+ const int fds[], size_t n_fds,
+ worker_request_func *request_cb,
+ worker_reply_func *reply_cb, void *aux)
+{
+ struct worker_request rq;
+ struct iovec *all_iovs;
+ int error;
+
+ assert(worker_is_running());
+
+ rq.request_len = iovec_len(iovs, n_iovs);
+ rq.request_cb = request_cb;
+ rq.reply_cb = reply_cb;
+ rq.reply_aux = aux;
+
+ all_iovs = prefix_iov(&rq, sizeof rq, iovs, n_iovs);
+ error = worker_send_iovec(all_iovs, n_iovs + 1, fds, n_fds);
+ if (error) {
+ worker_broke();
+ VLOG_ABORT("send failed (%s)", strerror(error));
+ }
+ free(all_iovs);
+}
+
+/* Closes the client socket, if any, so that worker_is_running() will return
+ * false.
+ *
+ * The client does this just before aborting if the worker process dies or
+ * malfunctions, to prevent the logging subsystem from trying to use the
+ * worker to log the failure. */
+static void
+worker_broke(void)
+{
+ if (client_sock >= 0) {
+ close(client_sock);
+ client_sock = -1;
+ }
+}
+\f
+/* Interfaces for RPC implementations (running in the worker process). */
+
+static int server_sock = -1;
+static bool expect_reply;
+static struct worker_request request;
+
+/* When a call to worker_request() or worker_request_iovec() provides a
+ * 'reply_cb' callback, the 'request_cb' implementation must call this function
+ * to send its reply. The main process will call 'reply_cb' passing the
+ * 'size' (zero or more) bytes of data in 'data' as arguments as well as the
+ * 'n_fds' (SOUTIL_MAX_FDS or fewer) file descriptors in 'fds'.
+ *
+ * If a call to worker_request() or worker_request_iovec() provides no
+ * 'reply_cb' callback, the 'request_cb' implementation must not call this
+ * function.
+ *
+ * 'reply_cb' receives copies (as if by dup()) of the file descriptors in
+ * fds[]. 'reply_cb' takes ownership of these copies, and the caller of
+ * worker_reply() retains its ownership of the originals.
+ *
+ * This function blocks until the RPC reply has been sent (if the socket buffer
+ * fills up) but it does not wait for the main process to receive or to process
+ * the reply. */
+void
+worker_reply(const void *data, size_t size, const int fds[], size_t n_fds)
+{
+ if (size > 0) {
+ struct iovec iov;
+
+ iov.iov_base = (void *) data;
+ iov.iov_len = size;
+ worker_reply_iovec(&iov, 1, fds, n_fds);
+ } else {
+ worker_reply_iovec(NULL, 0, fds, n_fds);
+ }
+}
+
+/* Same as worker_reply() except that the data to send is specified as an array
+ * of iovecs. */
+void
+worker_reply_iovec(const struct iovec *iovs, size_t n_iovs,
+ const int fds[], size_t n_fds)
+{
+ struct worker_reply reply;
+ struct iovec *all_iovs;
+ int error;
+
+ assert(expect_reply);
+ expect_reply = false;
+
+ reply.reply_len = iovec_len(iovs, n_iovs);
+ reply.reply_cb = request.reply_cb;
+ reply.reply_aux = request.reply_aux;
+
+ all_iovs = prefix_iov(&reply, sizeof reply, iovs, n_iovs);
+
+ error = send_iovec_and_fds_fully_block(server_sock, all_iovs, n_iovs + 1,
+ fds, n_fds);
+ if (error == EPIPE) {
+ /* Parent probably died. Continue processing any RPCs still buffered,
+ * to avoid missing log messages. */
+ VLOG_INFO("send failed (%s)", strerror(error));
+ } else if (error) {
+ VLOG_ABORT("send failed (%s)", strerror(error));
+ }
+
+ free(all_iovs);
+}
+
+static void
+worker_main(int fd)
+{
+ struct rxbuf rx;
+
+ server_sock = fd;
+
+ subprogram_name = "worker";
+ proctitle_set("%s: worker process for pid %lu",
+ program_name, (unsigned long int) getppid());
+ VLOG_INFO("worker process started");
+
+ rxbuf_init(&rx);
+ for (;;) {
+ int error;
+
+ error = rxbuf_run(&rx, server_sock, sizeof(struct worker_request));
+ if (!error) {
+ request = *(struct worker_request *) rx.header.data;
+
+ expect_reply = request.reply_cb != NULL;
+ request.request_cb(&rx.payload, rx.fds, rx.n_fds);
+ assert(!expect_reply);
+
+ rxbuf_clear(&rx);
+ } else if (error == EOF && !rx.header.size) {
+ /* Main process closed the IPC socket. Exit cleanly. */
+ break;
+ } else if (error != EAGAIN) {
+ VLOG_ABORT("RPC receive failed (%s)", strerror(error));
+ }
+
+ poll_fd_wait(server_sock, POLLIN);
+ poll_block();
+ }
+
+ VLOG_INFO("worker process exiting");
+ exit(0);
+}
+\f
+static void
+rxbuf_init(struct rxbuf *rx)
+{
+ ofpbuf_init(&rx->header, 0);
+ rx->n_fds = 0;
+ ofpbuf_init(&rx->payload, 0);
+}
+
+static void
+rxbuf_clear(struct rxbuf *rx)
+{
+ ofpbuf_clear(&rx->header);
+ rx->n_fds = 0;
+ ofpbuf_clear(&rx->payload);
+}
+
+static int
+rxbuf_run(struct rxbuf *rx, int sock, size_t header_len)
+{
+ for (;;) {
+ if (!rx->header.size) {
+ int retval;
+
+ ofpbuf_clear(&rx->header);
+ ofpbuf_prealloc_tailroom(&rx->header, header_len);
+
+ retval = recv_data_and_fds(sock, rx->header.data, header_len,
+ rx->fds, &rx->n_fds);
+ if (retval <= 0) {
+ return retval ? -retval : EOF;
+ }
+ rx->header.size += retval;
+ } else if (rx->header.size < header_len) {
+ size_t bytes_read;
+ int error;
+
+ error = read_fully(sock, ofpbuf_tail(&rx->header),
+ header_len - rx->header.size, &bytes_read);
+ rx->header.size += bytes_read;
+ if (error) {
+ return error;
+ }
+ } else {
+ size_t payload_len = *(size_t *) rx->header.data;
+
+ if (rx->payload.size < payload_len) {
+ size_t left = payload_len - rx->payload.size;
+ size_t bytes_read;
+ int error;
+
+ ofpbuf_prealloc_tailroom(&rx->payload, left);
+ error = read_fully(sock, ofpbuf_tail(&rx->payload), left,
+ &bytes_read);
+ rx->payload.size += bytes_read;
+ if (error) {
+ return error;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ return EAGAIN;
+}
+
+static struct iovec *
+prefix_iov(void *data, size_t len, const struct iovec *iovs, size_t n_iovs)
+{
+ struct iovec *dst;
+
+ dst = xmalloc((n_iovs + 1) * sizeof *dst);
+ dst[0].iov_base = data;
+ dst[0].iov_len = len;
+ memcpy(dst + 1, iovs, n_iovs * sizeof *iovs);
+
+ return dst;
+}
--- /dev/null
+/* Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WORKER_H
+#define WORKER_H 1
+
+/* Worker processes.
+ *
+ * Thes functions allow an OVS daemon to fork off a "worker process" to do
+ * tasks that may unavoidably block in the kernel. The worker executes remote
+ * procedure calls on behalf of the main process.
+ *
+ * Tasks that may unavoidably block in the kernel include writes to regular
+ * files, sends to Generic Netlink sockets (which as of this writing use a
+ * global lock), and other unusual operations.
+ *
+ * The worker functions *will* block if the finite buffer between a main
+ * process and its worker process fills up.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "compiler.h"
+
+struct iovec;
+struct ofpbuf;
+
+/* The main process calls this function to start a worker. */
+void worker_start(void);
+
+/* Interface for main process to interact with the worker. */
+typedef void worker_request_func(struct ofpbuf *request,
+ const int fds[], size_t n_fds);
+typedef void worker_reply_func(struct ofpbuf *reply,
+ const int fds[], size_t n_fds, void *aux);
+
+bool worker_is_running(void);
+void worker_run(void);
+void worker_wait(void);
+
+void worker_request(const void *data, size_t size,
+ const int fds[], size_t n_fds,
+ worker_request_func *request_cb,
+ worker_reply_func *reply_cb, void *aux);
+void worker_request_iovec(const struct iovec *iovs, size_t n_iovs,
+ const int fds[], size_t n_fds,
+ worker_request_func *request_cb,
+ worker_reply_func *reply_cb, void *aux);
+
+/* Interfaces for RPC implementations (running in the worker process). */
+void worker_reply(const void *data, size_t size,
+ const int fds[], size_t n_fds);
+void worker_reply_iovec(const struct iovec *iovs, size_t n_iovs,
+ const int fds[], size_t n_fds);
+
+#endif /* worker.h */
# -*- autoconf -*-
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
[AC_HELP_STRING([--enable-coverage],
[Enable gcov coverage tool.])],
[case "${enableval}" in
- (lcov|yes) coverage=true ;;
+ (yes) coverage=true ;;
(no) coverage=false ;;
(*) AC_MSG_ERROR([bad value ${enableval} for --enable-coverage]) ;;
esac],
[LOGDIR='${localstatedir}/log/${PACKAGE}'])
AC_SUBST([LOGDIR])])
+dnl Checks for the directory in which to store the Open vSwitch database.
+AC_DEFUN([OVS_CHECK_DBDIR],
+ [AC_ARG_WITH(
+ [dbdir],
+ AC_HELP_STRING([--with-dbdir=DIR],
+ [directory used for conf.db [[SYSCONFDIR/PACKAGE]]]),
+ [DBDIR=$withval],
+ [DBDIR='${sysconfdir}/${PACKAGE}'])
+ AC_SUBST([DBDIR])])
+
dnl Defines HAVE_BACKTRACE if backtrace() is declared in <execinfo.h>
dnl and exists in libc.
AC_DEFUN([OVS_CHECK_BACKTRACE],
lib/coverage-unixctl.man \
lib/daemon-syn.man \
lib/daemon.man \
+ lib/memory-unixctl.man \
lib/ssl-bootstrap-syn.man \
lib/ssl-bootstrap.man \
lib/ssl-syn.man \
lib/coverage-unixctl.man:
lib/daemon-syn.man:
lib/daemon.man:
+lib/memory-unixctl.man:
lib/ssl-bootstrap-syn.man:
lib/ssl-bootstrap.man:
lib/ssl-syn.man:
lib/common.man:
lib/vlog.man:
+utilities/ovs-l3ping.8: \
+ utilities/ovs-l3ping.8.in \
+ lib/common-syn.man \
+ lib/common.man
+utilities/ovs-l3ping.8.in:
+lib/common-syn.man:
+lib/common.man:
+
utilities/ovs-ofctl.8: \
utilities/ovs-ofctl.8.in \
lib/common.man \
lib/common.man \
lib/daemon.man \
lib/leak-checker.man \
+ lib/memory-unixctl.man \
lib/ssl-bootstrap.man \
lib/ssl.man \
lib/stress-unixctl.man \
lib/common.man:
lib/daemon.man:
lib/leak-checker.man:
+lib/memory-unixctl.man:
lib/ssl-bootstrap.man:
lib/ssl.man:
lib/stress-unixctl.man:
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "fail-open.h"
#include "in-band.h"
#include "odp-util.h"
+#include "ofp-actions.h"
+#include "ofp-msgs.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "ofproto-provider.h"
#include "pktbuf.h"
#include "rconn.h"
#include "shash.h"
+#include "simap.h"
#include "stream.h"
#include "timeval.h"
#include "vconn.h"
* that the message might be generated, a 0-bit disables it. */
uint32_t master_async_config[OAM_N_TYPES]; /* master, other */
uint32_t slave_async_config[OAM_N_TYPES]; /* slave */
+
+ /* Flow monitors. */
+ struct hmap monitors; /* Contains "struct ofmonitor"s. */
+ struct list updates; /* List of "struct ofpbuf"s. */
+ bool sent_abbrev_update; /* Does 'updates' contain NXFME_ABBREV? */
+ struct rconn_packet_counter *monitor_counter;
+ uint64_t monitor_paused;
};
static struct ofconn *ofconn_create(struct connmgr *, struct rconn *,
static void update_in_band_remotes(struct connmgr *);
static void add_snooper(struct connmgr *, struct vconn *);
+static void ofmonitor_run(struct connmgr *);
+static void ofmonitor_wait(struct connmgr *);
/* Creates and returns a new connection manager owned by 'ofproto'. 'name' is
* a name for the ofproto suitable for using in log messages.
LIST_FOR_EACH_SAFE (ofconn, next_ofconn, node, &mgr->all_conns) {
ofconn_run(ofconn, handle_openflow);
}
+ ofmonitor_run(mgr);
/* Fail-open maintenance. Do this after processing the ofconns since
* fail-open checks the status of the controller rconn. */
LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
ofconn_wait(ofconn, handling_openflow);
}
+ ofmonitor_wait(mgr);
if (handling_openflow && mgr->in_band) {
in_band_wait(mgr->in_band);
}
}
}
+/* 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)
SHASH_FOR_EACH (node, info) {
struct ofproto_controller_info *cinfo = node->data;
while (cinfo->pairs.n) {
- free((char *) cinfo->pairs.values[--cinfo->pairs.n]);
+ free(CONST_CAST(char *, cinfo->pairs.values[--cinfo->pairs.n]));
}
free(cinfo);
}
if (!vconn_verify_name(c->target)) {
if (!find_controller_by_target(mgr, c->target)) {
+ VLOG_INFO("%s: added primary controller \"%s\"",
+ mgr->name, c->target);
add_controller(mgr, c->target, c->dscp);
}
} else if (!pvconn_verify_name(c->target)) {
if (!ofservice_lookup(mgr, c->target)) {
+ VLOG_INFO("%s: added service controller \"%s\"",
+ mgr->name, c->target);
ofservice_create(mgr, c->target, c->dscp);
}
} else {
/* Delete controllers that are no longer configured.
* Update configuration of all now-existing controllers. */
HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, hmap_node, &mgr->controllers) {
+ const char *target = ofconn_get_target(ofconn);
struct ofproto_controller *c;
- c = shash_find_data(&new_controllers, ofconn_get_target(ofconn));
+ c = shash_find_data(&new_controllers, target);
if (!c) {
+ VLOG_INFO("%s: removed primary controller \"%s\"",
+ mgr->name, target);
ofconn_destroy(ofconn);
} else {
ofconn_reconfigure(ofconn, c);
/* Delete services that are no longer configured.
* Update configuration of all now-existing services. */
HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, node, &mgr->services) {
+ const char *target = pvconn_get_name(ofservice->pvconn);
struct ofproto_controller *c;
- c = shash_find_data(&new_controllers,
- pvconn_get_name(ofservice->pvconn));
+ c = shash_find_data(&new_controllers, target);
if (!c) {
+ VLOG_INFO("%s: removed service controller \"%s\"",
+ mgr->name, target);
ofservice_destroy(mgr, ofservice);
} else {
ofservice_reconfigure(ofservice, c);
static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(10, 10);
if (!VLOG_DROP_INFO(&err_rl)) {
- const struct ofputil_msg_type *type;
const char *type_name;
size_t request_len;
+ enum ofpraw raw;
request_len = ntohs(request->length);
- type_name = (!ofputil_decode_msg_type_partial(request,
- MIN(64, request_len),
- &type)
- ? ofputil_msg_type_name(type)
+ type_name = (!ofpraw_decode_partial(&raw, request,
+ MIN(64, request_len))
+ ? ofpraw_get_name(raw)
: "invalid");
VLOG_INFO("%s: sending %s error reply to %s message",
list_init(&ofconn->opgroups);
+ hmap_init(&ofconn->monitors);
+ list_init(&ofconn->updates);
+
ofconn_flush(ofconn);
return ofconn;
static void
ofconn_flush(struct ofconn *ofconn)
{
+ struct ofmonitor *monitor, *next_monitor;
int i;
ofconn->role = NX_ROLE_OTHER;
memset(ofconn->slave_async_config, 0,
sizeof ofconn->slave_async_config);
}
+
+ HMAP_FOR_EACH_SAFE (monitor, next_monitor, ofconn_node,
+ &ofconn->monitors) {
+ ofmonitor_destroy(monitor);
+ }
+ rconn_packet_counter_destroy(ofconn->monitor_counter);
+ ofconn->monitor_counter = rconn_packet_counter_create();
+ ofpbuf_list_delete(&ofconn->updates); /* ...but it should be empty. */
}
static void
rconn_packet_counter_destroy(ofconn->packet_in_counter);
rconn_packet_counter_destroy(ofconn->reply_counter);
pktbuf_destroy(ofconn->pktbuf);
+ rconn_packet_counter_destroy(ofconn->monitor_counter);
free(ofconn);
}
rconn_set_probe_interval(ofconn->rconn, probe_interval);
ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit);
+
+ /* If dscp value changed reconnect. */
+ if (c->dscp != rconn_get_dscp(ofconn->rconn)) {
+ rconn_set_dscp(ofconn->rconn, c->dscp);
+ rconn_reconnect(ofconn->rconn);
+ }
}
/* Returns true if it makes sense for 'ofconn' to receive and process OpenFlow
static bool
ofconn_may_recv(const struct ofconn *ofconn)
{
- int count = rconn_packet_counter_read (ofconn->reply_counter);
+ int count = ofconn->reply_counter->n_packets;
return (!ofconn->blocked || ofconn->retry) && count < OFCONN_REPLY_MAX;
}
ofconn_send(const struct ofconn *ofconn, struct ofpbuf *msg,
struct rconn_packet_counter *counter)
{
- update_openflow_length(msg);
+ ofpmsg_update_length(msg);
rconn_send(ofconn->rconn, msg, counter);
}
\f
pin.send_len = pin.packet_len;
} else {
/* Caller should have initialized 'send_len' to 'max_len' specified in
- * struct ofp_action_output. */
+ * output action. */
}
if (pin.buffer_id != UINT32_MAX) {
pin.send_len = MIN(pin.send_len, ofconn->miss_send_len);
* while (until a later call to pinsched_run()). */
pinsched_send(ofconn->schedulers[pin.reason == OFPR_NO_MATCH ? 0 : 1],
pin.fmd.in_port,
- ofputil_encode_packet_in(&pin, ofconn->packet_in_format),
+ ofputil_encode_packet_in(&pin, ofconn->protocol,
+ ofconn->packet_in_format),
do_send_packet_in, ofconn);
}
\f
* traffic until a controller has been defined and it tells us to do so. */
if (!connmgr_has_controllers(mgr)
&& mgr->fail_mode == OFPROTO_FAIL_STANDALONE) {
- union ofp_action action;
+ struct ofpbuf ofpacts;
struct cls_rule rule;
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT10_OUTPUT);
- action.output.len = htons(sizeof action);
- action.output.port = htons(OFPP_NORMAL);
+ ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
+ ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
+ ofpact_pad(&ofpacts);
+
cls_rule_init_catchall(&rule, 0);
- ofproto_add_flow(mgr->ofproto, &rule, &action, 1);
+ ofproto_add_flow(mgr->ofproto, &rule, ofpacts.data, ofpacts.size);
+
+ ofpbuf_uninit(&ofpacts);
}
}
\f
}
return NULL;
}
+\f
+/* Flow monitors (NXST_FLOW_MONITOR). */
+
+/* A counter incremented when something significant happens to an OpenFlow
+ * rule.
+ *
+ * - When a rule is added, its 'add_seqno' and 'modify_seqno' are set to
+ * the current value (which is then incremented).
+ *
+ * - When a rule is modified, its 'modify_seqno' is set to the current
+ * value (which is then incremented).
+ *
+ * Thus, by comparing an old value of monitor_seqno against a rule's
+ * 'add_seqno', one can tell whether the rule was added before or after the old
+ * value was read, and similarly for 'modify_seqno'.
+ *
+ * 32 bits should normally be sufficient (and would be nice, to save space in
+ * each rule) but then we'd have to have some special cases for wraparound.
+ *
+ * We initialize monitor_seqno to 1 to allow 0 to be used as an invalid
+ * value. */
+static uint64_t monitor_seqno = 1;
+
+COVERAGE_DEFINE(ofmonitor_pause);
+COVERAGE_DEFINE(ofmonitor_resume);
+
+enum ofperr
+ofmonitor_create(const struct ofputil_flow_monitor_request *request,
+ struct ofconn *ofconn, struct ofmonitor **monitorp)
+{
+ struct ofmonitor *m;
+
+ *monitorp = NULL;
+
+ m = ofmonitor_lookup(ofconn, request->id);
+ if (m) {
+ return OFPERR_NXBRC_FM_DUPLICATE_ID;
+ }
+
+ m = xmalloc(sizeof *m);
+ m->ofconn = ofconn;
+ hmap_insert(&ofconn->monitors, &m->ofconn_node, hash_int(request->id, 0));
+ m->id = request->id;
+ m->flags = request->flags;
+ m->out_port = request->out_port;
+ m->table_id = request->table_id;
+ m->match = request->match;
+
+ *monitorp = m;
+ return 0;
+}
+
+struct ofmonitor *
+ofmonitor_lookup(struct ofconn *ofconn, uint32_t id)
+{
+ struct ofmonitor *m;
+
+ HMAP_FOR_EACH_IN_BUCKET (m, ofconn_node, hash_int(id, 0),
+ &ofconn->monitors) {
+ if (m->id == id) {
+ return m;
+ }
+ }
+ return NULL;
+}
+
+void
+ofmonitor_destroy(struct ofmonitor *m)
+{
+ if (m) {
+ hmap_remove(&m->ofconn->monitors, &m->ofconn_node);
+ free(m);
+ }
+}
+
+void
+ofmonitor_report(struct connmgr *mgr, struct rule *rule,
+ enum nx_flow_update_event event,
+ enum ofp_flow_removed_reason reason,
+ const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid)
+{
+ enum nx_flow_monitor_flags update;
+ struct ofconn *ofconn;
+
+ switch (event) {
+ case NXFME_ADDED:
+ update = NXFMF_ADD;
+ rule->add_seqno = rule->modify_seqno = monitor_seqno++;
+ break;
+
+ case NXFME_DELETED:
+ update = NXFMF_DELETE;
+ break;
+
+ case NXFME_MODIFIED:
+ update = NXFMF_MODIFY;
+ rule->modify_seqno = monitor_seqno++;
+ break;
+
+ default:
+ case NXFME_ABBREV:
+ NOT_REACHED();
+ }
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ enum nx_flow_monitor_flags flags = 0;
+ struct ofmonitor *m;
+
+ if (ofconn->monitor_paused) {
+ /* Only send NXFME_DELETED notifications for flows that were added
+ * before we paused. */
+ if (event != NXFME_DELETED
+ || rule->add_seqno > ofconn->monitor_paused) {
+ continue;
+ }
+ }
+
+ HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
+ if (m->flags & update
+ && (m->table_id == 0xff || m->table_id == rule->table_id)
+ && ofoperation_has_out_port(rule->pending, m->out_port)
+ && cls_rule_is_loose_match(&rule->cr, &m->match)) {
+ flags |= m->flags;
+ }
+ }
+
+ if (flags) {
+ if (list_is_empty(&ofconn->updates)) {
+ ofputil_start_flow_update(&ofconn->updates);
+ ofconn->sent_abbrev_update = false;
+ }
+
+ if (ofconn != abbrev_ofconn || ofconn->monitor_paused) {
+ struct ofputil_flow_update fu;
+
+ fu.event = event;
+ fu.reason = event == NXFME_DELETED ? reason : 0;
+ fu.idle_timeout = rule->idle_timeout;
+ fu.hard_timeout = rule->hard_timeout;
+ fu.table_id = rule->table_id;
+ fu.cookie = rule->flow_cookie;
+ fu.match = &rule->cr;
+ if (flags & NXFMF_ACTIONS) {
+ fu.ofpacts = rule->ofpacts;
+ fu.ofpacts_len = rule->ofpacts_len;
+ } else {
+ fu.ofpacts = NULL;
+ fu.ofpacts_len = 0;
+ }
+ ofputil_append_flow_update(&fu, &ofconn->updates);
+ } else if (!ofconn->sent_abbrev_update) {
+ struct ofputil_flow_update fu;
+
+ fu.event = NXFME_ABBREV;
+ fu.xid = abbrev_xid;
+ ofputil_append_flow_update(&fu, &ofconn->updates);
+
+ ofconn->sent_abbrev_update = true;
+ }
+ }
+ }
+}
+
+void
+ofmonitor_flush(struct connmgr *mgr)
+{
+ struct ofconn *ofconn;
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ struct ofpbuf *msg, *next;
+
+ LIST_FOR_EACH_SAFE (msg, next, list_node, &ofconn->updates) {
+ list_remove(&msg->list_node);
+ ofconn_send(ofconn, msg, ofconn->monitor_counter);
+ if (!ofconn->monitor_paused
+ && ofconn->monitor_counter->n_bytes > 128 * 1024) {
+ struct ofpbuf *pause;
+
+ COVERAGE_INC(ofmonitor_pause);
+ ofconn->monitor_paused = monitor_seqno++;
+ pause = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_PAUSED,
+ OFP10_VERSION, htonl(0), 0);
+ ofconn_send(ofconn, pause, ofconn->monitor_counter);
+ }
+ }
+ }
+}
+
+static void
+ofmonitor_resume(struct ofconn *ofconn)
+{
+ struct ofpbuf *resumed;
+ struct ofmonitor *m;
+ struct list rules;
+ struct list msgs;
+
+ list_init(&rules);
+ HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
+ ofmonitor_collect_resume_rules(m, ofconn->monitor_paused, &rules);
+ }
+
+ list_init(&msgs);
+ ofmonitor_compose_refresh_updates(&rules, &msgs);
+
+ resumed = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_RESUMED, OFP10_VERSION,
+ htonl(0), 0);
+ list_push_back(&msgs, &resumed->list_node);
+ ofconn_send_replies(ofconn, &msgs);
+
+ ofconn->monitor_paused = 0;
+}
+
+static void
+ofmonitor_run(struct connmgr *mgr)
+{
+ struct ofconn *ofconn;
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ if (ofconn->monitor_paused && !ofconn->monitor_counter->n_packets) {
+ COVERAGE_INC(ofmonitor_resume);
+ ofmonitor_resume(ofconn);
+ }
+ }
+}
+
+static void
+ofmonitor_wait(struct connmgr *mgr)
+{
+ struct ofconn *ofconn;
+
+ LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+ if (ofconn->monitor_paused && !ofconn->monitor_counter->n_packets) {
+ poll_immediate_wake();
+ }
+ }
+}
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#ifndef CONNMGR_H
#define CONNMGR_H 1
+#include "classifier.h"
#include "hmap.h"
#include "list.h"
#include "ofp-errors.h"
struct ofputil_flow_removed;
struct ofputil_packet_in;
struct ofputil_phy_port;
+struct rule;
+struct simap;
struct sset;
/* ofproto supports two kinds of OpenFlow connections:
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 *);
/* Fail-open and in-band implementation. */
void connmgr_flushed(struct connmgr *);
+/* A flow monitor managed by NXST_FLOW_MONITOR and related requests. */
+struct ofmonitor {
+ struct ofconn *ofconn; /* Owning 'ofconn'. */
+ struct hmap_node ofconn_node; /* In ofconn's 'monitors' hmap. */
+ uint32_t id;
+
+ enum nx_flow_monitor_flags flags;
+
+ /* Matching. */
+ uint16_t out_port;
+ uint8_t table_id;
+ struct cls_rule match;
+};
+
+struct ofputil_flow_monitor_request;
+
+enum ofperr ofmonitor_create(const struct ofputil_flow_monitor_request *,
+ struct ofconn *, struct ofmonitor **);
+struct ofmonitor *ofmonitor_lookup(struct ofconn *, uint32_t id);
+void ofmonitor_destroy(struct ofmonitor *);
+
+void ofmonitor_report(struct connmgr *, struct rule *,
+ enum nx_flow_update_event, enum ofp_flow_removed_reason,
+ const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid);
+void ofmonitor_flush(struct connmgr *);
+
+void ofmonitor_collect_resume_rules(struct ofmonitor *, uint64_t seqno,
+ struct list *rules);
+void ofmonitor_compose_refresh_updates(struct list *rules, struct list *msgs);
+
#endif /* connmgr.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "flow.h"
#include "mac-learning.h"
#include "odp-util.h"
+#include "ofp-actions.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "ofproto.h"
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;
int disconn_secs = connmgr_failure_duration(fo->connmgr);
bool open = disconn_secs >= trigger_duration(fo);
if (open) {
- union ofp_action action;
+ struct ofpbuf ofpacts;
struct cls_rule rule;
/* Set up a flow that matches every packet and directs them to
* OFPP_NORMAL. */
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT10_OUTPUT);
- action.output.len = htons(sizeof action);
- action.output.port = htons(OFPP_NORMAL);
+ ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
+ ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
+ ofpact_pad(&ofpacts);
cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY);
- ofproto_add_flow(fo->ofproto, &rule, &action, 1);
+ ofproto_add_flow(fo->ofproto, &rule, ofpacts.data, ofpacts.size);
+
+ ofpbuf_uninit(&ofpacts);
}
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "netdev.h"
#include "netlink.h"
#include "odp-util.h"
+#include "ofp-actions.h"
#include "ofproto.h"
#include "ofpbuf.h"
#include "ofproto-provider.h"
bool
in_band_run(struct in_band *ib)
{
- struct {
- struct nx_action_set_queue nxsq;
- union ofp_action oa;
- } actions;
- const void *a;
- size_t na;
+ uint64_t ofpacts_stub[128 / 8];
+ struct ofpbuf ofpacts;
struct in_band_rule *rule, *next;
- memset(&actions, 0, sizeof actions);
- actions.oa.output.type = htons(OFPAT10_OUTPUT);
- actions.oa.output.len = htons(sizeof actions.oa);
- actions.oa.output.port = htons(OFPP_NORMAL);
- actions.oa.output.max_len = htons(0);
- if (ib->queue_id < 0) {
- a = &actions.oa;
- na = sizeof actions.oa / sizeof(union ofp_action);
- } else {
- actions.nxsq.type = htons(OFPAT10_VENDOR);
- actions.nxsq.len = htons(sizeof actions.nxsq);
- actions.nxsq.vendor = htonl(NX_VENDOR_ID);
- actions.nxsq.subtype = htons(NXAST_SET_QUEUE);
- actions.nxsq.queue_id = htonl(ib->queue_id);
- a = &actions;
- na = sizeof actions / sizeof(union ofp_action);
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+
+ if (ib->queue_id >= 0) {
+ ofpact_put_SET_QUEUE(&ofpacts)->queue_id = ib->queue_id;
}
+ ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
refresh_local(ib);
refresh_remotes(ib);
HMAP_FOR_EACH_SAFE (rule, next, cls_rule.hmap_node, &ib->rules) {
switch (rule->op) {
case ADD:
- ofproto_add_flow(ib->ofproto, &rule->cls_rule, a, na);
+ ofproto_add_flow(ib->ofproto, &rule->cls_rule,
+ ofpacts.data, ofpacts.size);
break;
case DELETE:
}
}
+ ofpbuf_uninit(&ofpacts);
+
return ib->n_remotes || !hmap_is_empty(&ib->rules);
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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
bool
governor_should_install_flow(struct governor *g, uint32_t hash, int n)
{
+ int old_count, new_count;
bool install_flow;
uint8_t *e;
- int count;
assert(n > 0);
governor_new_generation(g, new_size);
}
+ /* If we've set up most of the flows we've seen, then we're wasting time
+ * handling most packets one at a time, so in this case instead set up most
+ * flows directly and use the remaining flows as a sample set to adjust our
+ * criteria later.
+ *
+ * The definition of "most" is conservative, but the sample size is tuned
+ * based on a few experiments with TCP_CRR mode in netperf. */
+ if (g->n_setups >= g->n_flows - g->n_flows / 16
+ && g->n_flows >= 64
+ && hash & 0x3f) {
+ g->n_shortcuts++;
+ return true;
+ }
+
/* Do hash table processing.
*
* Even-numbered hash values use high-order nibbles.
* Odd-numbered hash values use low-order nibbles. */
e = &g->table[(hash >> 1) & (g->size - 1)];
- count = n + (hash & 1 ? *e >> 4 : *e & 0x0f);
- if (count >= FLOW_SETUP_THRESHOLD) {
+ old_count = (hash & 1 ? *e >> 4 : *e & 0x0f);
+ if (!old_count) {
+ g->n_flows++;
+ }
+ new_count = n + old_count;
+ if (new_count >= FLOW_SETUP_THRESHOLD) {
+ g->n_setups++;
install_flow = true;
- count = 0;
+ new_count = 0;
} else {
install_flow = false;
}
- *e = hash & 1 ? (count << 4) | (*e & 0x0f) : (*e & 0xf0) | count;
+ *e = hash & 1 ? (new_count << 4) | (*e & 0x0f) : (*e & 0xf0) | new_count;
return install_flow;
}
if (g->size != size) {
if (!g->size) {
VLOG_INFO("%s: engaging governor with %u kB hash table",
- g->name, g->size / 1024);
+ g->name, size / 1024);
} else {
VLOG_INFO("%s: processed %u packets in %.2f s, "
- "%s hash table to %u kB",
+ "%s hash table to %u kB "
+ "(%u hashes, %u setups, %u shortcuts)",
g->name, g->n_packets,
(time_msec() - g->start) / 1000.0,
size > g->size ? "enlarging" : "shrinking",
- size / 1024);
+ size / 1024,
+ g->n_flows, g->n_setups, g->n_shortcuts);
}
free(g->table);
g->table = xmalloc(size * sizeof *g->table);
g->size = size;
} else {
- VLOG_DBG("%s: processed %u packets in %.2f s with %u kB hash table",
+ VLOG_DBG("%s: processed %u packets in %.2f s with %u kB hash table "
+ "(%u hashes, %u setups, %u shortcuts)",
g->name, g->n_packets, (time_msec() - g->start) / 1000.0,
- size / 1024);
+ size / 1024, g->n_flows, g->n_setups, g->n_shortcuts);
}
/* Clear data for next generation. */
memset(g->table, 0, size * sizeof *g->table);
g->start = time_msec();
g->n_packets = 0;
+ g->n_flows /= 2;
+ g->n_setups /= 2;
+ g->n_shortcuts = 0;
}
/*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
unsigned int size; /* Table size in bytes. */
long long int start; /* Time when the table was last cleared. */
unsigned int n_packets; /* Number of packets processed. */
+
+ /* Statistics for skipping counters when most flows get set up. */
+ unsigned int n_flows; /* Number of unique flows seen. */
+ unsigned int n_setups; /* Number of flows set up based on counters. */
+ unsigned int n_shortcuts; /* Number of flows set up based on history. */
};
struct governor *governor_create(const char *name);
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
* Copyright (c) 2009 InMon Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
#include <config.h>
#include "ofproto-dpif-sflow.h"
#include <inttypes.h>
+#include <sys/socket.h>
#include <net/if.h>
#include <stdlib.h>
#include "collectors.h"
void
dpif_sflow_received(struct dpif_sflow *ds, struct ofpbuf *packet,
const struct flow *flow,
- const struct user_action_cookie *cookie)
+ const union user_action_cookie *cookie)
{
SFL_FLOW_SAMPLE_TYPE fs;
SFLFlow_sample_element hdrElem;
SFLSampler *sampler;
struct dpif_sflow_port *in_dsp;
struct netdev_stats stats;
+ ovs_be16 vlan_tci;
int error;
/* Build a flow sample */
switchElem.flowType.sw.src_priority = vlan_tci_to_pcp(flow->vlan_tci);
/* Retrieve data from user_action_cookie. */
- switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(cookie->vlan_tci);
- switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(cookie->vlan_tci);
-
- /* Set output port, as defined by http://www.sflow.org/sflow_version_5.txt
- (search for "Input/output port information"). */
- if (!cookie->n_output) {
- /* This value indicates that the packet was dropped for an unknown
- * reason. */
- fs.output = 0x40000000 | 256;
- } else if (cookie->n_output > 1 || !cookie->data) {
- /* Setting the high bit means "multiple output ports". */
- fs.output = 0x80000000 | cookie->n_output;
- } else {
- fs.output = cookie->data;
- }
+ vlan_tci = cookie->sflow.vlan_tci;
+ switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(vlan_tci);
+ switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(vlan_tci);
+
+ fs.output = cookie->sflow.output;
/* Submit the flow sample to be encoded into the next datagram. */
SFLADD_ELEMENT(&fs, &hdrElem);
/*
* Copyright (c) 2009, 2010 InMon Corp.
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void dpif_sflow_received(struct dpif_sflow *,
struct ofpbuf *,
const struct flow *,
- const struct user_action_cookie *);
+ const union user_action_cookie *);
int dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *, uint16_t);
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "odp-util.h"
#include "ofp-util.h"
#include "ofpbuf.h"
+#include "ofp-actions.h"
+#include "ofp-parse.h"
#include "ofp-print.h"
#include "ofproto-dpif-governor.h"
#include "ofproto-dpif-sflow.h"
#include "poll-loop.h"
+#include "simap.h"
#include "timer.h"
#include "unaligned.h"
#include "unixctl.h"
VLOG_DEFINE_THIS_MODULE(ofproto_dpif);
-COVERAGE_DEFINE(ofproto_dpif_ctlr_action);
COVERAGE_DEFINE(ofproto_dpif_expired);
-COVERAGE_DEFINE(ofproto_dpif_no_packet_in);
COVERAGE_DEFINE(ofproto_dpif_xlate);
COVERAGE_DEFINE(facet_changed_rule);
-COVERAGE_DEFINE(facet_invalidated);
COVERAGE_DEFINE(facet_revalidate);
COVERAGE_DEFINE(facet_unexpected);
COVERAGE_DEFINE(facet_suppress);
/* Maximum depth of flow table recursion (due to resubmit actions) in a
* flow translation. */
-#define MAX_RESUBMIT_RECURSION 32
+#define MAX_RESUBMIT_RECURSION 64
/* Number of implemented OpenFlow tables. */
enum { N_TABLES = 255 };
-BUILD_ASSERT_DECL(N_TABLES >= 1 && N_TABLES <= 255);
+enum { TBL_INTERNAL = N_TABLES - 1 }; /* Used for internal hidden rules. */
+BUILD_ASSERT_DECL(N_TABLES >= 2 && N_TABLES <= 255);
struct ofport_dpif;
struct ofproto_dpif;
}
static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *,
- const struct flow *, uint8_t table);
+ const struct flow *);
+static struct rule_dpif *rule_dpif_lookup__(struct ofproto_dpif *,
+ const struct flow *,
+ uint8_t table);
+static struct rule_dpif *rule_dpif_miss_rule(struct ofproto_dpif *ofproto,
+ const struct flow *flow);
static void rule_credit_stats(struct rule_dpif *,
const struct dpif_flow_stats *);
static void bundle_del_port(struct ofport_dpif *);
static void bundle_run(struct ofbundle *);
static void bundle_wait(struct ofbundle *);
-static struct ofbundle *lookup_input_bundle(struct ofproto_dpif *,
+static struct ofbundle *lookup_input_bundle(const struct ofproto_dpif *,
uint16_t in_port, bool warn,
struct ofport_dpif **in_ofportp);
* calling action_xlate_ctx_init(). */
void (*resubmit_hook)(struct action_xlate_ctx *, struct rule_dpif *rule);
+ /* If nonnull, flow translation calls this function to report some
+ * significant decision, e.g. to explain why OFPP_NORMAL translation
+ * dropped a packet. */
+ void (*report_hook)(struct action_xlate_ctx *, const char *s);
+
/* If nonnull, flow translation credits the specified statistics to each
* rule reached through a resubmit or OFPP_TABLE action.
*
struct ofpbuf *odp_actions; /* Datapath actions. */
tag_type tags; /* Tags associated with actions. */
- bool may_set_up_flow; /* True ordinarily; false if the actions must
- * be reassessed for every packet. */
+ enum slow_path_reason slow; /* 0 if fast path may be used. */
bool has_learn; /* Actions include NXAST_LEARN? */
bool has_normal; /* Actions output to OFPP_NORMAL? */
bool has_fin_timeout; /* Actions include NXAST_FIN_TIMEOUT? */
ovs_be16 initial_tci, struct rule_dpif *,
uint8_t tcp_flags, const struct ofpbuf *);
static void xlate_actions(struct action_xlate_ctx *,
- const union ofp_action *in, size_t n_in,
+ const struct ofpact *ofpacts, size_t ofpacts_len,
struct ofpbuf *odp_actions);
static void xlate_actions_for_side_effects(struct action_xlate_ctx *,
- const union ofp_action *in,
- size_t n_in);
+ const struct ofpact *ofpacts,
+ size_t ofpacts_len);
+
+static size_t put_userspace_action(const struct ofproto_dpif *,
+ struct ofpbuf *odp_actions,
+ const struct flow *,
+ const union user_action_cookie *);
+
+static void compose_slow_path(const struct ofproto_dpif *, const struct flow *,
+ enum slow_path_reason,
+ uint64_t *stub, size_t stub_size,
+ const struct nlattr **actionsp,
+ size_t *actions_lenp);
+
+static void xlate_report(struct action_xlate_ctx *ctx, const char *s);
+
+/* A subfacet (see "struct subfacet" below) has three possible installation
+ * states:
+ *
+ * - SF_NOT_INSTALLED: Not installed in the datapath. This will only be the
+ * case just after the subfacet is created, just before the subfacet is
+ * destroyed, or if the datapath returns an error when we try to install a
+ * subfacet.
+ *
+ * - SF_FAST_PATH: The subfacet's actions are installed in the datapath.
+ *
+ * - SF_SLOW_PATH: An action that sends every packet for the subfacet through
+ * ofproto_dpif is installed in the datapath.
+ */
+enum subfacet_path {
+ SF_NOT_INSTALLED, /* No datapath flow for this subfacet. */
+ SF_FAST_PATH, /* Full actions are installed. */
+ SF_SLOW_PATH, /* Send-to-userspace action is installed. */
+};
+
+static const char *subfacet_path_to_string(enum subfacet_path);
/* A dpif flow and actions associated with a facet.
*
size_t actions_len; /* Number of bytes in actions[]. */
struct nlattr *actions; /* Datapath actions. */
- bool installed; /* Installed in datapath? */
+ enum slow_path_reason slow; /* 0 if fast path may be used. */
+ enum subfacet_path path; /* Installed in datapath? */
/* This value is normally the same as ->facet->flow.vlan_tci. Only VLAN
* splinters can cause it to differ. This value should be removed when
static struct subfacet *subfacet_create(struct facet *, enum odp_key_fitness,
const struct nlattr *key,
- size_t key_len, ovs_be16 initial_tci);
+ size_t key_len, ovs_be16 initial_tci,
+ long long int now);
static struct subfacet *subfacet_find(struct ofproto_dpif *,
const struct nlattr *key, size_t key_len);
static void subfacet_destroy(struct subfacet *);
struct ofpbuf *odp_actions);
static int subfacet_install(struct subfacet *,
const struct nlattr *actions, size_t actions_len,
- struct dpif_flow_stats *);
+ struct dpif_flow_stats *, enum slow_path_reason);
static void subfacet_uninstall(struct subfacet *);
+static enum subfacet_path subfacet_want_path(enum slow_path_reason);
+
/* An exact-match instantiation of an OpenFlow flow.
*
* A facet associates a "struct flow", which represents the Open vSwitch
* between splintered and non-splintered subfacets due to the VLAN tag
* being initially different (present vs. absent). All of them have these
* properties in common so we just store one copy of them here. */
- bool may_install; /* Reassess actions for every packet? */
bool has_learn; /* Actions include NXAST_LEARN? */
bool has_normal; /* Actions output to OFPP_NORMAL? */
bool has_fin_timeout; /* Actions include NXAST_FIN_TIMEOUT? */
const struct flow *, uint32_t hash);
static struct facet *facet_lookup_valid(struct ofproto_dpif *,
const struct flow *, uint32_t hash);
-static bool facet_revalidate(struct facet *);
+static void facet_revalidate(struct facet *);
static bool facet_check_consistency(struct facet *);
static void facet_flush_stats(struct facet *);
static uint32_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
uint32_t realdev, ovs_be16 vlan_tci);
-static uint16_t vsp_vlandev_to_realdev(const struct ofproto_dpif *,
- uint16_t vlandev, int *vid);
+static bool vsp_adjust_flow(const struct ofproto_dpif *, struct flow *);
static void vsp_remove(struct ofport_dpif *);
static void vsp_add(struct ofport_dpif *, uint16_t realdev_ofp_port, int vid);
}
static void port_run(struct ofport_dpif *);
+static void port_run_fast(struct ofport_dpif *);
static void port_wait(struct ofport_dpif *);
static int set_cfm(struct ofport *, const struct cfm_settings *);
static void ofport_clear_priorities(struct ofport_dpif *);
uint32_t basis; /* Keeps each table's tags separate. */
};
+/* Reasons that we might need to revalidate every facet, and corresponding
+ * coverage counters.
+ *
+ * A value of 0 means that there is no need to revalidate.
+ *
+ * It would be nice to have some cleaner way to integrate with coverage
+ * counters, but with only a few reasons I guess this is good enough for
+ * now. */
+enum revalidate_reason {
+ REV_RECONFIGURE = 1, /* Switch configuration changed. */
+ REV_STP, /* Spanning tree protocol port status change. */
+ REV_PORT_TOGGLED, /* Port enabled or disabled by CFM, LACP, ...*/
+ REV_FLOW_TABLE, /* Flow table changed. */
+ REV_INCONSISTENCY /* Facet self-check failed. */
+};
+COVERAGE_DEFINE(rev_reconfigure);
+COVERAGE_DEFINE(rev_stp);
+COVERAGE_DEFINE(rev_port_toggled);
+COVERAGE_DEFINE(rev_flow_table);
+COVERAGE_DEFINE(rev_inconsistency);
+
struct ofproto_dpif {
struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
struct ofproto up;
struct dpif *dpif;
int max_ports;
+ /* Special OpenFlow rules. */
+ struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
+ struct rule_dpif *no_packet_in_rule; /* Drops flow table misses. */
+
/* Statistics. */
uint64_t n_matches;
/* Revalidation. */
struct table_dpif tables[N_TABLES];
- bool need_revalidate;
+ enum revalidate_reason need_revalidate;
struct tag_set revalidate_set;
/* Support for debugging async flow mods. */
return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
}
-static struct ofport_dpif *get_ofp_port(struct ofproto_dpif *,
+static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *,
uint16_t ofp_port);
-static struct ofport_dpif *get_odp_port(struct ofproto_dpif *,
+static struct ofport_dpif *get_odp_port(const struct ofproto_dpif *,
uint32_t odp_port);
static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
const struct ofpbuf *, ovs_be16 initial_tci,
/* Utilities. */
static int send_packet(const struct ofport_dpif *, struct ofpbuf *packet);
-static size_t
-compose_sflow_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions,
- const struct flow *, uint32_t odp_port);
+static size_t compose_sflow_action(const struct ofproto_dpif *,
+ struct ofpbuf *odp_actions,
+ const struct flow *, uint32_t odp_port);
static void add_mirror_actions(struct action_xlate_ctx *ctx,
const struct flow *flow);
/* Global variables. */
\f
/* Basic life-cycle. */
+static int add_internal_flows(struct ofproto_dpif *);
+
static struct ofproto *
alloc(void)
{
table->other_table = NULL;
table->basis = random_uint32();
}
- ofproto->need_revalidate = false;
+ ofproto->need_revalidate = 0;
tag_set_init(&ofproto->revalidate_set);
list_init(&ofproto->completions);
memset(&ofproto->stats, 0, sizeof ofproto->stats);
ofproto_init_tables(ofproto_, N_TABLES);
+ error = add_internal_flows(ofproto);
+ ofproto->up.tables[TBL_INTERNAL].flags = OFTABLE_HIDDEN | OFTABLE_READONLY;
+
+ return error;
+}
+
+static int
+add_internal_flow(struct ofproto_dpif *ofproto, int id,
+ const struct ofpbuf *ofpacts, struct rule_dpif **rulep)
+{
+ struct ofputil_flow_mod fm;
+ int error;
+
+ cls_rule_init_catchall(&fm.cr, 0);
+ cls_rule_set_reg(&fm.cr, 0, id);
+ fm.new_cookie = htonll(0);
+ fm.cookie = htonll(0);
+ fm.cookie_mask = htonll(0);
+ fm.table_id = TBL_INTERNAL;
+ fm.command = OFPFC_ADD;
+ fm.idle_timeout = 0;
+ fm.hard_timeout = 0;
+ fm.buffer_id = 0;
+ fm.out_port = 0;
+ fm.flags = 0;
+ fm.ofpacts = ofpacts->data;
+ fm.ofpacts_len = ofpacts->size;
+
+ error = ofproto_flow_mod(&ofproto->up, &fm);
+ if (error) {
+ VLOG_ERR_RL(&rl, "failed to add internal flow %d (%s)",
+ id, ofperr_to_string(error));
+ return error;
+ }
+
+ *rulep = rule_dpif_lookup__(ofproto, &fm.cr.flow, TBL_INTERNAL);
+ assert(*rulep != NULL);
return 0;
}
+static int
+add_internal_flows(struct ofproto_dpif *ofproto)
+{
+ struct ofpact_controller *controller;
+ uint64_t ofpacts_stub[128 / 8];
+ struct ofpbuf ofpacts;
+ int error;
+ int id;
+
+ ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ id = 1;
+
+ controller = ofpact_put_CONTROLLER(&ofpacts);
+ controller->max_len = UINT16_MAX;
+ controller->controller_id = 0;
+ controller->reason = OFPR_NO_MATCH;
+ ofpact_pad(&ofpacts);
+
+ error = add_internal_flow(ofproto, id++, &ofpacts, &ofproto->miss_rule);
+ if (error) {
+ return error;
+ }
+
+ ofpbuf_clear(&ofpacts);
+ error = add_internal_flow(ofproto, id++, &ofpacts,
+ &ofproto->no_packet_in_rule);
+ return error;
+}
+
static void
complete_operations(struct ofproto_dpif *ofproto)
{
run_fast(struct ofproto *ofproto_)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct ofport_dpif *ofport;
unsigned int work;
+ HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+ port_run_fast(ofport);
+ }
+
/* Handle one or more batches of upcalls, until there's nothing left to do
* or until we do a fixed total amount of work.
*
|| !tag_set_is_empty(&ofproto->revalidate_set)) {
struct tag_set revalidate_set = ofproto->revalidate_set;
bool revalidate_all = ofproto->need_revalidate;
- struct facet *facet, *next;
+ struct facet *facet;
+
+ switch (ofproto->need_revalidate) {
+ case REV_RECONFIGURE: COVERAGE_INC(rev_reconfigure); break;
+ case REV_STP: COVERAGE_INC(rev_stp); break;
+ case REV_PORT_TOGGLED: COVERAGE_INC(rev_port_toggled); break;
+ case REV_FLOW_TABLE: COVERAGE_INC(rev_flow_table); break;
+ case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
+ }
/* Clear the revalidation flags. */
tag_set_init(&ofproto->revalidate_set);
- ofproto->need_revalidate = false;
+ ofproto->need_revalidate = 0;
- HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &ofproto->facets) {
+ HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
if (revalidate_all
|| tag_set_intersects(&revalidate_set, facet->tags)) {
facet_revalidate(facet);
struct facet, hmap_node);
if (!tag_set_intersects(&ofproto->revalidate_set, facet->tags)) {
if (!facet_check_consistency(facet)) {
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_INCONSISTENCY;
}
}
}
}
}
+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_)
{
struct subfacet *subfacet;
LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
- subfacet->installed = false;
+ subfacet->path = SF_NOT_INSTALLED;
subfacet->dp_packet_count = 0;
subfacet->dp_byte_count = 0;
}
}
static void
-get_tables(struct ofproto *ofproto_, struct ofp_table_stats *ots)
+get_tables(struct ofproto *ofproto_, struct ofp10_table_stats *ots)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct dpif_dp_stats s;
struct ofport_dpif *port = ofport_dpif_cast(port_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
port->odp_port = ofp_port_to_odp_port(port->up.ofp_port);
port->bundle = NULL;
port->cfm = NULL;
struct ofport_dpif *port = ofport_dpif_cast(port_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
bundle_remove(port_);
set_cfm(port_, NULL);
if (ofproto->sflow) {
enum ofputil_port_config changed = old_config ^ port->up.pp.config;
if (changed & (OFPUTIL_PC_NO_RECV | OFPUTIL_PC_NO_RECV_STP |
- OFPUTIL_PC_NO_FWD | OFPUTIL_PC_NO_FLOOD)) {
- ofproto->need_revalidate = true;
+ OFPUTIL_PC_NO_FWD | OFPUTIL_PC_NO_FLOOD |
+ OFPUTIL_PC_NO_PACKET_IN)) {
+ ofproto->need_revalidate = REV_RECONFIGURE;
if (changed & OFPUTIL_PC_NO_FLOOD && port->bundle) {
bundle_update(port->bundle);
HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
dpif_sflow_add_port(ds, &ofport->up);
}
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
}
dpif_sflow_set_options(ds, sflow_options);
} else {
if (ds) {
dpif_sflow_destroy(ds);
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
ofproto->sflow = NULL;
}
}
struct ofproto_dpif *ofproto;
ofproto = ofproto_dpif_cast(ofport->up.ofproto);
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
ofport->cfm = cfm_create(netdev_get_name(ofport->up.netdev));
}
return ofport->cfm ? cfm_get_fault(ofport->cfm) : -1;
}
+static int
+get_cfm_opup(const struct ofport *ofport_)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+ return ofport->cfm ? cfm_get_opup(ofport->cfm) : -1;
+}
+
static int
get_cfm_remote_mpids(const struct ofport *ofport_, const uint64_t **rmps,
size_t *n_rmps)
/* Only revalidate flows if the configuration changed. */
if (!s != !ofproto->stp) {
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
}
if (s) {
fwd_change = stp_forward_in_state(ofport->stp_state)
!= stp_forward_in_state(state);
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_STP;
ofport->stp_state = state;
ofport->stp_state_entered = time_msec();
pdscp = xmalloc(sizeof *pdscp);
pdscp->priority = priority;
pdscp->dscp = dscp;
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
}
if (pdscp->dscp != dscp) {
pdscp->dscp = dscp;
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
}
hmap_insert(&new, &pdscp->hmap_node, hash_int(pdscp->priority, 0));
if (!hmap_is_empty(&ofport->priorities)) {
ofport_clear_priorities(ofport);
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
}
hmap_swap(&new, &ofport->priorities);
struct mac_learning *ml = ofproto->ml;
struct mac_entry *mac, *next_mac;
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
if (mac->port.p == bundle) {
if (all_ofprotos) {
{
struct ofbundle *bundle = port->bundle;
- bundle->ofproto->need_revalidate = true;
+ bundle->ofproto->need_revalidate = REV_RECONFIGURE;
list_remove(&port->bundle_node);
port->bundle = NULL;
}
if (port->bundle != bundle) {
- bundle->ofproto->need_revalidate = true;
+ bundle->ofproto->need_revalidate = REV_RECONFIGURE;
if (port->bundle) {
bundle_del_port(port);
}
}
}
if (lacp) {
- port->bundle->ofproto->need_revalidate = true;
+ port->bundle->ofproto->need_revalidate = REV_RECONFIGURE;
lacp_slave_register(bundle->lacp, port, lacp);
}
mirror_destroy(m);
} else if (hmapx_find_and_delete(&m->srcs, bundle)
|| hmapx_find_and_delete(&m->dsts, bundle)) {
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
}
}
}
/* LACP. */
if (s->lacp) {
if (!bundle->lacp) {
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
bundle->lacp = lacp_create();
}
lacp_configure(bundle->lacp, s->lacp);
break;
case PORT_VLAN_TRUNK:
- trunks = (unsigned long *) s->trunks;
+ trunks = CONST_CAST(unsigned long *, s->trunks);
break;
case PORT_VLAN_NATIVE_UNTAGGED:
bitmap_set1(trunks, vlan);
bitmap_set0(trunks, 0);
} else {
- trunks = (unsigned long *) s->trunks;
+ trunks = CONST_CAST(unsigned long *, s->trunks);
}
break;
bundle->ofproto->has_bonded_bundles = true;
if (bundle->bond) {
if (bond_reconfigure(bundle->bond, s->bond)) {
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
}
} else {
bundle->bond = bond_create(s->bond);
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
}
LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
}
}
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
ofproto->has_mirrors = true;
mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
mirror_update_dups(ofproto);
}
ofproto = mirror->ofproto;
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
forward_bpdu_changed(struct ofproto *ofproto_)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- /* Revalidate cached flows whenever forward_bpdu option changes. */
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
}
static void
/* Ports. */
static struct ofport_dpif *
-get_ofp_port(struct ofproto_dpif *ofproto, uint16_t ofp_port)
+get_ofp_port(const struct ofproto_dpif *ofproto, uint16_t ofp_port)
{
struct ofport *ofport = ofproto_get_port(&ofproto->up, ofp_port);
return ofport ? ofport_dpif_cast(ofport) : NULL;
}
static struct ofport_dpif *
-get_odp_port(struct ofproto_dpif *ofproto, uint32_t odp_port)
+get_odp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
{
return get_ofp_port(ofproto, odp_port_to_ofp_port(odp_port));
}
ofproto_port->ofp_port = odp_port_to_ofp_port(dpif_port->port_no);
}
+static void
+port_run_fast(struct ofport_dpif *ofport)
+{
+ if (ofport->cfm && cfm_should_send_ccm(ofport->cfm)) {
+ struct ofpbuf packet;
+
+ ofpbuf_init(&packet, 0);
+ cfm_compose_ccm(ofport->cfm, &packet, ofport->up.pp.hw_addr);
+ send_packet(ofport, &packet);
+ ofpbuf_uninit(&packet);
+ }
+}
+
static void
port_run(struct ofport_dpif *ofport)
{
ofport->carrier_seq = carrier_seq;
+ port_run_fast(ofport);
if (ofport->cfm) {
- cfm_run(ofport->cfm);
+ int cfm_opup = cfm_get_opup(ofport->cfm);
- if (cfm_should_send_ccm(ofport->cfm)) {
- struct ofpbuf packet;
+ cfm_run(ofport->cfm);
+ enable = enable && !cfm_get_fault(ofport->cfm);
- ofpbuf_init(&packet, 0);
- cfm_compose_ccm(ofport->cfm, &packet, ofport->up.pp.hw_addr);
- send_packet(ofport, &packet);
- ofpbuf_uninit(&packet);
+ if (cfm_opup >= 0) {
+ enable = enable && cfm_opup;
}
-
- enable = enable && !cfm_get_fault(ofport->cfm)
- && cfm_get_opup(ofport->cfm);
}
if (ofport->bundle) {
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
if (ofproto->has_bundle_action) {
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_PORT_TOGGLED;
}
}
port_add(struct ofproto *ofproto_, struct netdev *netdev, uint16_t *ofp_portp)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
- uint16_t odp_port;
+ uint16_t odp_port = UINT16_MAX;
int error;
error = dpif_port_add(ofproto->dpif, netdev, &odp_port);
/* ofproto->stats.rx_packets represents packets that were received on
* some port and we processed internally and dropped (e.g. STP).
- * Account fro them as if they had been forwarded to OFPP_LOCAL. */
+ * Account for them as if they had been forwarded to OFPP_LOCAL. */
if (stats->tx_packets != UINT64_MAX) {
stats->tx_packets += ofproto->stats.rx_packets;
size_t key_len;
ovs_be16 initial_tci;
struct list packets;
+ enum dpif_upcall_type upcall_type;
};
struct flow_miss_op {
flow_get_metadata(flow, &pin.fmd);
- /* Registers aren't meaningful on a miss. */
- memset(pin.fmd.reg_masks, 0, sizeof pin.fmd.reg_masks);
-
connmgr_send_packet_in(ofproto->up.connmgr, &pin);
}
-static bool
+static enum slow_path_reason
process_special(struct ofproto_dpif *ofproto, const struct flow *flow,
const struct ofpbuf *packet)
{
struct ofport_dpif *ofport = get_ofp_port(ofproto, flow->in_port);
if (!ofport) {
- return false;
+ return 0;
}
if (ofport->cfm && cfm_should_process_flow(ofport->cfm, flow)) {
if (packet) {
cfm_process_heartbeat(ofport->cfm, packet);
}
- return true;
+ return SLOW_CFM;
} else if (ofport->bundle && ofport->bundle->lacp
&& flow->dl_type == htons(ETH_TYPE_LACP)) {
if (packet) {
lacp_process_packet(ofport->bundle->lacp, ofport, packet);
}
- return true;
+ return SLOW_LACP;
} else if (ofproto->stp && stp_should_process_flow(flow)) {
if (packet) {
stp_process_packet(ofport, packet);
}
- return true;
+ return SLOW_STP;
}
- return false;
+ return 0;
}
static struct flow_miss *
struct flow_miss_op *ops, size_t *n_ops)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ long long int now = time_msec();
struct action_xlate_ctx ctx;
struct ofpbuf *packet;
ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
- dpif_flow_stats_extract(&miss->flow, packet, &stats);
+ dpif_flow_stats_extract(&miss->flow, packet, now, &stats);
rule_credit_stats(rule, &stats);
action_xlate_ctx_init(&ctx, ofproto, &miss->flow, miss->initial_tci,
rule, 0, packet);
ctx.resubmit_stats = &stats;
- xlate_actions(&ctx, rule->up.actions, rule->up.n_actions,
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
if (odp_actions.size) {
}
/* Handles 'miss', which matches 'facet'. May add any required datapath
- * operations to 'ops', incrementing '*n_ops' for each new op. */
+ * operations to 'ops', incrementing '*n_ops' for each new op.
+ *
+ * All of the packets in 'miss' are considered to have arrived at time 'now'.
+ * This is really important only for new facets: if we just called time_msec()
+ * here, then the new subfacet or its packets could look (occasionally) as
+ * though it was used some time after the facet was used. That can make a
+ * one-packet flow look like it has a nonzero duration, which looks odd in
+ * e.g. NetFlow statistics. */
static void
handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
+ long long int now,
struct flow_miss_op *ops, size_t *n_ops)
{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ enum subfacet_path want_path;
struct subfacet *subfacet;
struct ofpbuf *packet;
subfacet = subfacet_create(facet,
miss->key_fitness, miss->key, miss->key_len,
- miss->initial_tci);
+ miss->initial_tci, now);
LIST_FOR_EACH (packet, list_node, &miss->packets) {
struct flow_miss_op *op = &ops[*n_ops];
handle_flow_miss_common(facet->rule, packet, &miss->flow);
ofpbuf_use_stub(&odp_actions, op->stub, sizeof op->stub);
- if (!facet->may_install || !subfacet->actions) {
+ if (!subfacet->actions || subfacet->slow) {
subfacet_make_actions(subfacet, packet, &odp_actions);
}
- dpif_flow_stats_extract(&facet->flow, packet, &stats);
+ dpif_flow_stats_extract(&facet->flow, packet, now, &stats);
subfacet_update_stats(subfacet, &stats);
if (subfacet->actions_len) {
init_flow_miss_execute_op(miss, packet, op);
op->subfacet = subfacet;
- if (facet->may_install) {
+ if (!subfacet->slow) {
execute->actions = subfacet->actions;
execute->actions_len = subfacet->actions_len;
ofpbuf_uninit(&odp_actions);
}
}
- if (facet->may_install && subfacet->key_fitness != ODP_FIT_TOO_LITTLE) {
+ want_path = subfacet_want_path(subfacet->slow);
+ if (miss->upcall_type == DPIF_UC_MISS || subfacet->path != want_path) {
struct flow_miss_op *op = &ops[(*n_ops)++];
struct dpif_flow_put *put = &op->dpif_op.u.flow_put;
put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
put->key = miss->key;
put->key_len = miss->key_len;
- put->actions = subfacet->actions;
- put->actions_len = subfacet->actions_len;
- put->stats = NULL;
- }
-}
-
-/* Handles flow miss 'miss' on 'ofproto'. The flow does not match any flow in
- * the OpenFlow flow table. */
-static void
-handle_flow_miss_no_rule(struct ofproto_dpif *ofproto, struct flow_miss *miss)
-{
- uint16_t in_port = miss->flow.in_port;
- struct ofport_dpif *port = get_ofp_port(ofproto, in_port);
-
- if (!port) {
- VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, in_port);
- }
-
- if (port && port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
- /* XXX install 'drop' flow entry */
- COVERAGE_INC(ofproto_dpif_no_packet_in);
- } else {
- const struct ofpbuf *packet;
-
- LIST_FOR_EACH (packet, list_node, &miss->packets) {
- send_packet_in_miss(ofproto, packet, &miss->flow);
+ if (want_path == SF_FAST_PATH) {
+ put->actions = subfacet->actions;
+ put->actions_len = subfacet->actions_len;
+ } else {
+ compose_slow_path(ofproto, &facet->flow, subfacet->slow,
+ op->stub, sizeof op->stub,
+ &put->actions, &put->actions_len);
}
+ put->stats = NULL;
}
}
struct flow_miss_op *ops, size_t *n_ops)
{
struct facet *facet;
+ long long int now;
uint32_t hash;
/* The caller must ensure that miss->hmap_node.hash contains
facet = facet_lookup_valid(ofproto, &miss->flow, hash);
if (!facet) {
- struct rule_dpif *rule = rule_dpif_lookup(ofproto, &miss->flow, 0);
- if (!rule) {
- handle_flow_miss_no_rule(ofproto, miss);
- return;
- } else if (!flow_miss_should_make_facet(ofproto, miss, hash)) {
+ struct rule_dpif *rule = rule_dpif_lookup(ofproto, &miss->flow);
+
+ if (!flow_miss_should_make_facet(ofproto, miss, hash)) {
handle_flow_miss_without_facet(miss, rule, ops, n_ops);
return;
}
facet = facet_create(rule, &miss->flow, hash);
+ now = facet->used;
+ } else {
+ now = time_msec();
}
- handle_flow_miss_with_facet(miss, facet, ops, n_ops);
+ handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
}
/* Like odp_flow_key_to_flow(), this function converts the 'key_len' bytes of
struct ofpbuf *packet)
{
enum odp_key_fitness fitness;
- uint16_t realdev;
- int vid;
fitness = odp_flow_key_to_flow(key, key_len, flow);
if (fitness == ODP_FIT_ERROR) {
}
*initial_tci = flow->vlan_tci;
- realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid);
- if (realdev) {
- /* Cause the flow to be processed as if it came in on the real device
- * with the VLAN device's VLAN ID. */
- flow->in_port = realdev;
- flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
+ if (vsp_adjust_flow(ofproto, flow)) {
if (packet) {
/* Make the packet resemble the flow, so that it gets sent to an
* OpenFlow controller properly, so that it looks correct for
flow_extract(upcall->packet, miss->flow.skb_priority,
miss->flow.tun_id, miss->flow.in_port, &miss->flow);
- /* Handle 802.1ag, LACP, and STP specially. */
- if (process_special(ofproto, &miss->flow, upcall->packet)) {
- ofproto_update_local_port_stats(&ofproto->up,
- 0, upcall->packet->size);
- ofproto->n_matches++;
- continue;
- }
-
/* Add other packets to a to-do list. */
hash = flow_hash(&miss->flow, 0);
existing_miss = flow_miss_find(&todo, &miss->flow, hash);
hmap_insert(&todo, &miss->hmap_node, hash);
miss->key = upcall->key;
miss->key_len = upcall->key_len;
+ miss->upcall_type = upcall->type;
list_init(&miss->packets);
n_misses++;
case DPIF_OP_FLOW_PUT:
if (!op->dpif_op.error) {
- op->subfacet->installed = true;
+ op->subfacet->path = subfacet_want_path(op->subfacet->slow);
}
break;
hmap_destroy(&todo);
}
+static enum { SFLOW_UPCALL, MISS_UPCALL, BAD_UPCALL }
+classify_upcall(const struct dpif_upcall *upcall)
+{
+ union user_action_cookie cookie;
+
+ /* First look at the upcall type. */
+ switch (upcall->type) {
+ case DPIF_UC_ACTION:
+ break;
+
+ case DPIF_UC_MISS:
+ return MISS_UPCALL;
+
+ case DPIF_N_UC_TYPES:
+ default:
+ VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, upcall->type);
+ return BAD_UPCALL;
+ }
+
+ /* "action" upcalls need a closer look. */
+ memcpy(&cookie, &upcall->userdata, sizeof(cookie));
+ switch (cookie.type) {
+ case USER_ACTION_COOKIE_SFLOW:
+ return SFLOW_UPCALL;
+
+ case USER_ACTION_COOKIE_SLOW_PATH:
+ return MISS_UPCALL;
+
+ case USER_ACTION_COOKIE_UNSPEC:
+ default:
+ VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata);
+ return BAD_UPCALL;
+ }
+}
+
static void
-handle_userspace_upcall(struct ofproto_dpif *ofproto,
- struct dpif_upcall *upcall)
+handle_sflow_upcall(struct ofproto_dpif *ofproto,
+ const struct dpif_upcall *upcall)
{
- struct user_action_cookie cookie;
+ union user_action_cookie cookie;
enum odp_key_fitness fitness;
ovs_be16 initial_tci;
struct flow flow;
- memcpy(&cookie, &upcall->userdata, sizeof(cookie));
-
fitness = ofproto_dpif_extract_flow_key(ofproto, upcall->key,
upcall->key_len, &flow,
&initial_tci, upcall->packet);
return;
}
- if (cookie.type == USER_ACTION_COOKIE_SFLOW) {
- if (ofproto->sflow) {
- dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
- &cookie);
- }
- } else {
- VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata);
- }
+ memcpy(&cookie, &upcall->userdata, sizeof(cookie));
+ dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, &cookie);
}
static int
assert(max_batch <= FLOW_MISS_MAX_BATCH);
- n_processed = 0;
n_misses = 0;
for (n_processed = 0; n_processed < max_batch; n_processed++) {
struct dpif_upcall *upcall = &misses[n_misses];
break;
}
- switch (upcall->type) {
- case DPIF_UC_ACTION:
- handle_userspace_upcall(ofproto, upcall);
- ofpbuf_uninit(buf);
- break;
-
- case DPIF_UC_MISS:
+ switch (classify_upcall(upcall)) {
+ case MISS_UPCALL:
/* Handle it later. */
n_misses++;
break;
- case DPIF_N_UC_TYPES:
- default:
- VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32,
- upcall->type);
+ case SFLOW_UPCALL:
+ if (ofproto->sflow) {
+ handle_sflow_upcall(ofproto, upcall);
+ }
+ ofpbuf_uninit(buf);
+ break;
+
+ case BAD_UPCALL:
+ ofpbuf_uninit(buf);
break;
}
}
+ /* Handle deferred MISS_UPCALL processing. */
handle_miss_upcalls(ofproto, misses, n_misses);
for (i = 0; i < n_misses; i++) {
ofpbuf_uninit(&miss_bufs[i]);
return MIN(dp_max_idle, 1000);
}
+/* Updates flow table statistics given that the datapath just reported 'stats'
+ * as 'subfacet''s statistics. */
+static void
+update_subfacet_stats(struct subfacet *subfacet,
+ const struct dpif_flow_stats *stats)
+{
+ struct facet *facet = subfacet->facet;
+
+ if (stats->n_packets >= subfacet->dp_packet_count) {
+ uint64_t extra = stats->n_packets - subfacet->dp_packet_count;
+ facet->packet_count += extra;
+ } else {
+ VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
+ }
+
+ if (stats->n_bytes >= subfacet->dp_byte_count) {
+ facet->byte_count += stats->n_bytes - subfacet->dp_byte_count;
+ } else {
+ VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
+ }
+
+ subfacet->dp_packet_count = stats->n_packets;
+ subfacet->dp_byte_count = stats->n_bytes;
+
+ facet->tcp_flags |= stats->tcp_flags;
+
+ subfacet_update_time(subfacet, stats->used);
+ if (facet->accounted_bytes < facet->byte_count) {
+ facet_learn(facet);
+ facet_account(facet);
+ facet->accounted_bytes = facet->byte_count;
+ }
+ facet_push_stats(facet);
+}
+
+/* 'key' with length 'key_len' bytes is a flow in 'dpif' that we know nothing
+ * about, or a flow that shouldn't be installed but was anyway. Delete it. */
+static void
+delete_unexpected_flow(struct dpif *dpif,
+ const struct nlattr *key, size_t key_len)
+{
+ if (!VLOG_DROP_WARN(&rl)) {
+ struct ds s;
+
+ ds_init(&s);
+ odp_flow_key_format(key, key_len, &s);
+ VLOG_WARN("unexpected flow from datapath %s", ds_cstr(&s));
+ ds_destroy(&s);
+ }
+
+ COVERAGE_INC(facet_unexpected);
+ dpif_flow_del(dpif, key, key_len, NULL);
+}
+
/* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
*
* This function also pushes statistics updates to rules which each facet
struct subfacet *subfacet;
subfacet = subfacet_find(p, key, key_len);
- if (subfacet && subfacet->installed) {
- struct facet *facet = subfacet->facet;
-
- if (stats->n_packets >= subfacet->dp_packet_count) {
- uint64_t extra = stats->n_packets - subfacet->dp_packet_count;
- facet->packet_count += extra;
- } else {
- VLOG_WARN_RL(&rl, "unexpected packet count from the datapath");
- }
-
- if (stats->n_bytes >= subfacet->dp_byte_count) {
- facet->byte_count += stats->n_bytes - subfacet->dp_byte_count;
- } else {
- VLOG_WARN_RL(&rl, "unexpected byte count from datapath");
- }
-
- subfacet->dp_packet_count = stats->n_packets;
- subfacet->dp_byte_count = stats->n_bytes;
-
- facet->tcp_flags |= stats->tcp_flags;
-
- subfacet_update_time(subfacet, stats->used);
- if (facet->accounted_bytes < facet->byte_count) {
- facet_learn(facet);
- facet_account(facet);
- facet->accounted_bytes = facet->byte_count;
- }
- facet_push_stats(facet);
- } else {
- if (!VLOG_DROP_WARN(&rl)) {
- struct ds s;
+ switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
+ case SF_FAST_PATH:
+ update_subfacet_stats(subfacet, stats);
+ break;
- ds_init(&s);
- odp_flow_key_format(key, key_len, &s);
- VLOG_WARN("unexpected flow from datapath %s", ds_cstr(&s));
- ds_destroy(&s);
- }
+ case SF_SLOW_PATH:
+ /* Stats are updated per-packet. */
+ break;
- COVERAGE_INC(facet_unexpected);
- /* There's a flow in the datapath that we know nothing about, or a
- * flow that shouldn't be installed but was anyway. Delete it. */
- dpif_flow_del(p->dpif, key, key_len, NULL);
+ case SF_NOT_INSTALLED:
+ default:
+ delete_unexpected_flow(p->dpif, key, key_len);
+ break;
}
}
dpif_flow_dump_done(&dump);
dpif_operate(ofproto->dpif, opsp, n);
for (i = 0; i < n; i++) {
subfacet_reset_dp_stats(subfacets[i], &stats[i]);
- subfacets[i]->installed = false;
+ subfacets[i]->path = SF_NOT_INSTALLED;
subfacet_destroy(subfacets[i]);
}
}
static void
expire_subfacets(struct ofproto_dpif *ofproto, int dp_max_idle)
{
- long long int cutoff = time_msec() - dp_max_idle;
+ /* Cutoff time for most flows. */
+ long long int normal_cutoff = time_msec() - dp_max_idle;
+
+ /* We really want to keep flows for special protocols around, so use a more
+ * conservative cutoff. */
+ long long int special_cutoff = time_msec() - 10000;
struct subfacet *subfacet, *next_subfacet;
struct subfacet *batch[EXPIRE_MAX_BATCH];
n_batch = 0;
HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
&ofproto->subfacets) {
+ long long int cutoff;
+
+ cutoff = (subfacet->slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)
+ ? special_cutoff
+ : normal_cutoff);
if (subfacet->used < cutoff) {
- if (subfacet->installed) {
+ if (subfacet->path != SF_NOT_INSTALLED) {
batch[n_batch++] = subfacet;
if (n_batch >= EXPIRE_MAX_BATCH) {
expire_batch(ofproto, batch, n_batch);
long long int now;
uint8_t reason;
+ if (rule->up.pending) {
+ /* We'll have to expire it later. */
+ return;
+ }
+
/* Has 'rule' expired? */
now = time_msec();
if (rule->up.hard_timeout
facet->flow.vlan_tci,
facet->rule, facet->tcp_flags, NULL);
ctx.may_learn = true;
- xlate_actions_for_side_effects(&ctx, facet->rule->up.actions,
- facet->rule->up.n_actions);
+ xlate_actions_for_side_effects(&ctx, facet->rule->up.ofpacts,
+ facet->rule->up.ofpacts_len);
}
static void
static bool
facet_is_controller_flow(struct facet *facet)
{
- return (facet
- && facet->rule->up.n_actions == 1
- && action_outputs_to_port(&facet->rule->up.actions[0],
- htons(OFPP_CONTROLLER)));
+ if (facet) {
+ const struct rule *rule = &facet->rule->up;
+ const struct ofpact *ofpacts = rule->ofpacts;
+ size_t ofpacts_len = rule->ofpacts_len;
+
+ if (ofpacts_len > 0 &&
+ ofpacts->type == OFPACT_CONTROLLER &&
+ ofpact_next(ofpacts) >= ofpact_end(ofpacts, ofpacts_len)) {
+ return true;
+ }
+ }
+ return false;
}
/* Folds all of 'facet''s statistics into its rule. Also updates the
facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow,
uint32_t hash)
{
- struct facet *facet = facet_find(ofproto, flow, hash);
+ struct facet *facet;
- /* The facet we found might not be valid, since we could be in need of
- * revalidation. If it is not valid, don't return it. */
+ facet = facet_find(ofproto, flow, hash);
if (facet
&& (ofproto->need_revalidate
- || tag_set_intersects(&ofproto->revalidate_set, facet->tags))
- && !facet_revalidate(facet)) {
- COVERAGE_INC(facet_invalidated);
- return NULL;
+ || tag_set_intersects(&ofproto->revalidate_set, facet->tags))) {
+ facet_revalidate(facet);
}
return facet;
}
+static const char *
+subfacet_path_to_string(enum subfacet_path path)
+{
+ switch (path) {
+ case SF_NOT_INSTALLED:
+ return "not installed";
+ case SF_FAST_PATH:
+ return "in fast path";
+ case SF_SLOW_PATH:
+ return "in slow path";
+ default:
+ return "<error>";
+ }
+}
+
+/* Returns the path in which a subfacet should be installed if its 'slow'
+ * member has the specified value. */
+static enum subfacet_path
+subfacet_want_path(enum slow_path_reason slow)
+{
+ return slow ? SF_SLOW_PATH : SF_FAST_PATH;
+}
+
+/* Returns true if 'subfacet' needs to have its datapath flow updated,
+ * supposing that its actions have been recalculated as 'want_actions' and that
+ * 'slow' is nonzero iff 'subfacet' should be in the slow path. */
+static bool
+subfacet_should_install(struct subfacet *subfacet, enum slow_path_reason slow,
+ const struct ofpbuf *want_actions)
+{
+ enum subfacet_path want_path = subfacet_want_path(slow);
+ return (want_path != subfacet->path
+ || (want_path == SF_FAST_PATH
+ && (subfacet->actions_len != want_actions->size
+ || memcmp(subfacet->actions, want_actions->data,
+ subfacet->actions_len))));
+}
+
static bool
facet_check_consistency(struct facet *facet)
{
bool ok;
/* Check the rule for consistency. */
- rule = rule_dpif_lookup(ofproto, &facet->flow, 0);
- if (!rule) {
- if (!VLOG_DROP_WARN(&rl)) {
- char *s = flow_to_string(&facet->flow);
- VLOG_WARN("%s: facet should not exist", s);
- free(s);
- }
- return false;
- } else if (rule != facet->rule) {
+ rule = rule_dpif_lookup(ofproto, &facet->flow);
+ ok = rule == facet->rule;
+ if (!ok) {
may_log = !VLOG_DROP_WARN(&rl);
- ok = false;
if (may_log) {
struct ds s;
VLOG_WARN("%s", ds_cstr(&s));
ds_destroy(&s);
}
- } else {
- ok = true;
}
/* Check the datapath actions for consistency. */
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ enum subfacet_path want_path;
+ struct odputil_keybuf keybuf;
struct action_xlate_ctx ctx;
- bool actions_changed;
- bool should_install;
+ struct ofpbuf key;
+ struct ds s;
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
subfacet->initial_tci, rule, 0, NULL);
- xlate_actions(&ctx, rule->up.actions, rule->up.n_actions,
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
- should_install = (ctx.may_set_up_flow
- && subfacet->key_fitness != ODP_FIT_TOO_LITTLE);
- if (!should_install && !subfacet->installed) {
- /* The actions for uninstallable flows may vary from one packet to
- * the next, so don't compare the actions. */
+ if (subfacet->path == SF_NOT_INSTALLED) {
+ /* This only happens if the datapath reported an error when we
+ * tried to install the flow. Don't flag another error here. */
continue;
}
- actions_changed = (subfacet->actions_len != odp_actions.size
- || memcmp(subfacet->actions, odp_actions.data,
- subfacet->actions_len));
- if (should_install != subfacet->installed || actions_changed) {
- if (ok) {
- may_log = !VLOG_DROP_WARN(&rl);
- ok = false;
- }
-
- if (may_log) {
- struct odputil_keybuf keybuf;
- struct ofpbuf key;
- struct ds s;
+ want_path = subfacet_want_path(subfacet->slow);
+ if (want_path == SF_SLOW_PATH && subfacet->path == SF_SLOW_PATH) {
+ /* The actions for slow-path flows may legitimately vary from one
+ * packet to the next. We're done. */
+ continue;
+ }
- ds_init(&s);
- subfacet_get_key(subfacet, &keybuf, &key);
- odp_flow_key_format(key.data, key.size, &s);
+ if (!subfacet_should_install(subfacet, subfacet->slow, &odp_actions)) {
+ continue;
+ }
- ds_put_cstr(&s, ": inconsistency in subfacet");
- if (should_install != subfacet->installed) {
- enum odp_key_fitness fitness = subfacet->key_fitness;
+ /* Inconsistency! */
+ if (ok) {
+ may_log = !VLOG_DROP_WARN(&rl);
+ ok = false;
+ }
+ if (!may_log) {
+ /* Rate-limited, skip reporting. */
+ continue;
+ }
- ds_put_format(&s, " (should%s have been installed)",
- should_install ? "" : " not");
- ds_put_format(&s, " (may_set_up_flow=%s, fitness=%s)",
- ctx.may_set_up_flow ? "true" : "false",
- odp_key_fitness_to_string(fitness));
- }
- if (actions_changed) {
- ds_put_cstr(&s, " (actions were: ");
- format_odp_actions(&s, subfacet->actions,
- subfacet->actions_len);
- ds_put_cstr(&s, ") (correct actions: ");
- format_odp_actions(&s, odp_actions.data, odp_actions.size);
- ds_put_char(&s, ')');
- } else {
- ds_put_cstr(&s, " (actions: ");
- format_odp_actions(&s, subfacet->actions,
- subfacet->actions_len);
- ds_put_char(&s, ')');
- }
- VLOG_WARN("%s", ds_cstr(&s));
- ds_destroy(&s);
- }
+ ds_init(&s);
+ subfacet_get_key(subfacet, &keybuf, &key);
+ odp_flow_key_format(key.data, key.size, &s);
+
+ ds_put_cstr(&s, ": inconsistency in subfacet");
+ if (want_path != subfacet->path) {
+ enum odp_key_fitness fitness = subfacet->key_fitness;
+
+ ds_put_format(&s, " (%s, fitness=%s)",
+ subfacet_path_to_string(subfacet->path),
+ odp_key_fitness_to_string(fitness));
+ ds_put_format(&s, " (should have been %s)",
+ subfacet_path_to_string(want_path));
+ } else if (want_path == SF_FAST_PATH) {
+ ds_put_cstr(&s, " (actions were: ");
+ format_odp_actions(&s, subfacet->actions,
+ subfacet->actions_len);
+ ds_put_cstr(&s, ") (correct actions: ");
+ format_odp_actions(&s, odp_actions.data, odp_actions.size);
+ ds_put_char(&s, ')');
+ } else {
+ ds_put_cstr(&s, " (actions: ");
+ format_odp_actions(&s, subfacet->actions,
+ subfacet->actions_len);
+ ds_put_char(&s, ')');
}
+ VLOG_WARN("%s", ds_cstr(&s));
+ ds_destroy(&s);
}
ofpbuf_uninit(&odp_actions);
* 'facet' to the new rule and recompiles its actions.
*
* - If the rule found is the same as 'facet''s current rule, leaves 'facet'
- * where it is and recompiles its actions anyway.
- *
- * - If there is none, destroys 'facet'.
- *
- * Returns true if 'facet' still exists, false if it has been destroyed. */
-static bool
+ * where it is and recompiles its actions anyway. */
+static void
facet_revalidate(struct facet *facet)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
struct rule_dpif *new_rule;
struct subfacet *subfacet;
- bool actions_changed;
int i;
COVERAGE_INC(facet_revalidate);
- /* Determine the new rule. */
- new_rule = rule_dpif_lookup(ofproto, &facet->flow, 0);
- if (!new_rule) {
- /* No new rule, so delete the facet. */
- facet_remove(facet);
- return false;
- }
+ new_rule = rule_dpif_lookup(ofproto, &facet->flow);
/* Calculate new datapath actions.
*
memset(&ctx, 0, sizeof ctx);
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
- bool should_install;
+ enum slow_path_reason slow;
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
subfacet->initial_tci, new_rule, 0, NULL);
- xlate_actions(&ctx, new_rule->up.actions, new_rule->up.n_actions,
+ xlate_actions(&ctx, new_rule->up.ofpacts, new_rule->up.ofpacts_len,
&odp_actions);
- actions_changed = (subfacet->actions_len != odp_actions.size
- || memcmp(subfacet->actions, odp_actions.data,
- subfacet->actions_len));
-
- should_install = (ctx.may_set_up_flow
- && subfacet->key_fitness != ODP_FIT_TOO_LITTLE);
- if (actions_changed || should_install != subfacet->installed) {
- if (should_install) {
- struct dpif_flow_stats stats;
- subfacet_install(subfacet,
- odp_actions.data, odp_actions.size, &stats);
- subfacet_update_stats(subfacet, &stats);
- } else {
- subfacet_uninstall(subfacet);
- }
+ slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
+ if (subfacet_should_install(subfacet, slow, &odp_actions)) {
+ struct dpif_flow_stats stats;
+
+ subfacet_install(subfacet,
+ odp_actions.data, odp_actions.size, &stats, slow);
+ subfacet_update_stats(subfacet, &stats);
if (!new_actions) {
new_actions = xcalloc(list_size(&facet->subfacets),
/* Update 'facet' now that we've taken care of all the old state. */
facet->tags = ctx.tags;
facet->nf_flow.output_iface = ctx.nf_output_iface;
- facet->may_install = ctx.may_set_up_flow;
facet->has_learn = ctx.has_learn;
facet->has_normal = ctx.has_normal;
facet->has_fin_timeout = ctx.has_fin_timeout;
facet->mirrors = ctx.mirrors;
- if (new_actions) {
- i = 0;
- LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
- if (new_actions[i].odp_actions) {
- free(subfacet->actions);
- subfacet->actions = new_actions[i].odp_actions;
- subfacet->actions_len = new_actions[i].actions_len;
- }
- i++;
+
+ i = 0;
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ subfacet->slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
+
+ if (new_actions && new_actions[i].odp_actions) {
+ free(subfacet->actions);
+ subfacet->actions = new_actions[i].odp_actions;
+ subfacet->actions_len = new_actions[i].actions_len;
}
- free(new_actions);
+ i++;
}
+ free(new_actions);
+
if (facet->rule != new_rule) {
COVERAGE_INC(facet_changed_rule);
list_remove(&facet->list_node);
facet->used = new_rule->up.created;
facet->prev_used = facet->used;
}
-
- return true;
}
/* Updates 'facet''s used time. Caller is responsible for calling
action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule,
0, NULL);
ctx.resubmit_stats = stats;
- xlate_actions_for_side_effects(&ctx, rule->up.actions, rule->up.n_actions);
+ xlate_actions_for_side_effects(&ctx, rule->up.ofpacts,
+ rule->up.ofpacts_len);
}
\f
/* Subfacets. */
* subfacet_make_actions(). */
static struct subfacet *
subfacet_create(struct facet *facet, enum odp_key_fitness key_fitness,
- const struct nlattr *key, size_t key_len, ovs_be16 initial_tci)
+ const struct nlattr *key, size_t key_len,
+ ovs_be16 initial_tci, long long int now)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
uint32_t key_hash = odp_flow_key_hash(key, key_len);
struct subfacet *subfacet;
- subfacet = subfacet_find__(ofproto, key, key_len, key_hash, &facet->flow);
- if (subfacet) {
- if (subfacet->facet == facet) {
- return subfacet;
+ if (list_is_empty(&facet->subfacets)) {
+ subfacet = &facet->one_subfacet;
+ } else {
+ subfacet = subfacet_find__(ofproto, key, key_len, key_hash,
+ &facet->flow);
+ if (subfacet) {
+ if (subfacet->facet == facet) {
+ return subfacet;
+ }
+
+ /* This shouldn't happen. */
+ VLOG_ERR_RL(&rl, "subfacet with wrong facet");
+ subfacet_destroy(subfacet);
}
- /* This shouldn't happen. */
- VLOG_ERR_RL(&rl, "subfacet with wrong facet");
- subfacet_destroy(subfacet);
+ subfacet = xmalloc(sizeof *subfacet);
}
- subfacet = (list_is_empty(&facet->subfacets)
- ? &facet->one_subfacet
- : xmalloc(sizeof *subfacet));
hmap_insert(&ofproto->subfacets, &subfacet->hmap_node, key_hash);
list_push_back(&facet->subfacets, &subfacet->list_node);
subfacet->facet = facet;
subfacet->key = NULL;
subfacet->key_len = 0;
}
- subfacet->used = time_msec();
+ subfacet->used = now;
subfacet->dp_packet_count = 0;
subfacet->dp_byte_count = 0;
subfacet->actions_len = 0;
subfacet->actions = NULL;
- subfacet->installed = false;
+ subfacet->slow = (subfacet->key_fitness == ODP_FIT_TOO_LITTLE
+ ? SLOW_MATCH
+ : 0);
+ subfacet->path = SF_NOT_INSTALLED;
subfacet->initial_tci = initial_tci;
return subfacet;
action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
rule, 0, packet);
- xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, odp_actions);
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, odp_actions);
facet->tags = ctx.tags;
- facet->may_install = ctx.may_set_up_flow;
facet->has_learn = ctx.has_learn;
facet->has_normal = ctx.has_normal;
facet->has_fin_timeout = ctx.has_fin_timeout;
facet->nf_flow.output_iface = ctx.nf_output_iface;
facet->mirrors = ctx.mirrors;
+ subfacet->slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
if (subfacet->actions_len != odp_actions->size
|| memcmp(subfacet->actions, odp_actions->data, odp_actions->size)) {
free(subfacet->actions);
static int
subfacet_install(struct subfacet *subfacet,
const struct nlattr *actions, size_t actions_len,
- struct dpif_flow_stats *stats)
+ struct dpif_flow_stats *stats,
+ enum slow_path_reason slow)
{
struct facet *facet = subfacet->facet;
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ enum subfacet_path path = subfacet_want_path(slow);
+ uint64_t slow_path_stub[128 / 8];
struct odputil_keybuf keybuf;
enum dpif_flow_put_flags flags;
struct ofpbuf key;
flags |= DPIF_FP_ZERO_STATS;
}
+ if (path == SF_SLOW_PATH) {
+ compose_slow_path(ofproto, &facet->flow, slow,
+ slow_path_stub, sizeof slow_path_stub,
+ &actions, &actions_len);
+ }
+
subfacet_get_key(subfacet, &keybuf, &key);
ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
actions, actions_len, stats);
subfacet_reset_dp_stats(subfacet, stats);
}
+ if (!ret) {
+ subfacet->path = path;
+ }
return ret;
}
+static int
+subfacet_reinstall(struct subfacet *subfacet, struct dpif_flow_stats *stats)
+{
+ return subfacet_install(subfacet, subfacet->actions, subfacet->actions_len,
+ stats, subfacet->slow);
+}
+
/* If 'subfacet' is installed in the datapath, uninstalls it. */
static void
subfacet_uninstall(struct subfacet *subfacet)
{
- if (subfacet->installed) {
+ if (subfacet->path != SF_NOT_INSTALLED) {
struct rule_dpif *rule = subfacet->facet->rule;
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
struct odputil_keybuf keybuf;
if (!error) {
subfacet_update_stats(subfacet, &stats);
}
- subfacet->installed = false;
+ subfacet->path = SF_NOT_INSTALLED;
} else {
assert(subfacet->dp_packet_count == 0);
assert(subfacet->dp_byte_count == 0);
/* Rules. */
static struct rule_dpif *
-rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow,
- uint8_t table_id)
+rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
+{
+ struct rule_dpif *rule;
+
+ rule = rule_dpif_lookup__(ofproto, flow, 0);
+ if (rule) {
+ return rule;
+ }
+
+ return rule_dpif_miss_rule(ofproto, flow);
+}
+
+static struct rule_dpif *
+rule_dpif_lookup__(struct ofproto_dpif *ofproto, const struct flow *flow,
+ uint8_t table_id)
{
struct cls_rule *cls_rule;
struct classifier *cls;
return rule_dpif_cast(rule_from_cls_rule(cls_rule));
}
+static struct rule_dpif *
+rule_dpif_miss_rule(struct ofproto_dpif *ofproto, const struct flow *flow)
+{
+ struct ofport_dpif *port;
+
+ port = get_ofp_port(ofproto, flow->in_port);
+ if (!port) {
+ VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, flow->in_port);
+ return ofproto->miss_rule;
+ }
+
+ if (port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
+ return ofproto->no_packet_in_rule;
+ }
+ return ofproto->miss_rule;
+}
+
static void
complete_operation(struct rule_dpif *rule)
{
uint8_t table_id;
enum ofperr error;
- error = validate_actions(rule->up.actions, rule->up.n_actions,
- &rule->up.cr.flow, ofproto->max_ports);
+ error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len,
+ &rule->up.cr.flow, ofproto->max_ports);
if (error) {
return error;
}
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions;
- dpif_flow_stats_extract(flow, packet, &stats);
+ dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
rule_credit_stats(rule, &stats);
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
rule, stats.tcp_flags, packet);
ctx.resubmit_stats = &stats;
- xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, &odp_actions);
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions);
execute_odp_actions(ofproto, flow, odp_actions.data,
odp_actions.size, packet);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
enum ofperr error;
- error = validate_actions(rule->up.actions, rule->up.n_actions,
- &rule->up.cr.flow, ofproto->max_ports);
+ error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len,
+ &rule->up.cr.flow, ofproto->max_ports);
if (error) {
ofoperation_complete(rule->up.pending, error);
return;
struct flow flow;
int error;
- flow_extract((struct ofpbuf *) packet, 0, 0, 0, &flow);
+ flow_extract(packet, 0, 0, 0, &flow);
odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
flow.vlan_tci);
if (odp_port != ofport->odp_port) {
\f
/* OpenFlow to datapath action translation. */
-static void do_xlate_actions(const union ofp_action *in, size_t n_in,
- struct action_xlate_ctx *ctx);
+static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
+ struct action_xlate_ctx *);
static void xlate_normal(struct action_xlate_ctx *);
+/* Composes an ODP action for a "slow path" action for 'flow' within 'ofproto'.
+ * The action will state 'slow' as the reason that the action is in the slow
+ * path. (This is purely informational: it allows a human viewing "ovs-dpctl
+ * dump-flows" output to see why a flow is in the slow path.)
+ *
+ * The 'stub_size' bytes in 'stub' will be used to store the action.
+ * 'stub_size' must be large enough for the action.
+ *
+ * The action and its size will be stored in '*actionsp' and '*actions_lenp',
+ * respectively. */
+static void
+compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
+ enum slow_path_reason slow,
+ uint64_t *stub, size_t stub_size,
+ const struct nlattr **actionsp, size_t *actions_lenp)
+{
+ union user_action_cookie cookie;
+ struct ofpbuf buf;
+
+ cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
+ cookie.slow_path.unused = 0;
+ cookie.slow_path.reason = slow;
+
+ ofpbuf_use_stack(&buf, stub, stub_size);
+ if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
+ uint32_t pid = dpif_port_get_pid(ofproto->dpif, UINT16_MAX);
+ odp_put_userspace_action(pid, &cookie, &buf);
+ } else {
+ put_userspace_action(ofproto, &buf, flow, &cookie);
+ }
+ *actionsp = buf.data;
+ *actions_lenp = buf.size;
+}
+
static size_t
put_userspace_action(const struct ofproto_dpif *ofproto,
struct ofpbuf *odp_actions,
const struct flow *flow,
- const struct user_action_cookie *cookie)
+ const union user_action_cookie *cookie)
{
uint32_t pid;
return odp_put_userspace_action(pid, cookie, odp_actions);
}
+static void
+compose_sflow_cookie(const struct ofproto_dpif *ofproto,
+ ovs_be16 vlan_tci, uint32_t odp_port,
+ unsigned int n_outputs, union user_action_cookie *cookie)
+{
+ int ifindex;
+
+ cookie->type = USER_ACTION_COOKIE_SFLOW;
+ cookie->sflow.vlan_tci = vlan_tci;
+
+ /* See http://www.sflow.org/sflow_version_5.txt (search for "Input/output
+ * port information") for the interpretation of cookie->output. */
+ switch (n_outputs) {
+ case 0:
+ /* 0x40000000 | 256 means "packet dropped for unknown reason". */
+ cookie->sflow.output = 0x40000000 | 256;
+ break;
+
+ case 1:
+ ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port);
+ if (ifindex) {
+ cookie->sflow.output = ifindex;
+ break;
+ }
+ /* Fall through. */
+ default:
+ /* 0x80000000 means "multiple output ports. */
+ cookie->sflow.output = 0x80000000 | n_outputs;
+ break;
+ }
+}
+
/* Compose SAMPLE action for sFlow. */
static size_t
compose_sflow_action(const struct ofproto_dpif *ofproto,
const struct flow *flow,
uint32_t odp_port)
{
- uint32_t port_ifindex;
uint32_t probability;
- struct user_action_cookie cookie;
+ union user_action_cookie cookie;
size_t sample_offset, actions_offset;
- int cookie_offset, n_output;
+ int cookie_offset;
if (!ofproto->sflow || flow->in_port == OFPP_NONE) {
return 0;
}
- if (odp_port == OVSP_NONE) {
- port_ifindex = 0;
- n_output = 0;
- } else {
- port_ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port);
- n_output = 1;
- }
-
sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
/* Number of packets out of UINT_MAX to sample. */
nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
-
- cookie.type = USER_ACTION_COOKIE_SFLOW;
- cookie.data = port_ifindex;
- cookie.n_output = n_output;
- cookie.vlan_tci = 0;
+ compose_sflow_cookie(ofproto, htons(0), odp_port,
+ odp_port == OVSP_NONE ? 0 : 1, &cookie);
cookie_offset = put_userspace_action(ofproto, odp_actions, flow, &cookie);
nl_msg_end_nested(odp_actions, actions_offset);
fix_sflow_action(struct action_xlate_ctx *ctx)
{
const struct flow *base = &ctx->base_flow;
- struct user_action_cookie *cookie;
+ union user_action_cookie *cookie;
if (!ctx->user_cookie_offset) {
return;
}
cookie = ofpbuf_at(ctx->odp_actions, ctx->user_cookie_offset,
- sizeof(*cookie));
- assert(cookie != NULL);
+ sizeof(*cookie));
assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
- if (ctx->sflow_n_outputs) {
- cookie->data = dpif_sflow_odp_port_to_ifindex(ctx->ofproto->sflow,
- ctx->sflow_odp_port);
- }
- if (ctx->sflow_n_outputs >= 255) {
- cookie->n_output = 255;
- } else {
- cookie->n_output = ctx->sflow_n_outputs;
- }
- cookie->vlan_tci = base->vlan_tci;
+ compose_sflow_cookie(ctx->ofproto, base->vlan_tci,
+ ctx->sflow_odp_port, ctx->sflow_n_outputs, cookie);
}
static void
static void
xlate_table_action(struct action_xlate_ctx *ctx,
- uint16_t in_port, uint8_t table_id)
+ uint16_t in_port, uint8_t table_id, bool may_packet_in)
{
if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
struct ofproto_dpif *ofproto = ctx->ofproto;
/* Look up a flow with 'in_port' as the input port. */
old_in_port = ctx->flow.in_port;
ctx->flow.in_port = in_port;
- rule = rule_dpif_lookup(ofproto, &ctx->flow, table_id);
+ rule = rule_dpif_lookup__(ofproto, &ctx->flow, table_id);
/* Tag the flow. */
if (table_id > 0 && table_id < N_TABLES) {
ctx->resubmit_hook(ctx, rule);
}
+ if (rule == NULL && may_packet_in) {
+ /* TODO:XXX
+ * check if table configuration flags
+ * OFPTC_TABLE_MISS_CONTROLLER, default.
+ * OFPTC_TABLE_MISS_CONTINUE,
+ * OFPTC_TABLE_MISS_DROP
+ * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do?
+ */
+ rule = rule_dpif_miss_rule(ofproto, &ctx->flow);
+ }
+
if (rule) {
struct rule_dpif *old_rule = ctx->rule;
ctx->recurse++;
ctx->rule = rule;
- do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx);
+ do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
ctx->rule = old_rule;
ctx->recurse--;
}
}
static void
-xlate_resubmit_table(struct action_xlate_ctx *ctx,
- const struct nx_action_resubmit *nar)
+xlate_ofpact_resubmit(struct action_xlate_ctx *ctx,
+ const struct ofpact_resubmit *resubmit)
{
uint16_t in_port;
uint8_t table_id;
- in_port = (nar->in_port == htons(OFPP_IN_PORT)
- ? ctx->flow.in_port
- : ntohs(nar->in_port));
- table_id = nar->table == 255 ? ctx->table_id : nar->table;
+ in_port = resubmit->in_port;
+ if (in_port == OFPP_IN_PORT) {
+ in_port = ctx->flow.in_port;
+ }
+
+ table_id = resubmit->table_id;
+ if (table_id == 255) {
+ table_id = ctx->table_id;
+ }
- xlate_table_action(ctx, in_port, table_id);
+ xlate_table_action(ctx, in_port, table_id, false);
}
static void
struct ofputil_packet_in pin;
struct ofpbuf *packet;
- ctx->may_set_up_flow = false;
+ ctx->slow |= SLOW_CONTROLLER;
if (!ctx->packet) {
return;
}
}
static bool
-compose_dec_ttl(struct action_xlate_ctx *ctx)
+compose_dec_ttl(struct action_xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
{
if (ctx->flow.dl_type != htons(ETH_TYPE_IP) &&
ctx->flow.dl_type != htons(ETH_TYPE_IPV6)) {
ctx->flow.nw_ttl--;
return false;
} else {
- execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
+ size_t i;
+
+ for (i = 0; i < ids->n_controllers; i++) {
+ execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
+ ids->cnt_ids[i]);
+ }
/* Stop processing for current table. */
return true;
}
static void
-xlate_output_action__(struct action_xlate_ctx *ctx,
- uint16_t port, uint16_t max_len)
+xlate_output_action(struct action_xlate_ctx *ctx,
+ uint16_t port, uint16_t max_len, bool may_packet_in)
{
uint16_t prev_nf_output_iface = ctx->nf_output_iface;
compose_output_action(ctx, ctx->flow.in_port);
break;
case OFPP_TABLE:
- xlate_table_action(ctx, ctx->flow.in_port, ctx->table_id);
+ xlate_table_action(ctx, ctx->flow.in_port, 0, may_packet_in);
break;
case OFPP_NORMAL:
xlate_normal(ctx);
static void
xlate_output_reg_action(struct action_xlate_ctx *ctx,
- const struct nx_action_output_reg *naor)
+ const struct ofpact_output_reg *or)
{
- struct mf_subfield src;
- uint64_t ofp_port;
-
- nxm_decode(&src, naor->src, naor->ofs_nbits);
- ofp_port = mf_get_subfield(&src, &ctx->flow);
-
- if (ofp_port <= UINT16_MAX) {
- xlate_output_action__(ctx, ofp_port, ntohs(naor->max_len));
+ uint64_t port = mf_get_subfield(&or->src, &ctx->flow);
+ if (port <= UINT16_MAX) {
+ xlate_output_action(ctx, port, or->max_len, false);
}
}
-static void
-xlate_output_action(struct action_xlate_ctx *ctx,
- const struct ofp_action_output *oao)
-{
- xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
-}
-
static void
xlate_enqueue_action(struct action_xlate_ctx *ctx,
- const struct ofp_action_enqueue *oae)
+ const struct ofpact_enqueue *enqueue)
{
- uint16_t ofp_port;
+ uint16_t ofp_port = enqueue->port;
+ uint32_t queue_id = enqueue->queue;
uint32_t flow_priority, priority;
int error;
- error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
- &priority);
+ /* Translate queue to priority. */
+ error = dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &priority);
if (error) {
/* Fall back to ordinary output action. */
- xlate_output_action__(ctx, ntohs(oae->port), 0);
+ xlate_output_action(ctx, enqueue->port, 0, false);
return;
}
- /* Figure out datapath output port. */
- ofp_port = ntohs(oae->port);
+ /* Check output port. */
if (ofp_port == OFPP_IN_PORT) {
ofp_port = ctx->flow.in_port;
} else if (ofp_port == ctx->flow.in_port) {
}
static void
-xlate_set_queue_action(struct action_xlate_ctx *ctx,
- const struct nx_action_set_queue *nasq)
+xlate_set_queue_action(struct action_xlate_ctx *ctx, uint32_t queue_id)
{
- uint32_t priority;
- int error;
+ uint32_t skb_priority;
- error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id),
- &priority);
- if (error) {
- /* Couldn't translate queue to a priority, so ignore. A warning
+ if (!dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &skb_priority)) {
+ ctx->flow.skb_priority = skb_priority;
+ } else {
+ /* Couldn't translate queue to a priority. Nothing to do. A warning
* has already been logged. */
- return;
}
-
- ctx->flow.skb_priority = priority;
}
struct xlate_reg_state {
static void
xlate_autopath(struct action_xlate_ctx *ctx,
- const struct nx_action_autopath *naa)
+ const struct ofpact_autopath *ap)
{
- uint16_t ofp_port = ntohl(naa->id);
+ uint16_t ofp_port = ap->port;
struct ofport_dpif *port = get_ofp_port(ctx->ofproto, ofp_port);
if (!port || !port->bundle) {
ofp_port = slave->up.ofp_port;
}
}
- autopath_execute(naa, &ctx->flow, ofp_port);
+ nxm_reg_load(&ap->dst, ofp_port, &ctx->flow);
}
static bool
}
}
+static void
+xlate_bundle_action(struct action_xlate_ctx *ctx,
+ const struct ofpact_bundle *bundle)
+{
+ uint16_t port;
+
+ port = bundle_execute(bundle, &ctx->flow, slave_enabled_cb, ctx->ofproto);
+ if (bundle->dst.field) {
+ nxm_reg_load(&bundle->dst, port, &ctx->flow);
+ } else {
+ xlate_output_action(ctx, port, 0, false);
+ }
+}
+
static void
xlate_learn_action(struct action_xlate_ctx *ctx,
- const struct nx_action_learn *learn)
+ const struct ofpact_learn *learn)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
struct ofputil_flow_mod fm;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
int error;
- learn_execute(learn, &ctx->flow, &fm);
+ ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ learn_execute(learn, &ctx->flow, &fm, &ofpacts);
error = ofproto_flow_mod(&ctx->ofproto->up, &fm);
if (error && !VLOG_DROP_WARN(&rl)) {
ofperr_get_name(error));
}
- free(fm.actions);
+ ofpbuf_uninit(&ofpacts);
}
/* Reduces '*timeout' to no more than 'max'. A value of zero in either case
static void
xlate_fin_timeout(struct action_xlate_ctx *ctx,
- const struct nx_action_fin_timeout *naft)
+ const struct ofpact_fin_timeout *oft)
{
if (ctx->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
struct rule_dpif *rule = ctx->rule;
- reduce_timeout(ntohs(naft->fin_idle_timeout), &rule->up.idle_timeout);
- reduce_timeout(ntohs(naft->fin_hard_timeout), &rule->up.hard_timeout);
+ reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
+ reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
}
}
}
static void
-do_xlate_actions(const union ofp_action *in, size_t n_in,
+do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
struct action_xlate_ctx *ctx)
{
const struct ofport_dpif *port;
- const union ofp_action *ia;
bool was_evictable = true;
- size_t left;
+ const struct ofpact *a;
port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
if (port && !may_receive(port, ctx)) {
was_evictable = ctx->rule->up.evictable;
ctx->rule->up.evictable = false;
}
- OFPUTIL_ACTION_FOR_EACH_UNSAFE (ia, left, in, n_in) {
- const struct ofp_action_dl_addr *oada;
- const struct nx_action_resubmit *nar;
- const struct nx_action_set_tunnel *nast;
- const struct nx_action_set_queue *nasq;
- const struct nx_action_multipath *nam;
- const struct nx_action_autopath *naa;
- const struct nx_action_bundle *nab;
- const struct nx_action_output_reg *naor;
- const struct nx_action_controller *nac;
- enum ofputil_action_code code;
- ovs_be64 tun_id;
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ struct ofpact_controller *controller;
if (ctx->exit) {
break;
}
- code = ofputil_decode_action_unsafe(ia);
- switch (code) {
- case OFPUTIL_OFPAT10_OUTPUT:
- xlate_output_action(ctx, &ia->output);
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
+ ofpact_get_OUTPUT(a)->max_len, true);
+ break;
+
+ case OFPACT_CONTROLLER:
+ controller = ofpact_get_CONTROLLER(a);
+ execute_controller_action(ctx, controller->max_len,
+ controller->reason,
+ controller->controller_id);
break;
- case OFPUTIL_OFPAT10_SET_VLAN_VID:
+ case OFPACT_ENQUEUE:
+ xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a));
+ break;
+
+ case OFPACT_SET_VLAN_VID:
ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
- ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
+ ctx->flow.vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
+ | htons(VLAN_CFI));
break;
- case OFPUTIL_OFPAT10_SET_VLAN_PCP:
+ case OFPACT_SET_VLAN_PCP:
ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
- ctx->flow.vlan_tci |= htons(
- (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
+ ctx->flow.vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
+ << VLAN_PCP_SHIFT)
+ | VLAN_CFI);
break;
- case OFPUTIL_OFPAT10_STRIP_VLAN:
+ case OFPACT_STRIP_VLAN:
ctx->flow.vlan_tci = htons(0);
break;
- case OFPUTIL_OFPAT10_SET_DL_SRC:
- oada = ((struct ofp_action_dl_addr *) ia);
- memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
+ case OFPACT_SET_ETH_SRC:
+ memcpy(ctx->flow.dl_src, ofpact_get_SET_ETH_SRC(a)->mac,
+ ETH_ADDR_LEN);
break;
- case OFPUTIL_OFPAT10_SET_DL_DST:
- oada = ((struct ofp_action_dl_addr *) ia);
- memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
+ case OFPACT_SET_ETH_DST:
+ memcpy(ctx->flow.dl_dst, ofpact_get_SET_ETH_DST(a)->mac,
+ ETH_ADDR_LEN);
break;
- case OFPUTIL_OFPAT10_SET_NW_SRC:
- ctx->flow.nw_src = ia->nw_addr.nw_addr;
+ case OFPACT_SET_IPV4_SRC:
+ ctx->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
break;
- case OFPUTIL_OFPAT10_SET_NW_DST:
- ctx->flow.nw_dst = ia->nw_addr.nw_addr;
+ case OFPACT_SET_IPV4_DST:
+ ctx->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
break;
- case OFPUTIL_OFPAT10_SET_NW_TOS:
+ case OFPACT_SET_IPV4_DSCP:
/* OpenFlow 1.0 only supports IPv4. */
if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
ctx->flow.nw_tos &= ~IP_DSCP_MASK;
- ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK;
+ ctx->flow.nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp;
}
break;
- case OFPUTIL_OFPAT10_SET_TP_SRC:
- ctx->flow.tp_src = ia->tp_port.tp_port;
- break;
-
- case OFPUTIL_OFPAT10_SET_TP_DST:
- ctx->flow.tp_dst = ia->tp_port.tp_port;
+ case OFPACT_SET_L4_SRC_PORT:
+ ctx->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
break;
- case OFPUTIL_OFPAT10_ENQUEUE:
- xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
+ case OFPACT_SET_L4_DST_PORT:
+ ctx->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
break;
- case OFPUTIL_NXAST_RESUBMIT:
- nar = (const struct nx_action_resubmit *) ia;
- xlate_table_action(ctx, ntohs(nar->in_port), ctx->table_id);
+ case OFPACT_RESUBMIT:
+ xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
break;
- case OFPUTIL_NXAST_RESUBMIT_TABLE:
- xlate_resubmit_table(ctx, (const struct nx_action_resubmit *) ia);
+ case OFPACT_SET_TUNNEL:
+ ctx->flow.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
break;
- case OFPUTIL_NXAST_SET_TUNNEL:
- nast = (const struct nx_action_set_tunnel *) ia;
- tun_id = htonll(ntohl(nast->tun_id));
- ctx->flow.tun_id = tun_id;
+ case OFPACT_SET_QUEUE:
+ xlate_set_queue_action(ctx, ofpact_get_SET_QUEUE(a)->queue_id);
break;
- case OFPUTIL_NXAST_SET_QUEUE:
- nasq = (const struct nx_action_set_queue *) ia;
- xlate_set_queue_action(ctx, nasq);
- break;
-
- case OFPUTIL_NXAST_POP_QUEUE:
+ case OFPACT_POP_QUEUE:
ctx->flow.skb_priority = ctx->orig_skb_priority;
break;
- case OFPUTIL_NXAST_REG_MOVE:
- nxm_execute_reg_move((const struct nx_action_reg_move *) ia,
- &ctx->flow);
+ case OFPACT_REG_MOVE:
+ nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->flow);
break;
- case OFPUTIL_NXAST_REG_LOAD:
- nxm_execute_reg_load((const struct nx_action_reg_load *) ia,
- &ctx->flow);
+ case OFPACT_REG_LOAD:
+ nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
break;
- case OFPUTIL_NXAST_NOTE:
- /* Nothing to do. */
- break;
-
- case OFPUTIL_NXAST_SET_TUNNEL64:
- tun_id = ((const struct nx_action_set_tunnel64 *) ia)->tun_id;
- ctx->flow.tun_id = tun_id;
+ case OFPACT_DEC_TTL:
+ if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
+ goto out;
+ }
break;
- case OFPUTIL_NXAST_MULTIPATH:
- nam = (const struct nx_action_multipath *) ia;
- multipath_execute(nam, &ctx->flow);
+ case OFPACT_NOTE:
+ /* Nothing to do. */
break;
- case OFPUTIL_NXAST_AUTOPATH:
- naa = (const struct nx_action_autopath *) ia;
- xlate_autopath(ctx, naa);
+ case OFPACT_MULTIPATH:
+ multipath_execute(ofpact_get_MULTIPATH(a), &ctx->flow);
break;
- case OFPUTIL_NXAST_BUNDLE:
- ctx->ofproto->has_bundle_action = true;
- nab = (const struct nx_action_bundle *) ia;
- xlate_output_action__(ctx, bundle_execute(nab, &ctx->flow,
- slave_enabled_cb,
- ctx->ofproto), 0);
+ case OFPACT_AUTOPATH:
+ xlate_autopath(ctx, ofpact_get_AUTOPATH(a));
break;
- case OFPUTIL_NXAST_BUNDLE_LOAD:
+ case OFPACT_BUNDLE:
ctx->ofproto->has_bundle_action = true;
- nab = (const struct nx_action_bundle *) ia;
- bundle_execute_load(nab, &ctx->flow, slave_enabled_cb,
- ctx->ofproto);
+ xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
break;
- case OFPUTIL_NXAST_OUTPUT_REG:
- naor = (const struct nx_action_output_reg *) ia;
- xlate_output_reg_action(ctx, naor);
+ case OFPACT_OUTPUT_REG:
+ xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a));
break;
- case OFPUTIL_NXAST_LEARN:
+ case OFPACT_LEARN:
ctx->has_learn = true;
if (ctx->may_learn) {
- xlate_learn_action(ctx, (const struct nx_action_learn *) ia);
+ xlate_learn_action(ctx, ofpact_get_LEARN(a));
}
break;
- case OFPUTIL_NXAST_DEC_TTL:
- if (compose_dec_ttl(ctx)) {
- goto out;
- }
- break;
-
- case OFPUTIL_NXAST_EXIT:
+ case OFPACT_EXIT:
ctx->exit = true;
break;
- case OFPUTIL_NXAST_FIN_TIMEOUT:
+ case OFPACT_FIN_TIMEOUT:
ctx->has_fin_timeout = true;
- xlate_fin_timeout(ctx, (const struct nx_action_fin_timeout *) ia);
- break;
-
- case OFPUTIL_NXAST_CONTROLLER:
- nac = (const struct nx_action_controller *) ia;
- execute_controller_action(ctx, ntohs(nac->max_len), nac->reason,
- ntohs(nac->controller_id));
+ xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a));
break;
}
}
ctx->may_learn = packet != NULL;
ctx->tcp_flags = tcp_flags;
ctx->resubmit_hook = NULL;
+ ctx->report_hook = NULL;
ctx->resubmit_stats = NULL;
}
-/* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions in
- * 'odp_actions', using 'ctx'. */
+/* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
+ * into datapath actions in 'odp_actions', using 'ctx'. */
static void
xlate_actions(struct action_xlate_ctx *ctx,
- const union ofp_action *in, size_t n_in,
+ const struct ofpact *ofpacts, size_t ofpacts_len,
struct ofpbuf *odp_actions)
{
/* Normally false. Set to true if we ever hit MAX_RESUBMIT_RECURSION, so
* tracing purposes. */
static bool hit_resubmit_limit;
+ enum slow_path_reason special;
+
COVERAGE_INC(ofproto_dpif_xlate);
ofpbuf_clear(odp_actions);
ctx->odp_actions = odp_actions;
ctx->tags = 0;
- ctx->may_set_up_flow = true;
+ ctx->slow = 0;
ctx->has_learn = false;
ctx->has_normal = false;
ctx->has_fin_timeout = false;
}
}
- if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) {
- ctx->may_set_up_flow = false;
+ special = process_special(ctx->ofproto, &ctx->flow, ctx->packet);
+ if (special) {
+ ctx->slow |= special;
} else {
static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
ovs_be16 initial_tci = ctx->base_flow.vlan_tci;
add_sflow_action(ctx);
- do_xlate_actions(in, n_in, ctx);
+ do_xlate_actions(ofpacts, ofpacts_len, ctx);
if (ctx->max_resubmit_trigger && !ctx->resubmit_hook) {
if (!hit_resubmit_limit) {
if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow,
ctx->odp_actions->data,
ctx->odp_actions->size)) {
- ctx->may_set_up_flow = false;
+ ctx->slow |= SLOW_IN_BAND;
if (ctx->packet
&& connmgr_msg_in_hook(ctx->ofproto->up.connmgr, &ctx->flow,
ctx->packet)) {
}
}
-/* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions,
- * using 'ctx', and discards the datapath actions. */
+/* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts'
+ * into datapath actions, using 'ctx', and discards the datapath actions. */
static void
xlate_actions_for_side_effects(struct action_xlate_ctx *ctx,
- const union ofp_action *in, size_t n_in)
+ const struct ofpact *ofpacts,
+ size_t ofpacts_len)
{
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions;
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
- xlate_actions(ctx, in, n_in, &odp_actions);
+ xlate_actions(ctx, ofpacts, ofpacts_len, &odp_actions);
ofpbuf_uninit(&odp_actions);
}
+
+static void
+xlate_report(struct action_xlate_ctx *ctx, const char *s)
+{
+ if (ctx->report_hook) {
+ ctx->report_hook(ctx, s);
+ }
+}
\f
/* OFPP_NORMAL implementation. */
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)
{
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) {
}
static struct ofbundle *
-lookup_input_bundle(struct ofproto_dpif *ofproto, uint16_t in_port, bool warn,
- struct ofport_dpif **in_ofportp)
+lookup_input_bundle(const struct ofproto_dpif *ofproto, uint16_t in_port,
+ bool warn, struct ofport_dpif **in_ofportp)
{
struct ofport_dpif *ofport;
* we don't know about.
*
* - The ofproto client didn't configure the port as part of a bundle.
+ * This is particularly likely to happen if a packet was received on the
+ * port after it was created, but before the client had a chance to
+ * configure its bundle.
*/
if (warn) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
* so in one special case.
*/
static bool
-is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
- struct ofport_dpif *in_port, uint16_t vlan, tag_type *tags)
+is_admissible(struct action_xlate_ctx *ctx, struct ofport_dpif *in_port,
+ uint16_t vlan)
{
+ struct ofproto_dpif *ofproto = ctx->ofproto;
+ struct flow *flow = &ctx->flow;
struct ofbundle *in_bundle = in_port->bundle;
/* Drop frames for reserved multicast addresses
* only if forward_bpdu option is absent. */
- if (eth_addr_is_reserved(flow->dl_dst) && !ofproto->up.forward_bpdu) {
+ if (!ofproto->up.forward_bpdu && eth_addr_is_reserved(flow->dl_dst)) {
+ xlate_report(ctx, "packet has reserved destination MAC, dropping");
return false;
}
struct mac_entry *mac;
switch (bond_check_admissibility(in_bundle->bond, in_port,
- flow->dl_dst, tags)) {
+ flow->dl_dst, &ctx->tags)) {
case BV_ACCEPT:
break;
case BV_DROP:
+ xlate_report(ctx, "bonding refused admissibility, dropping");
return false;
case BV_DROP_IF_MOVED:
if (mac && mac->port.p != in_bundle &&
(!is_gratuitous_arp(flow)
|| mac_entry_is_grat_arp_locked(mac))) {
+ xlate_report(ctx, "SLB bond thinks this packet looped back, "
+ "dropping");
return false;
}
break;
in_bundle = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port,
ctx->packet != NULL, &in_port);
if (!in_bundle) {
+ xlate_report(ctx, "no input bundle, dropping");
return;
}
"VLAN tag received on port %s",
ctx->ofproto->up.name, in_bundle->name);
}
+ xlate_report(ctx, "partial VLAN tag, dropping");
return;
}
"%s, which is reserved exclusively for mirroring",
ctx->ofproto->up.name, in_bundle->name);
}
+ xlate_report(ctx, "input port is mirror output port, dropping");
return;
}
/* Check VLAN. */
vid = vlan_tci_to_vid(ctx->flow.vlan_tci);
if (!input_vid_is_valid(vid, in_bundle, ctx->packet != NULL)) {
+ xlate_report(ctx, "disallowed VLAN VID for this input port, dropping");
return;
}
vlan = input_vid_to_vlan(in_bundle, vid);
/* Check other admissibility requirements. */
- if (in_port &&
- !is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) {
+ if (in_port && !is_admissible(ctx, in_port, vlan)) {
return;
}
&ctx->tags);
if (mac) {
if (mac->port.p != in_bundle) {
+ xlate_report(ctx, "forwarding to learned port");
output_normal(ctx, mac->port.p, vlan);
+ } else {
+ xlate_report(ctx, "learned port is input port, dropping");
}
} else {
struct ofbundle *bundle;
+ xlate_report(ctx, "no learned MAC for destination, flooding");
HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
if (bundle != in_bundle
&& ofbundle_includes_vlan(bundle, vlan)
if (table->catchall_table != catchall || table->other_table != other) {
table->catchall_table = catchall;
table->other_table = other;
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_FLOW_TABLE;
}
}
if (table->other_table && rule->tag) {
tag_set_add(&ofproto->revalidate_set, rule->tag);
} else {
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_FLOW_TABLE;
}
}
}
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
if (frag_handling != OFPC_FRAG_REASM) {
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
return true;
} else {
return false;
static enum ofperr
packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
const struct flow *flow,
- const union ofp_action *ofp_actions, size_t n_ofp_actions)
+ const struct ofpact *ofpacts, size_t ofpacts_len)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
enum ofperr error;
return OFPERR_NXBRC_BAD_IN_PORT;
}
- error = validate_actions(ofp_actions, n_ofp_actions, flow,
- ofproto->max_ports);
+ error = ofpacts_check(ofpacts, ofpacts_len, flow, ofproto->max_ports);
if (!error) {
struct odputil_keybuf keybuf;
struct dpif_flow_stats stats;
ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
odp_flow_key_from_flow(&key, flow);
- dpif_flow_stats_extract(flow, packet, &stats);
+ dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, NULL,
packet_get_tcp_flags(packet, flow), packet);
ofpbuf_use_stub(&odp_actions,
odp_actions_stub, sizeof odp_actions_stub);
- xlate_actions(&ctx, ofp_actions, n_ofp_actions, &odp_actions);
+ xlate_actions(&ctx, ofpacts, ofpacts_len, &odp_actions);
dpif_execute(ofproto->dpif, key.data, key.size,
odp_actions.data, odp_actions.size, packet);
ofpbuf_uninit(&odp_actions);
struct ofexpired expired;
LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
- if (subfacet->installed) {
+ if (subfacet->path == SF_FAST_PATH) {
struct dpif_flow_stats stats;
- subfacet_install(subfacet, subfacet->actions,
- subfacet->actions_len, &stats);
+ subfacet_reinstall(subfacet, &stats);
subfacet_update_stats(subfacet, &stats);
}
}
ds_put_char_multiple(result, '\t', level);
ds_put_cstr(result, "OpenFlow ");
- ofp_print_actions(result, rule->up.actions, rule->up.n_actions);
+ ofpacts_format(rule->up.ofpacts, rule->up.ofpacts_len, result);
ds_put_char(result, '\n');
}
trace_format_rule(result, ctx->table_id, ctx->recurse + 1, rule);
}
+static void
+trace_report(struct action_xlate_ctx *ctx, const char *s)
+{
+ struct trace_ctx *trace = CONTAINER_OF(ctx, struct trace_ctx, ctx);
+ struct ds *result = trace->result;
+
+ ds_put_char_multiple(result, '\t', ctx->recurse);
+ ds_put_cstr(result, s);
+ ds_put_char(result, '\n');
+}
+
static void
ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
void *aux OVS_UNUSED)
/* ofproto/trace dpname flow [-generate] */
const char *flow_s = argv[2];
const char *generate_s = argv[3];
- int error;
- /* Convert string to datapath key. */
- ofpbuf_init(&odp_key, 0);
- error = odp_flow_key_from_string(flow_s, NULL, &odp_key);
- if (error) {
- unixctl_command_reply_error(conn, "Bad flow syntax");
- goto exit;
- }
+ /* Allow 'flow_s' to be either a datapath flow or an OpenFlow-like
+ * flow. We guess which type it is based on whether 'flow_s' contains
+ * an '(', since a datapath flow always contains '(') but an
+ * OpenFlow-like flow should not (in fact it's allowed but I believe
+ * that's not documented anywhere).
+ *
+ * An alternative would be to try to parse 'flow_s' both ways, but then
+ * it would be tricky giving a sensible error message. After all, do
+ * you just say "syntax error" or do you present both error messages?
+ * Both choices seem lousy. */
+ if (strchr(flow_s, '(')) {
+ int error;
+
+ /* Convert string to datapath key. */
+ ofpbuf_init(&odp_key, 0);
+ error = odp_flow_key_from_string(flow_s, NULL, &odp_key);
+ if (error) {
+ unixctl_command_reply_error(conn, "Bad flow syntax");
+ goto exit;
+ }
- /* Convert odp_key to flow. */
- error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data,
- odp_key.size, &flow,
- &initial_tci, NULL);
- if (error == ODP_FIT_ERROR) {
- unixctl_command_reply_error(conn, "Invalid flow");
- goto exit;
+ /* Convert odp_key to flow. */
+ error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data,
+ odp_key.size, &flow,
+ &initial_tci, NULL);
+ if (error == ODP_FIT_ERROR) {
+ unixctl_command_reply_error(conn, "Invalid flow");
+ goto exit;
+ }
+ } else {
+ char *error_s;
+
+ error_s = parse_ofp_exact_flow(&flow, argv[2]);
+ if (error_s) {
+ unixctl_command_reply_error(conn, error_s);
+ free(error_s);
+ goto exit;
+ }
+
+ initial_tci = flow.vlan_tci;
+ vsp_adjust_flow(ofproto, &flow);
}
/* Generate a packet, if requested. */
flow_format(ds, flow);
ds_put_char(ds, '\n');
- rule = rule_dpif_lookup(ofproto, flow, 0);
+ rule = rule_dpif_lookup(ofproto, flow);
+
trace_format_rule(ds, 0, 0, rule);
+ if (rule == ofproto->miss_rule) {
+ ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n");
+ } else if (rule == ofproto->no_packet_in_rule) {
+ ds_put_cstr(ds, "\nNo match, packets dropped because "
+ "OFPPC_NO_PACKET_IN is set on in_port.\n");
+ }
+
if (rule) {
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions;
action_xlate_ctx_init(&trace.ctx, ofproto, flow, initial_tci,
rule, tcp_flags, packet);
trace.ctx.resubmit_hook = trace_resubmit;
- xlate_actions(&trace.ctx, rule->up.actions, rule->up.n_actions,
+ trace.ctx.report_hook = trace_report;
+ xlate_actions(&trace.ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
ds_put_char(ds, '\n');
format_odp_actions(ds, odp_actions.data, odp_actions.size);
ofpbuf_uninit(&odp_actions);
- if (!trace.ctx.may_set_up_flow) {
- if (packet) {
- ds_put_cstr(ds, "\nThis flow is not cachable.");
- } else {
- ds_put_cstr(ds, "\nThe datapath actions are incomplete--"
- "for complete actions, please supply a packet.");
+ if (trace.ctx.slow) {
+ enum slow_path_reason slow;
+
+ ds_put_cstr(ds, "\nThis flow is handled by the userspace "
+ "slow path because it:");
+ for (slow = trace.ctx.slow; slow; ) {
+ enum slow_path_reason bit = rightmost_1bit(slow);
+
+ switch (bit) {
+ case SLOW_CFM:
+ ds_put_cstr(ds, "\n\t- Consists of CFM packets.");
+ break;
+ case SLOW_LACP:
+ ds_put_cstr(ds, "\n\t- Consists of LACP packets.");
+ break;
+ case SLOW_STP:
+ ds_put_cstr(ds, "\n\t- Consists of STP packets.");
+ break;
+ case SLOW_IN_BAND:
+ ds_put_cstr(ds, "\n\t- Needs in-band special case "
+ "processing.");
+ if (!packet) {
+ ds_put_cstr(ds, "\n\t (The datapath actions are "
+ "incomplete--for complete actions, "
+ "please supply a packet.)");
+ }
+ break;
+ case SLOW_CONTROLLER:
+ ds_put_cstr(ds, "\n\t- Sends \"packet-in\" messages "
+ "to the OpenFlow controller.");
+ break;
+ case SLOW_MATCH:
+ ds_put_cstr(ds, "\n\t- Needs more specific matching "
+ "than the datapath supports.");
+ break;
+ }
+
+ slow &= ~bit;
+ }
+
+ if (slow & ~SLOW_MATCH) {
+ ds_put_cstr(ds, "\nThe datapath actions above do not reflect "
+ "the special slow-path processing.");
}
}
}
}
}
if (errors) {
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_INCONSISTENCY;
}
if (errors) {
return 0;
}
- ofproto->need_revalidate = true;
+ ofproto->need_revalidate = REV_RECONFIGURE;
if (ofport->realdev_ofp_port) {
vsp_remove(ofport);
return 0;
}
+/* Given 'flow', a flow representing a packet received on 'ofproto', checks
+ * whether 'flow->in_port' represents a Linux VLAN device. If so, changes
+ * 'flow->in_port' to the "real" device backing the VLAN device, sets
+ * 'flow->vlan_tci' to the VLAN VID, and returns true. Otherwise (which is
+ * always the case unless VLAN splinters are enabled), returns false without
+ * making any changes. */
+static bool
+vsp_adjust_flow(const struct ofproto_dpif *ofproto, struct flow *flow)
+{
+ uint16_t realdev;
+ int vid;
+
+ realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port, &vid);
+ if (!realdev) {
+ return false;
+ }
+
+ /* Cause the flow to be processed as if it came in on the real device with
+ * the VLAN device's VLAN ID. */
+ flow->in_port = realdev;
+ flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI);
+ return true;
+}
+
static void
vsp_remove(struct ofport_dpif *port)
{
run,
run_fast,
wait,
+ get_memory_usage,
flush,
get_features,
get_tables,
set_sflow,
set_cfm,
get_cfm_fault,
+ get_cfm_opup,
get_cfm_remote_mpids,
get_cfm_health,
set_stp,
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "shash.h"
#include "timeval.h"
+struct ofpact;
struct ofputil_flow_mod;
+struct simap;
/* An OpenFlow switch.
*
unsigned int n_pending; /* list_size(&pending). */
struct hmap deletions; /* All OFOPERATION_DELETE "ofoperation"s. */
+ /* Flow table operation logging. */
+ int n_add, n_delete, n_modify; /* Number of unreported ops of each kind. */
+ long long int first_op, last_op; /* Range of times for unreported ops. */
+ long long int next_op_report; /* Time to report ops, or LLONG_MAX. */
+ long long int op_backoff; /* Earliest time to report ops again. */
+
/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
*
* This is deprecated. It is only for compatibility with broken device
struct heap_node evg_node; /* In eviction_group's "rules" heap. */
struct eviction_group *eviction_group; /* NULL if not in any group. */
- union ofp_action *actions; /* OpenFlow actions. */
- int n_actions; /* Number of elements in actions[]. */
+ struct ofpact *ofpacts; /* Sequence of "struct ofpacts". */
+ unsigned int ofpacts_len; /* Size of 'ofpacts', in bytes. */
+
+ /* Flow monitors. */
+ enum nx_flow_monitor_flags monitor_flags;
+ uint64_t add_seqno; /* Sequence number when added. */
+ uint64_t modify_seqno; /* Sequence number when changed. */
};
static inline struct rule *
void ofproto_rule_expire(struct rule *, uint8_t reason);
void ofproto_rule_destroy(struct rule *);
+bool ofproto_rule_has_out_port(const struct rule *, uint16_t out_port);
+
void ofoperation_complete(struct ofoperation *, enum ofperr);
struct rule *ofoperation_get_victim(struct ofoperation *);
+bool ofoperation_has_out_port(const struct ofoperation *, uint16_t out_port);
+
+bool ofproto_rule_is_hidden(const struct rule *);
+
/* ofproto class structure, to be defined by each ofproto implementation.
*
*
* - Call ofproto_rule_expire() for each OpenFlow flow that has reached
* its hard_timeout or idle_timeout, to expire the flow.
*
+ * (But rules that are part of a pending operation, e.g. rules for
+ * which ->pending is true, may not expire.)
+ *
* Returns 0 if successful, otherwise a positive errno value. */
int (*run)(struct ofproto *ofproto);
* 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
*
* - 'name' to "table#" where # is the table ID.
*
- * - 'wildcards' to OFPFW_ALL.
+ * - 'wildcards' to OFPFW10_ALL.
*
* - 'max_entries' to 1,000,000.
*
* - 'matched_count' to the number of packets looked up in this flow
* table so far that matched one of the flow entries.
*
- * Keep in mind that all of the members of struct ofp_table_stats are in
+ * Keep in mind that all of the members of struct ofp10_table_stats are in
* network byte order.
*/
- void (*get_tables)(struct ofproto *ofproto, struct ofp_table_stats *ots);
+ void (*get_tables)(struct ofproto *ofproto, struct ofp10_table_stats *ots);
/* ## ---------------- ## */
/* ## ofport Functions ## */
* registers, then it is an error if 'rule->cr' does not wildcard all
* registers.
*
- * - Validate that 'rule->actions' and 'rule->n_actions' are well-formed
- * OpenFlow actions that the datapath can correctly implement. The
- * validate_actions() function (in ofp-util.c) can be useful as a model
- * for action validation, but it accepts all of the OpenFlow actions
- * that OVS understands. If your ofproto implementation only
- * implements a subset of those, then you should implement your own
- * action validation.
+ * - Validate that 'rule->ofpacts' is a sequence of well-formed actions
+ * that the datapath can correctly implement. If your ofproto
+ * implementation only implements a subset of the actions that Open
+ * vSwitch understands, then you should implement your own action
+ * validation.
*
* - If the rule is valid, update the datapath flow table, adding the new
* rule or replacing the existing one.
enum ofp_config_flags frag_handling);
/* Implements the OpenFlow OFPT_PACKET_OUT command. The datapath should
- * execute the 'n_actions' in the 'actions' array on 'packet'.
+ * execute the 'ofpacts_len' bytes of "struct ofpacts" in 'ofpacts'.
*
- * The caller retains ownership of 'packet', so ->packet_out() should not
- * modify or free it.
+ * The caller retains ownership of 'packet' and of 'ofpacts', so
+ * ->packet_out() should not modify or free them.
*
- * This function must validate that the 'n_actions' elements in 'actions'
- * are well-formed OpenFlow actions that can be correctly implemented by
- * the datapath. If not, then it should return an OpenFlow error code.
+ * This function must validate that it can implement 'ofpacts'. If not,
+ * then it should return an OpenFlow error code.
*
* 'flow' reflects the flow information for 'packet'. All of the
* information in 'flow' is extracted from 'packet', except for
- * flow->in_port, which is taken from the OFPT_PACKET_OUT message.
- * flow->tun_id and its register values are zeroed.
+ * flow->in_port (see below). flow->tun_id and its register values are
+ * zeroed.
+ *
+ * flow->in_port comes from the OpenFlow OFPT_PACKET_OUT message. The
+ * implementation should reject invalid flow->in_port values by returning
+ * OFPERR_NXBRC_BAD_IN_PORT. For consistency, the implementation should
+ * consider valid for flow->in_port any value that could possibly be seen
+ * in a packet that it passes to connmgr_send_packet_in(). Ideally, even
+ * an implementation that never generates packet-ins (e.g. due to hardware
+ * limitations) should still allow flow->in_port values for every possible
+ * physical port and OFPP_LOCAL. The only virtual ports (those above
+ * OFPP_MAX) that the caller will ever pass in as flow->in_port, other than
+ * OFPP_LOCAL, are OFPP_NONE and OFPP_CONTROLLER. The implementation
+ * should allow both of these, treating each of them as packets generated
+ * by the controller as opposed to packets originating from some switch
+ * port.
+ *
+ * (Ordinarily the only effect of flow->in_port is on output actions that
+ * involve the input port, such as actions that output to OFPP_IN_PORT,
+ * OFPP_FLOOD, or OFPP_ALL. flow->in_port can also affect Nicira extension
+ * "resubmit" actions.)
*
* 'packet' is not matched against the OpenFlow flow table, so its
* statistics should not be included in OpenFlow flow statistics.
* Returns 0 if successful, otherwise an OpenFlow error code. */
enum ofperr (*packet_out)(struct ofproto *ofproto, struct ofpbuf *packet,
const struct flow *flow,
- const union ofp_action *actions,
- size_t n_actions);
+ const struct ofpact *ofpacts,
+ size_t ofpacts_len);
/* ## ------------------------- ## */
/* ## OFPP_NORMAL configuration ## */
* not support CFM. */
int (*get_cfm_fault)(const struct ofport *ofport);
+ /* Check the operational status reported by the remote CFM endpoint of
+ * 'ofp_port' Returns 1 if operationally up, 0 if operationally down, and
+ * -1 if CFM is not enabled on 'ofp_port' or does not support operational
+ * status.
+ *
+ * This function may be a null pointer if the ofproto implementation does
+ * not support CFM. */
+ int (*get_cfm_opup)(const struct ofport *ofport);
+
/* Gets the MPIDs of the remote maintenance points broadcasting to
* 'ofport'. Populates 'rmps' with a provider owned array of MPIDs, and
* 'n_rmps' with the number of MPIDs in 'rmps'. Returns a number less than
int ofproto_flow_mod(struct ofproto *, const struct ofputil_flow_mod *);
void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
- const union ofp_action *, size_t n_actions);
+ const struct ofpact *ofpacts, size_t ofpacts_len);
bool ofproto_delete_flow(struct ofproto *, const struct cls_rule *);
void ofproto_flush_flows(struct ofproto *);
that may be used on \fBofproto/trace\fR.
.
.IP "\fBofproto/trace \fIswitch priority tun_id in_port packet\fR"
-.IQ "\fBofproto/trace \fIswitch odp_flow \fB\-generate\fR"
+.IQ "\fBofproto/trace \fIswitch flow \fB\-generate\fR"
Traces the path of an imaginary packet through \fIswitch\fR. Both
forms require \fIswitch\fR, the switch on which the packet arrived
(one of those listed by \fBofproto/list\fR). The first form specifies
.IP
The second form specifies the packet's contents implicitly:
.RS
-.IP "\fIodp_flow\fR"
-A flow in the form printed by \fBovs\-dpctl\fR(8)'s \fBdump\-flows\fR
+.IP "\fIflow\fR"
+A flow in one of two forms: either the form printed by
+\fBovs\-dpctl\fR(8)'s \fBdump\-flows\fR command, or in a format
+similar to that accepted by \fBovs\-ofctl\fR(8)'s \fBadd\-flow\fR
command. This is not an OpenFlow flow: besides other differences, it
never contains wildcards. \fB\*(PN\fR generates an arbitrary packet
-that has the specified \fIodp_flow\fR.
+that has the specified \fIflow\fR.
.RE
.IP
\fB\*(PN\fR will respond with extensive information on how the packet
would be handled if it were to be received. The packet will not
actually be sent, but side effects such as MAC learning will occur.
.
-.IP "\fBofproto/trace \fIswitch odp_flow\fR"
+.IP "\fBofproto/trace \fIswitch flow\fR"
Traces the path of a packet in an imaginary flow through
\fIswitch\fR. The arguments are:
.RS
.IP "\fIswitch\fR"
The switch on which the packet arrived (one of those listed by
\fBofproto/list\fR).
-.IP "\fIodp_flow\fR"
-A flow in the form printed by \fBovs\-dpctl\fR(8)'s \fBdump\-flows\fR
+.IP "\fIflow\fR"
+A flow in one of two forms: either the form printed by
+\fBovs\-dpctl\fR(8)'s \fBdump\-flows\fR command, or in a format
+similar to that accepted by \fBovs\-ofctl\fR(8)'s \fBadd\-flow\fR
command. This is not an OpenFlow flow: besides other differences, it
never contains wildcards.
.RE
.IP
\fB\*(PN\fR will respond with extensive information on how a packet
-in \fIodp_flow\fR would be handled if it were received by
+in \fIflow\fR would be handled if it were received by
\fIswitch\fR. No packet will actually be sent. Some side effects may
occur, but MAC learning in particular will not.
.IP
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
* Copyright (c) 2010 Jean Tourrilhes - HP-Labs.
*
* Licensed under the Apache License, Version 2.0 (the "License");
#include "meta-flow.h"
#include "netdev.h"
#include "nx-match.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
+#include "ofp-msgs.h"
#include "ofp-print.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "poll-loop.h"
#include "random.h"
#include "shash.h"
+#include "simap.h"
#include "sset.h"
#include "timeval.h"
#include "unaligned.h"
struct ofproto *ofproto; /* Owning ofproto. */
struct list ofproto_node; /* In ofproto's "pending" list. */
struct list ops; /* List of "struct ofoperation"s. */
+ int n_running; /* Number of ops still pending. */
/* Data needed to send OpenFlow reply on failure or to send a buffered
* packet on success.
struct ofconn *ofconn; /* ofconn for reply (but see note above). */
struct ofp_header *request; /* Original request (truncated at 64 bytes). */
uint32_t buffer_id; /* Buffer id from original request. */
- int error; /* 0 if no error yet, otherwise error code. */
};
static struct ofopgroup *ofopgroup_create_unattached(struct ofproto *);
const struct ofp_header *,
uint32_t buffer_id);
static void ofopgroup_submit(struct ofopgroup *);
-static void ofopgroup_destroy(struct ofopgroup *);
+static void ofopgroup_complete(struct ofopgroup *);
/* A single flow table operation. */
struct ofoperation {
struct hmap_node hmap_node; /* In ofproto's "deletions" hmap. */
struct rule *rule; /* Rule being operated upon. */
enum ofoperation_type type; /* Type of operation. */
- int status; /* -1 if pending, otherwise 0 or error code. */
- struct rule *victim; /* OFOPERATION_ADDING: Replaced rule. */
- union ofp_action *actions; /* OFOPERATION_MODIFYING: Replaced actions. */
- int n_actions; /* OFOPERATION_MODIFYING: # of old actions. */
+
+ /* OFOPERATION_ADD. */
+ struct rule *victim; /* Rule being replaced, if any.. */
+
+ /* OFOPERATION_MODIFY: The old actions, if the actions are changing. */
+ struct ofpact *ofpacts;
+ size_t ofpacts_len;
+
+ /* OFOPERATION_DELETE. */
+ enum ofp_flow_removed_reason reason; /* Reason flow was removed. */
+
ovs_be64 flow_cookie; /* Rule's old flow cookie. */
+ enum ofperr error; /* 0 if no error. */
};
-static void ofoperation_create(struct ofopgroup *, struct rule *,
- enum ofoperation_type);
+static struct ofoperation *ofoperation_create(struct ofopgroup *,
+ struct rule *,
+ enum ofoperation_type,
+ enum ofp_flow_removed_reason);
static void ofoperation_destroy(struct ofoperation *);
/* oftable. */
static void ofproto_rule_destroy__(struct rule *);
static void ofproto_rule_send_removed(struct rule *, uint8_t reason);
static bool rule_is_modifiable(const struct rule *);
-static bool rule_is_hidden(const struct rule *);
/* OpenFlow. */
static enum ofperr add_flow(struct ofproto *, struct ofconn *,
list_init(&ofproto->pending);
ofproto->n_pending = 0;
hmap_init(&ofproto->deletions);
+ ofproto->n_add = ofproto->n_delete = ofproto->n_modify = 0;
+ ofproto->first_op = ofproto->last_op = LLONG_MIN;
+ ofproto->next_op_report = LLONG_MAX;
+ ofproto->op_backoff = LLONG_MIN;
ofproto->vlan_bitmap = NULL;
ofproto->vlans_changed = false;
ofproto->min_mtu = INT_MAX;
assert(ofproto->n_tables);
ofproto->datapath_id = pick_datapath_id(ofproto);
- VLOG_INFO("using datapath ID %016"PRIx64, ofproto->datapath_id);
init_ports(ofproto);
*ofprotop = ofproto;
}
}
+uint64_t
+ofproto_get_datapath_id(const struct ofproto *ofproto)
+{
+ return ofproto->datapath_id;
+}
+
void
ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id)
{
uint64_t old_dpid = p->datapath_id;
p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p);
if (p->datapath_id != old_dpid) {
- VLOG_INFO("datapath ID changed to %016"PRIx64, p->datapath_id);
-
/* Force all active connections to reconnect, since there is no way to
* notify a controller that the datapath ID has changed. */
ofproto_reconnect_controllers(p);
if (mfr_desc) {
if (strlen(mfr_desc) >= sizeof ods->mfr_desc) {
- VLOG_WARN("truncating mfr_desc, must be less than %zu characters",
- sizeof ods->mfr_desc);
+ VLOG_WARN("%s: truncating mfr_desc, must be less than %zu bytes",
+ p->name, sizeof ods->mfr_desc);
}
free(p->mfr_desc);
p->mfr_desc = xstrdup(mfr_desc);
}
if (hw_desc) {
if (strlen(hw_desc) >= sizeof ods->hw_desc) {
- VLOG_WARN("truncating hw_desc, must be less than %zu characters",
- sizeof ods->hw_desc);
+ VLOG_WARN("%s: truncating hw_desc, must be less than %zu bytes",
+ p->name, sizeof ods->hw_desc);
}
free(p->hw_desc);
p->hw_desc = xstrdup(hw_desc);
}
if (sw_desc) {
if (strlen(sw_desc) >= sizeof ods->sw_desc) {
- VLOG_WARN("truncating sw_desc, must be less than %zu characters",
- sizeof ods->sw_desc);
+ VLOG_WARN("%s: truncating sw_desc, must be less than %zu bytes",
+ p->name, sizeof ods->sw_desc);
}
free(p->sw_desc);
p->sw_desc = xstrdup(sw_desc);
}
if (serial_desc) {
if (strlen(serial_desc) >= sizeof ods->serial_num) {
- VLOG_WARN("truncating serial_desc, must be less than %zu "
- "characters",
- sizeof ods->serial_num);
+ VLOG_WARN("%s: truncating serial_desc, must be less than %zu "
+ "bytes", p->name, sizeof ods->serial_num);
}
free(p->serial_desc);
p->serial_desc = xstrdup(serial_desc);
}
if (dp_desc) {
if (strlen(dp_desc) >= sizeof ods->dp_desc) {
- VLOG_WARN("truncating dp_desc, must be less than %zu characters",
- sizeof ods->dp_desc);
+ VLOG_WARN("%s: truncating dp_desc, must be less than %zu bytes",
+ p->name, sizeof ods->dp_desc);
}
free(p->dp_desc);
p->dp_desc = xstrdup(dp_desc);
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
if (!ofport) {
- VLOG_WARN("%s: cannot get STP status on nonexistent port %"PRIu16,
- ofproto->name, ofp_port);
+ VLOG_WARN_RL(&rl, "%s: cannot get STP status on nonexistent "
+ "port %"PRIu16, ofproto->name, ofp_port);
return ENODEV;
}
cls_cursor_init(&cursor, &table->cls, NULL);
CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
if (!rule->pending) {
- ofoperation_create(group, rule, OFOPERATION_DELETE);
+ ofoperation_create(group, rule, OFOPERATION_DELETE,
+ OFPRR_DELETE);
oftable_remove_rule(rule);
ofproto->ofproto_class->rule_destruct(rule);
}
NOT_REACHED();
}
+ if (time_msec() >= p->next_op_report) {
+ long long int ago = (time_msec() - p->first_op) / 1000;
+ long long int interval = (p->last_op - p->first_op) / 1000;
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_format(&s, "%d flow_mods ",
+ p->n_add + p->n_delete + p->n_modify);
+ if (interval == ago) {
+ ds_put_format(&s, "in the last %lld s", ago);
+ } else if (interval) {
+ ds_put_format(&s, "in the %lld s starting %lld s ago",
+ interval, ago);
+ } else {
+ ds_put_format(&s, "%lld s ago", ago);
+ }
+
+ ds_put_cstr(&s, " (");
+ if (p->n_add) {
+ ds_put_format(&s, "%d adds, ", p->n_add);
+ }
+ if (p->n_delete) {
+ ds_put_format(&s, "%d deletes, ", p->n_delete);
+ }
+ if (p->n_modify) {
+ ds_put_format(&s, "%d modifications, ", p->n_modify);
+ }
+ s.length -= 2;
+ ds_put_char(&s, ')');
+
+ VLOG_INFO("%s: %s", p->name, ds_cstr(&s));
+ ds_destroy(&s);
+
+ p->n_add = p->n_delete = p->n_modify = 0;
+ p->next_op_report = LLONG_MAX;
+ }
+
return error;
}
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)
* (0...65535, inclusive) then the flow will be visible to OpenFlow
* controllers; otherwise, it will be hidden.
*
- * The caller retains ownership of 'cls_rule' and 'actions'.
+ * The caller retains ownership of 'cls_rule' and 'ofpacts'.
*
* This is a helper function for in-band control and fail-open. */
void
ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule,
- const union ofp_action *actions, size_t n_actions)
+ const struct ofpact *ofpacts, size_t ofpacts_len)
{
const struct rule *rule;
rule = rule_from_cls_rule(classifier_find_rule_exactly(
&ofproto->tables[0].cls, cls_rule));
- if (!rule || !ofputil_actions_equal(rule->actions, rule->n_actions,
- actions, n_actions)) {
+ if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
+ ofpacts, ofpacts_len)) {
struct ofputil_flow_mod fm;
memset(&fm, 0, sizeof fm);
fm.cr = *cls_rule;
fm.buffer_id = UINT32_MAX;
- fm.actions = (union ofp_action *) actions;
- fm.n_actions = n_actions;
+ fm.ofpacts = xmemdup(ofpacts, ofpacts_len);
+ fm.ofpacts_len = ofpacts_len;
add_flow(ofproto, NULL, &fm, NULL);
+ free(fm.ofpacts);
}
}
} else {
/* Initiate deletion -> success. */
struct ofopgroup *group = ofopgroup_create_unattached(ofproto);
- ofoperation_create(group, rule, OFOPERATION_DELETE);
+ ofoperation_create(group, rule, OFOPERATION_DELETE, OFPRR_DELETE);
oftable_remove_rule(rule);
ofproto->ofproto_class->rule_destruct(rule);
ofopgroup_submit(group);
sset_destroy(&devnames);
}
-/* Opens and returns a netdev for 'ofproto_port', or a null pointer if the
- * netdev cannot be opened. On success, also fills in 'opp'. */
+/* Opens and returns a netdev for 'ofproto_port' in 'ofproto', or a null
+ * pointer if the netdev cannot be opened. On success, also fills in
+ * 'opp'. */
static struct netdev *
-ofport_open(const struct ofproto_port *ofproto_port,
+ofport_open(const struct ofproto *ofproto,
+ const struct ofproto_port *ofproto_port,
struct ofputil_phy_port *pp)
{
enum netdev_flags flags;
error = netdev_open(ofproto_port->name, ofproto_port->type, &netdev);
if (error) {
- VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s "
+ VLOG_WARN_RL(&rl, "%s: ignoring port %s (%"PRIu16") because netdev %s "
"cannot be opened (%s)",
+ ofproto->name,
ofproto_port->name, ofproto_port->ofp_port,
ofproto_port->name, strerror(error));
return NULL;
/* Fetch 'name''s location and properties from the datapath. */
netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port)
- ? ofport_open(&ofproto_port, &pp)
+ ? ofport_open(ofproto, &ofproto_port, &pp)
: NULL);
if (netdev) {
port = ofproto_get_port(ofproto, ofproto_port.ofp_port);
OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
uint16_t ofp_port = ofproto_port.ofp_port;
if (ofproto_get_port(p, ofp_port)) {
- VLOG_WARN_RL(&rl, "ignoring duplicate port %"PRIu16" in datapath",
- ofp_port);
+ VLOG_WARN_RL(&rl, "%s: ignoring duplicate port %"PRIu16" "
+ "in datapath", p->name, ofp_port);
} else if (shash_find(&p->port_by_name, ofproto_port.name)) {
- VLOG_WARN_RL(&rl, "ignoring duplicate device %s in datapath",
- ofproto_port.name);
+ VLOG_WARN_RL(&rl, "%s: ignoring duplicate device %s in datapath",
+ p->name, ofproto_port.name);
} else {
struct ofputil_phy_port pp;
struct netdev *netdev;
- netdev = ofport_open(&ofproto_port, &pp);
+ netdev = ofport_open(p, &ofproto_port, &pp);
if (netdev) {
ofport_install(p, netdev, &pp);
}
ofproto_rule_destroy__(struct rule *rule)
{
if (rule) {
- free(rule->actions);
+ free(rule->ofpacts);
rule->ofproto->ofproto_class->rule_dealloc(rule);
}
}
}
/* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
- * that outputs to 'out_port' (output to OFPP_FLOOD and OFPP_ALL doesn't
- * count). */
-static bool
-rule_has_out_port(const struct rule *rule, uint16_t out_port)
+ * that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */
+bool
+ofproto_rule_has_out_port(const struct rule *rule, uint16_t port)
{
- const union ofp_action *oa;
- size_t left;
+ return (port == OFPP_NONE
+ || ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port));
+}
- if (out_port == OFPP_NONE) {
+/* Returns true if a rule related to 'op' has an OpenFlow OFPAT_OUTPUT or
+ * OFPAT_ENQUEUE action that outputs to 'out_port'. */
+bool
+ofoperation_has_out_port(const struct ofoperation *op, uint16_t out_port)
+{
+ if (ofproto_rule_has_out_port(op->rule, out_port)) {
return true;
}
- OFPUTIL_ACTION_FOR_EACH_UNSAFE (oa, left, rule->actions, rule->n_actions) {
- if (action_outputs_to_port(oa, htons(out_port))) {
- return true;
- }
+
+ switch (op->type) {
+ case OFOPERATION_ADD:
+ return op->victim && ofproto_rule_has_out_port(op->victim, out_port);
+
+ case OFOPERATION_DELETE:
+ return false;
+
+ case OFOPERATION_MODIFY:
+ return ofpacts_output_to_port(op->ofpacts, op->ofpacts_len, out_port);
}
- return false;
+
+ NOT_REACHED();
}
/* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
* Rules with priority higher than UINT16_MAX are set up by ofproto itself
* (e.g. by in-band control) and are intentionally hidden from the
* controller. */
-static bool
-rule_is_hidden(const struct rule *rule)
+bool
+ofproto_rule_is_hidden(const struct rule *rule)
{
return rule->cr.priority > UINT16_MAX;
}
struct ofpbuf *buf;
/* Send reply. */
- osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
+ buf = ofpraw_alloc_reply(OFPRAW_OFPT_GET_CONFIG_REPLY, oh, 0);
+ osc = ofpbuf_put_uninit(buf, sizeof *osc);
flags = ofproto->frag_handling;
if (ofconn_get_invalid_ttl_to_controller(ofconn)) {
flags |= OFPC_INVALID_TTL_TO_CONTROLLER;
}
static enum ofperr
-handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
+handle_set_config(struct ofconn *ofconn, const struct ofp_header *oh)
{
+ const struct ofp_switch_config *osc = ofpmsg_body(oh);
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
uint16_t flags = ntohs(osc->flags);
}
static enum ofperr
-handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
+handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
struct ofputil_packet_out po;
struct ofpbuf *payload;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
struct flow flow;
enum ofperr error;
error = reject_slave_controller(ofconn);
if (error) {
- return error;
+ goto exit;
}
/* Decode message. */
- error = ofputil_decode_packet_out(&po, opo);
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ error = ofputil_decode_packet_out(&po, oh, &ofpacts);
if (error) {
- return error;
+ goto exit_free_ofpacts;
}
/* Get payload. */
if (po.buffer_id != UINT32_MAX) {
error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL);
if (error || !payload) {
- return error;
+ goto exit_free_ofpacts;
}
} else {
payload = xmalloc(sizeof *payload);
/* Send out packet. */
flow_extract(payload, 0, 0, po.in_port, &flow);
error = p->ofproto_class->packet_out(p, payload, &flow,
- po.actions, po.n_actions);
+ po.ofpacts, po.ofpacts_len);
ofpbuf_delete(payload);
+exit_free_ofpacts:
+ ofpbuf_uninit(&ofpacts);
+exit:
return error;
}
static enum ofperr
handle_desc_stats_request(struct ofconn *ofconn,
- const struct ofp_stats_msg *request)
+ const struct ofp_header *request)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
struct ofp_desc_stats *ods;
struct ofpbuf *msg;
- ods = ofputil_make_stats_reply(sizeof *ods, request, &msg);
+ msg = ofpraw_alloc_stats_reply(request, 0);
+ ods = ofpbuf_put_zeros(msg, sizeof *ods);
ovs_strlcpy(ods->mfr_desc, p->mfr_desc, sizeof ods->mfr_desc);
ovs_strlcpy(ods->hw_desc, p->hw_desc, sizeof ods->hw_desc);
ovs_strlcpy(ods->sw_desc, p->sw_desc, sizeof ods->sw_desc);
static enum ofperr
handle_table_stats_request(struct ofconn *ofconn,
- const struct ofp_stats_msg *request)
+ const struct ofp_header *request)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
- struct ofp_table_stats *ots;
+ struct ofp10_table_stats *ots;
struct ofpbuf *msg;
size_t i;
- ofputil_make_stats_reply(sizeof(struct ofp_stats_msg), request, &msg);
-
+ msg = ofpraw_alloc_stats_reply(request, sizeof *ots * p->n_tables);
ots = ofpbuf_put_zeros(msg, sizeof *ots * p->n_tables);
for (i = 0; i < p->n_tables; i++) {
ots[i].table_id = i;
sprintf(ots[i].name, "table%zu", i);
- ots[i].wildcards = htonl(OFPFW_ALL);
+ ots[i].wildcards = htonl(OFPFW10_ALL);
ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
ots[i].active_count = htonl(classifier_count(&p->tables[i].cls));
}
append_port_stat(struct ofport *port, struct list *replies)
{
struct netdev_stats stats;
- struct ofp_port_stats *ops;
+ struct ofp10_port_stats *ops;
/* Intentionally ignore return value, since errors will set
* 'stats' to all-1s, which is correct for OpenFlow, and
* netdev_get_stats() will log errors. */
ofproto_port_get_stats(port, &stats);
- ops = ofputil_append_stats_reply(sizeof *ops, replies);
+ ops = ofpmp_append(replies, sizeof *ops);
ops->port_no = htons(port->pp.port_no);
memset(ops->pad, 0, sizeof ops->pad);
put_32aligned_be64(&ops->rx_packets, htonll(stats.rx_packets));
static enum ofperr
handle_port_stats_request(struct ofconn *ofconn,
- const struct ofp_port_stats_request *psr)
+ const struct ofp_header *request)
{
struct ofproto *p = ofconn_get_ofproto(ofconn);
+ const struct ofp10_port_stats_request *psr = ofpmsg_body(request);
struct ofport *port;
struct list replies;
- ofputil_start_stats_reply(&psr->osm, &replies);
+ ofpmp_init(&replies, request);
if (psr->port_no != htons(OFPP_NONE)) {
port = ofproto_get_port(p, ntohs(psr->port_no));
if (port) {
return 0;
}
+static enum ofperr
+handle_port_desc_stats_request(struct ofconn *ofconn,
+ const struct ofp_header *request)
+{
+ struct ofproto *p = ofconn_get_ofproto(ofconn);
+ enum ofp_version version;
+ struct ofport *port;
+ struct list replies;
+
+ ofpmp_init(&replies, request);
+
+ version = ofputil_protocol_to_ofp_version(ofconn_get_protocol(ofconn));
+ HMAP_FOR_EACH (port, hmap_node, &p->ports) {
+ ofputil_append_port_desc_stats_reply(version, &port->pp, &replies);
+ }
+
+ ofconn_send_replies(ofconn, &replies);
+ return 0;
+}
+
static void
calc_flow_duration__(long long int start, long long int now,
uint32_t *sec, uint32_t *nsec)
}
static struct oftable *
-next_visible_table(struct ofproto *ofproto, uint8_t table_id)
+next_visible_table(const struct ofproto *ofproto, uint8_t table_id)
{
struct oftable *table;
}
static struct oftable *
-first_matching_table(struct ofproto *ofproto, uint8_t table_id)
+first_matching_table(const struct ofproto *ofproto, uint8_t table_id)
{
if (table_id == 0xff) {
return next_visible_table(ofproto, 0);
}
static struct oftable *
-next_matching_table(struct ofproto *ofproto,
- struct oftable *table, uint8_t table_id)
+next_matching_table(const struct ofproto *ofproto,
+ const struct oftable *table, uint8_t table_id)
{
return (table_id == 0xff
? next_visible_table(ofproto, (table - ofproto->tables) + 1)
if (rule->pending) {
return OFPROTO_POSTPONE;
}
- if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)
+ if (!ofproto_rule_is_hidden(rule)
+ && ofproto_rule_has_out_port(rule, out_port)
&& !((rule->flow_cookie ^ cookie) & cookie_mask)) {
list_push_back(rules, &rule->ofproto_node);
}
if (rule->pending) {
return OFPROTO_POSTPONE;
}
- if (!rule_is_hidden(rule) && rule_has_out_port(rule, out_port)
+ if (!ofproto_rule_is_hidden(rule)
+ && ofproto_rule_has_out_port(rule, out_port)
&& !((rule->flow_cookie ^ cookie) & cookie_mask)) {
list_push_back(rules, &rule->ofproto_node);
}
static enum ofperr
handle_flow_stats_request(struct ofconn *ofconn,
- const struct ofp_stats_msg *osm)
+ const struct ofp_header *request)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_flow_stats_request fsr;
struct rule *rule;
enum ofperr error;
- error = ofputil_decode_flow_stats_request(&fsr, &osm->header);
+ error = ofputil_decode_flow_stats_request(&fsr, request);
if (error) {
return error;
}
return error;
}
- ofputil_start_stats_reply(osm, &replies);
+ ofpmp_init(&replies, request);
LIST_FOR_EACH (rule, ofproto_node, &rules) {
long long int now = time_msec();
struct ofputil_flow_stats fs;
fs.hard_age = age_secs(now - rule->modified);
ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count,
&fs.byte_count);
- fs.actions = rule->actions;
- fs.n_actions = rule->n_actions;
+ fs.ofpacts = rule->ofpacts;
+ fs.ofpacts_len = rule->ofpacts_len;
ofputil_append_flow_stats_reply(&fs, &replies);
}
ofconn_send_replies(ofconn, &replies);
ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
cls_rule_format(&rule->cr, results);
ds_put_char(results, ',');
- if (rule->n_actions > 0) {
- ofp_print_actions(results, rule->actions, rule->n_actions);
+ if (rule->ofpacts_len > 0) {
+ ofpacts_format(rule->ofpacts, rule->ofpacts_len, results);
} else {
ds_put_cstr(results, "drop");
}
/* Checks the fault status of CFM for 'ofp_port' within 'ofproto'. Returns a
* bitmask of 'cfm_fault_reason's to indicate a CFM fault (generally
* indicating a connectivity problem). Returns zero if CFM is not faulted,
- * and -1 if CFM is not enabled on 'port'. */
+ * and -1 if CFM is not enabled on 'ofp_port'. */
int
ofproto_port_get_cfm_fault(const struct ofproto *ofproto, uint16_t ofp_port)
{
: -1);
}
+/* Checks the operational status reported by the remote CFM endpoint of
+ * 'ofp_port' Returns 1 if operationally up, 0 if operationally down, and -1
+ * if CFM is not enabled on 'ofp_port' or does not support operational status.
+ */
+int
+ofproto_port_get_cfm_opup(const struct ofproto *ofproto, uint16_t ofp_port)
+{
+ struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+ return (ofport && ofproto->ofproto_class->get_cfm_opup
+ ? ofproto->ofproto_class->get_cfm_opup(ofport)
+ : -1);
+}
+
/* Gets the MPIDs of the remote maintenance points broadcasting to 'ofp_port'
* within 'ofproto'. Populates 'rmps' with an array of MPIDs owned by
* 'ofproto', and 'n_rmps' with the number of MPIDs in 'rmps'. Returns a
static enum ofperr
handle_aggregate_stats_request(struct ofconn *ofconn,
- const struct ofp_stats_msg *osm)
+ const struct ofp_header *oh)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_flow_stats_request request;
struct rule *rule;
enum ofperr error;
- error = ofputil_decode_flow_stats_request(&request, &osm->header);
+ error = ofputil_decode_flow_stats_request(&request, oh);
if (error) {
return error;
}
stats.byte_count = UINT64_MAX;
}
- reply = ofputil_encode_aggregate_stats_reply(&stats, osm);
+ reply = ofputil_encode_aggregate_stats_reply(&stats, oh);
ofconn_send_reply(ofconn, reply);
return 0;
put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id,
const struct netdev_queue_stats *stats)
{
- struct ofp_queue_stats *reply;
+ struct ofp10_queue_stats *reply;
- reply = ofputil_append_stats_reply(sizeof *reply, &cbdata->replies);
+ reply = ofpmp_append(&cbdata->replies, sizeof *reply);
reply->port_no = htons(cbdata->ofport->pp.port_no);
memset(reply->pad, 0, sizeof reply->pad);
reply->queue_id = htonl(queue_id);
put_queue_stats(cbdata, queue_id, stats);
}
-static void
+static enum ofperr
handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id,
struct queue_stats_cbdata *cbdata)
{
if (!netdev_get_queue_stats(port->netdev, queue_id, &stats)) {
put_queue_stats(cbdata, queue_id, &stats);
+ } else {
+ return OFPERR_OFPQOFC_BAD_QUEUE;
}
}
+ return 0;
}
static enum ofperr
handle_queue_stats_request(struct ofconn *ofconn,
- const struct ofp_queue_stats_request *qsr)
+ const struct ofp_header *rq)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ const struct ofp10_queue_stats_request *qsr = ofpmsg_body(rq);
struct queue_stats_cbdata cbdata;
- struct ofport *port;
unsigned int port_no;
+ struct ofport *port;
uint32_t queue_id;
+ enum ofperr error;
COVERAGE_INC(ofproto_queue_req);
- ofputil_start_stats_reply(&qsr->osm, &cbdata.replies);
+ ofpmp_init(&cbdata.replies, rq);
port_no = ntohs(qsr->port_no);
queue_id = ntohl(qsr->queue_id);
if (port_no == OFPP_ALL) {
+ error = OFPERR_OFPQOFC_BAD_QUEUE;
HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
- handle_queue_stats_for_port(port, queue_id, &cbdata);
+ if (!handle_queue_stats_for_port(port, queue_id, &cbdata)) {
+ error = 0;
+ }
}
- } else if (port_no < OFPP_MAX) {
+ } else {
port = ofproto_get_port(ofproto, port_no);
- if (port) {
- handle_queue_stats_for_port(port, queue_id, &cbdata);
- }
+ error = (port
+ ? handle_queue_stats_for_port(port, queue_id, &cbdata)
+ : OFPERR_OFPQOFC_BAD_PORT);
+ }
+ if (!error) {
+ ofconn_send_replies(ofconn, &cbdata.replies);
} else {
ofpbuf_list_delete(&cbdata.replies);
- return OFPERR_OFPQOFC_BAD_PORT;
}
- ofconn_send_replies(ofconn, &cbdata.replies);
- return 0;
+ return error;
}
static bool
* error code on failure, or OFPROTO_POSTPONE if the operation cannot be
* initiated now but may be retried later.
*
+ * Upon successful return, takes ownership of 'fm->ofpacts'. On failure,
+ * ownership remains with the caller.
+ *
* 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
* if any. */
static enum ofperr
rule->ofproto = ofproto;
rule->cr = fm->cr;
rule->pending = NULL;
- rule->flow_cookie = fm->cookie;
+ rule->flow_cookie = fm->new_cookie;
rule->created = rule->modified = rule->used = time_msec();
rule->idle_timeout = fm->idle_timeout;
rule->hard_timeout = fm->hard_timeout;
rule->table_id = table - ofproto->tables;
rule->send_flow_removed = (fm->flags & OFPFF_SEND_FLOW_REM) != 0;
- rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
- rule->n_actions = fm->n_actions;
+ rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
+ rule->ofpacts_len = fm->ofpacts_len;
rule->evictable = true;
rule->eviction_group = NULL;
+ rule->monitor_flags = 0;
+ rule->add_seqno = 0;
+ rule->modify_seqno = 0;
/* Insert new rule. */
victim = oftable_replace_rule(rule);
} else if (victim && victim->pending) {
error = OFPROTO_POSTPONE;
} else {
+ struct ofoperation *op;
struct rule *evict;
if (classifier_count(&table->cls) > table->max_flows) {
}
group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
- ofoperation_create(group, rule, OFOPERATION_ADD);
- rule->pending->victim = victim;
+ op = ofoperation_create(group, rule, OFOPERATION_ADD, 0);
+ op->victim = victim;
error = ofproto->ofproto_class->rule_construct(rule);
if (error) {
+ op->group->n_running--;
ofoperation_destroy(rule->pending);
} else if (evict) {
delete_flow__(evict, group);
group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
error = OFPERR_OFPBRC_EPERM;
LIST_FOR_EACH (rule, ofproto_node, rules) {
+ struct ofoperation *op;
+ bool actions_changed;
+ ovs_be64 new_cookie;
+
if (rule_is_modifiable(rule)) {
/* At least one rule is modifiable, don't report EPERM error. */
error = 0;
continue;
}
- if (!ofputil_actions_equal(fm->actions, fm->n_actions,
- rule->actions, rule->n_actions)) {
- ofoperation_create(group, rule, OFOPERATION_MODIFY);
- rule->pending->actions = rule->actions;
- rule->pending->n_actions = rule->n_actions;
- rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
- rule->n_actions = fm->n_actions;
- ofproto->ofproto_class->rule_modify_actions(rule);
+ actions_changed = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
+ rule->ofpacts, rule->ofpacts_len);
+ new_cookie = (fm->new_cookie != htonll(UINT64_MAX)
+ ? fm->new_cookie
+ : rule->flow_cookie);
+ if (!actions_changed && new_cookie == rule->flow_cookie) {
+ /* No change at all. */
+ continue;
+ }
+
+ op = ofoperation_create(group, rule, OFOPERATION_MODIFY, 0);
+ rule->flow_cookie = new_cookie;
+ if (actions_changed) {
+ op->ofpacts = rule->ofpacts;
+ op->ofpacts_len = rule->ofpacts_len;
+ rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
+ rule->ofpacts_len = fm->ofpacts_len;
+ rule->ofproto->ofproto_class->rule_modify_actions(rule);
} else {
- rule->modified = time_msec();
+ ofoperation_complete(op, 0);
}
- rule->flow_cookie = fm->cookie;
}
ofopgroup_submit(group);
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
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. */
ofproto_rule_send_removed(rule, OFPRR_DELETE);
- ofoperation_create(group, rule, OFOPERATION_DELETE);
+ ofoperation_create(group, rule, OFOPERATION_DELETE, OFPRR_DELETE);
oftable_remove_rule(rule);
ofproto->ofproto_class->rule_destruct(rule);
}
{
struct ofputil_flow_removed fr;
- if (rule_is_hidden(rule) || !rule->send_flow_removed) {
+ if (ofproto_rule_is_hidden(rule) || !rule->send_flow_removed) {
return;
}
calc_flow_duration__(rule->created, time_msec(),
&fr.duration_sec, &fr.duration_nsec);
fr.idle_timeout = rule->idle_timeout;
+ fr.hard_timeout = rule->hard_timeout;
rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count,
&fr.byte_count);
* OFPRR_HARD_TIMEOUT or OFPRR_IDLE_TIMEOUT), and then removes 'rule' from its
* ofproto.
*
+ * 'rule' must not have a pending operation (that is, 'rule->pending' must be
+ * NULL).
+ *
* ofproto implementation ->run() functions should use this function to expire
* OpenFlow flows. */
void
ofproto_rule_send_removed(rule, reason);
group = ofopgroup_create_unattached(ofproto);
- ofoperation_create(group, rule, OFOPERATION_DELETE);
+ ofoperation_create(group, rule, OFOPERATION_DELETE, reason);
oftable_remove_rule(rule);
ofproto->ofproto_class->rule_destruct(rule);
ofopgroup_submit(group);
static enum ofperr
handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofputil_flow_mod fm;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
enum ofperr error;
+ long long int now;
error = reject_slave_controller(ofconn);
if (error) {
- return error;
+ goto exit;
}
- error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn));
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
+ &ofpacts);
if (error) {
- return error;
+ goto exit_free_ofpacts;
}
- /* We do not support the emergency flow cache. It will hopefully get
- * dropped from OpenFlow in the near future. */
- if (fm.flags & OFPFF_EMERG) {
- /* There isn't a good fit for an error code, so just state that the
- * flow table is full. */
- return OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ if (fm.flags & OFPFF10_EMERG) {
+ /* We do not support the OpenFlow 1.0 emergency flow cache, which is not
+ * required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. */
+ /* We do not support the emergency flow cache. It will hopefully get
+ * dropped from OpenFlow in the near future. There is no good error
+ * code, so just state that the flow table is full. */
+ error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
+ } else {
+ error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
}
+ if (error) {
+ goto exit_free_ofpacts;
+ }
+
+ /* Record the operation for logging a summary report. */
+ switch (fm.command) {
+ case OFPFC_ADD:
+ ofproto->n_add++;
+ break;
- return handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
+ case OFPFC_MODIFY:
+ case OFPFC_MODIFY_STRICT:
+ ofproto->n_modify++;
+ break;
+
+ case OFPFC_DELETE:
+ case OFPFC_DELETE_STRICT:
+ ofproto->n_delete++;
+ break;
+ }
+
+ now = time_msec();
+ if (ofproto->next_op_report == LLONG_MAX) {
+ ofproto->first_op = now;
+ ofproto->next_op_report = MAX(now + 10 * 1000,
+ ofproto->op_backoff);
+ ofproto->op_backoff = ofproto->next_op_report + 60 * 1000;
+ }
+ ofproto->last_op = now;
+
+exit_free_ofpacts:
+ ofpbuf_uninit(&ofpacts);
+exit:
+ return error;
}
static enum ofperr
default:
if (fm->command > 0xff) {
- VLOG_WARN_RL(&rl, "flow_mod has explicit table_id but "
- "flow_mod_table_id extension is not enabled");
+ VLOG_WARN_RL(&rl, "%s: flow_mod has explicit table_id but "
+ "flow_mod_table_id extension is not enabled",
+ ofproto->name);
}
return OFPERR_OFPFMFC_BAD_COMMAND;
}
static enum ofperr
handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
- struct nx_role_request *nrr = (struct nx_role_request *) oh;
+ const struct nx_role_request *nrr = ofpmsg_body(oh);
struct nx_role_request *reply;
struct ofpbuf *buf;
uint32_t role;
ofconn_set_role(ofconn, role);
- reply = make_nxmsg_xid(sizeof *reply, NXT_ROLE_REPLY, oh->xid, &buf);
+ buf = ofpraw_alloc_reply(OFPRAW_NXT_ROLE_REPLY, oh, 0);
+ reply = ofpbuf_put_zeros(buf, sizeof *reply);
reply->role = htonl(role);
ofconn_send_reply(ofconn, buf);
handle_nxt_flow_mod_table_id(struct ofconn *ofconn,
const struct ofp_header *oh)
{
- const struct nx_flow_mod_table_id *msg
- = (const struct nx_flow_mod_table_id *) oh;
+ const struct nx_flow_mod_table_id *msg = ofpmsg_body(oh);
enum ofputil_protocol cur, next;
cur = ofconn_get_protocol(ofconn);
static enum ofperr
handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh)
{
- const struct nx_set_flow_format *msg
- = (const struct nx_set_flow_format *) oh;
+ const struct nx_set_flow_format *msg = ofpmsg_body(oh);
enum ofputil_protocol cur, next;
enum ofputil_protocol next_base;
handle_nxt_set_packet_in_format(struct ofconn *ofconn,
const struct ofp_header *oh)
{
- const struct nx_set_packet_in_format *msg;
+ const struct nx_set_packet_in_format *msg = ofpmsg_body(oh);
uint32_t format;
- msg = (const struct nx_set_packet_in_format *) oh;
format = ntohl(msg->format);
if (format != NXPIF_OPENFLOW10 && format != NXPIF_NXM) {
return OFPERR_OFPBRC_EPERM;
static enum ofperr
handle_nxt_set_async_config(struct ofconn *ofconn, const struct ofp_header *oh)
{
- const struct nx_async_config *msg = (const struct nx_async_config *) oh;
+ const struct nx_async_config *msg = ofpmsg_body(oh);
uint32_t master[OAM_N_TYPES];
uint32_t slave[OAM_N_TYPES];
slave[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[1]);
ofconn_set_async_config(ofconn, master, slave);
+ if (ofconn_get_type(ofconn) == OFCONN_SERVICE &&
+ !ofconn_get_miss_send_len(ofconn)) {
+ ofconn_set_miss_send_len(ofconn, OFP_DEFAULT_MISS_SEND_LEN);
+ }
return 0;
}
handle_nxt_set_controller_id(struct ofconn *ofconn,
const struct ofp_header *oh)
{
- const struct nx_controller_id *nci;
+ const struct nx_controller_id *nci = ofpmsg_body(oh);
- nci = (const struct nx_controller_id *) oh;
if (!is_all_zeros(nci->zero, sizeof nci->zero)) {
return OFPERR_NXBRC_MUST_BE_ZERO;
}
static enum ofperr
handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
- struct ofp_header *ob;
struct ofpbuf *buf;
if (ofconn_has_pending_opgroups(ofconn)) {
return OFPROTO_POSTPONE;
}
- ob = make_openflow_xid(sizeof *ob, OFPT10_BARRIER_REPLY, oh->xid, &buf);
+ buf = ofpraw_alloc_reply((oh->version == OFP10_VERSION
+ ? OFPRAW_OFPT10_BARRIER_REPLY
+ : OFPRAW_OFPT11_BARRIER_REPLY), oh, 0);
ofconn_send_reply(ofconn, buf);
return 0;
}
+static void
+ofproto_compose_flow_refresh_update(const struct rule *rule,
+ enum nx_flow_monitor_flags flags,
+ struct list *msgs)
+{
+ struct ofoperation *op = rule->pending;
+ struct ofputil_flow_update fu;
+
+ if (op && op->type == OFOPERATION_ADD && !op->victim) {
+ /* We'll report the final flow when the operation completes. Reporting
+ * it now would cause a duplicate report later. */
+ return;
+ }
+
+ fu.event = (flags & (NXFMF_INITIAL | NXFMF_ADD)
+ ? NXFME_ADDED : NXFME_MODIFIED);
+ fu.reason = 0;
+ fu.idle_timeout = rule->idle_timeout;
+ fu.hard_timeout = rule->hard_timeout;
+ fu.table_id = rule->table_id;
+ fu.cookie = rule->flow_cookie;
+ fu.match = CONST_CAST(struct cls_rule *, &rule->cr);
+ if (!(flags & NXFMF_ACTIONS)) {
+ fu.ofpacts = NULL;
+ fu.ofpacts_len = 0;
+ } else if (!op) {
+ fu.ofpacts = rule->ofpacts;
+ fu.ofpacts_len = rule->ofpacts_len;
+ } else {
+ /* An operation is in progress. Use the previous version of the flow's
+ * actions, so that when the operation commits we report the change. */
+ switch (op->type) {
+ case OFOPERATION_ADD:
+ /* We already verified that there was a victim. */
+ fu.ofpacts = op->victim->ofpacts;
+ fu.ofpacts_len = op->victim->ofpacts_len;
+ break;
+
+ case OFOPERATION_MODIFY:
+ if (op->ofpacts) {
+ fu.ofpacts = op->ofpacts;
+ fu.ofpacts_len = op->ofpacts_len;
+ } else {
+ fu.ofpacts = rule->ofpacts;
+ fu.ofpacts_len = rule->ofpacts_len;
+ }
+ break;
+
+ case OFOPERATION_DELETE:
+ fu.ofpacts = rule->ofpacts;
+ fu.ofpacts_len = rule->ofpacts_len;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+ }
+
+ if (list_is_empty(msgs)) {
+ ofputil_start_flow_update(msgs);
+ }
+ ofputil_append_flow_update(&fu, msgs);
+}
+
+void
+ofmonitor_compose_refresh_updates(struct list *rules, struct list *msgs)
+{
+ struct rule *rule;
+
+ LIST_FOR_EACH (rule, ofproto_node, rules) {
+ enum nx_flow_monitor_flags flags = rule->monitor_flags;
+ rule->monitor_flags = 0;
+
+ ofproto_compose_flow_refresh_update(rule, flags, msgs);
+ }
+}
+
+static void
+ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m,
+ struct rule *rule, uint64_t seqno,
+ struct list *rules)
+{
+ enum nx_flow_monitor_flags update;
+
+ if (ofproto_rule_is_hidden(rule)) {
+ return;
+ }
+
+ if (!(rule->pending
+ ? ofoperation_has_out_port(rule->pending, m->out_port)
+ : ofproto_rule_has_out_port(rule, m->out_port))) {
+ return;
+ }
+
+ if (seqno) {
+ if (rule->add_seqno > seqno) {
+ update = NXFMF_ADD | NXFMF_MODIFY;
+ } else if (rule->modify_seqno > seqno) {
+ update = NXFMF_MODIFY;
+ } else {
+ return;
+ }
+
+ if (!(m->flags & update)) {
+ return;
+ }
+ } else {
+ update = NXFMF_INITIAL;
+ }
+
+ if (!rule->monitor_flags) {
+ list_push_back(rules, &rule->ofproto_node);
+ }
+ rule->monitor_flags |= update | (m->flags & NXFMF_ACTIONS);
+}
+
+static void
+ofproto_collect_ofmonitor_refresh_rules(const struct ofmonitor *m,
+ uint64_t seqno,
+ struct list *rules)
+{
+ const struct ofproto *ofproto = ofconn_get_ofproto(m->ofconn);
+ const struct ofoperation *op;
+ const struct oftable *table;
+
+ FOR_EACH_MATCHING_TABLE (table, m->table_id, ofproto) {
+ struct cls_cursor cursor;
+ struct rule *rule;
+
+ cls_cursor_init(&cursor, &table->cls, &m->match);
+ CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
+ assert(!rule->pending); /* XXX */
+ ofproto_collect_ofmonitor_refresh_rule(m, rule, seqno, rules);
+ }
+ }
+
+ HMAP_FOR_EACH (op, hmap_node, &ofproto->deletions) {
+ struct rule *rule = op->rule;
+
+ if (((m->table_id == 0xff
+ ? !(ofproto->tables[rule->table_id].flags & OFTABLE_HIDDEN)
+ : m->table_id == rule->table_id))
+ && cls_rule_is_loose_match(&rule->cr, &m->match)) {
+ ofproto_collect_ofmonitor_refresh_rule(m, rule, seqno, rules);
+ }
+ }
+}
+
+static void
+ofproto_collect_ofmonitor_initial_rules(struct ofmonitor *m,
+ struct list *rules)
+{
+ if (m->flags & NXFMF_INITIAL) {
+ ofproto_collect_ofmonitor_refresh_rules(m, 0, rules);
+ }
+}
+
+void
+ofmonitor_collect_resume_rules(struct ofmonitor *m,
+ uint64_t seqno, struct list *rules)
+{
+ ofproto_collect_ofmonitor_refresh_rules(m, seqno, rules);
+}
+
+static enum ofperr
+handle_flow_monitor_request(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+ struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
+ struct ofmonitor **monitors;
+ size_t n_monitors, allocated_monitors;
+ struct list replies;
+ enum ofperr error;
+ struct list rules;
+ struct ofpbuf b;
+ size_t i;
+
+ error = 0;
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ monitors = NULL;
+ n_monitors = allocated_monitors = 0;
+ for (;;) {
+ struct ofputil_flow_monitor_request request;
+ struct ofmonitor *m;
+ int retval;
+
+ retval = ofputil_decode_flow_monitor_request(&request, &b);
+ if (retval == EOF) {
+ break;
+ } else if (retval) {
+ error = retval;
+ goto error;
+ }
+
+ if (request.table_id != 0xff
+ && request.table_id >= ofproto->n_tables) {
+ error = OFPERR_OFPBRC_BAD_TABLE_ID;
+ goto error;
+ }
+
+ error = ofmonitor_create(&request, ofconn, &m);
+ if (error) {
+ goto error;
+ }
+
+ if (n_monitors >= allocated_monitors) {
+ monitors = x2nrealloc(monitors, &allocated_monitors,
+ sizeof *monitors);
+ }
+ monitors[n_monitors++] = m;
+ }
+
+ list_init(&rules);
+ for (i = 0; i < n_monitors; i++) {
+ ofproto_collect_ofmonitor_initial_rules(monitors[i], &rules);
+ }
+
+ ofpmp_init(&replies, oh);
+ ofmonitor_compose_refresh_updates(&rules, &replies);
+ ofconn_send_replies(ofconn, &replies);
+
+ free(monitors);
+
+ return 0;
+
+error:
+ for (i = 0; i < n_monitors; i++) {
+ ofmonitor_destroy(monitors[i]);
+ }
+ free(monitors);
+ return error;
+}
+
+static enum ofperr
+handle_flow_monitor_cancel(struct ofconn *ofconn, const struct ofp_header *oh)
+{
+ struct ofmonitor *m;
+ uint32_t id;
+
+ id = ofputil_decode_flow_monitor_cancel(oh);
+ m = ofmonitor_lookup(ofconn, id);
+ if (!m) {
+ return OFPERR_NXBRC_FM_BAD_ID;
+ }
+
+ ofmonitor_destroy(m);
+ return 0;
+}
+
static enum ofperr
handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
{
const struct ofp_header *oh = msg->data;
- const struct ofputil_msg_type *type;
+ enum ofptype type;
enum ofperr error;
- error = ofputil_decode_msg_type(oh, &type);
+ error = ofptype_decode(&type, oh);
if (error) {
return error;
}
- switch (ofputil_msg_type_code(type)) {
+ switch (type) {
/* OpenFlow requests. */
- case OFPUTIL_OFPT_ECHO_REQUEST:
+ case OFPTYPE_ECHO_REQUEST:
return handle_echo_request(ofconn, oh);
- case OFPUTIL_OFPT_FEATURES_REQUEST:
+ case OFPTYPE_FEATURES_REQUEST:
return handle_features_request(ofconn, oh);
- case OFPUTIL_OFPT_GET_CONFIG_REQUEST:
+ case OFPTYPE_GET_CONFIG_REQUEST:
return handle_get_config_request(ofconn, oh);
- case OFPUTIL_OFPT_SET_CONFIG:
- return handle_set_config(ofconn, msg->data);
+ case OFPTYPE_SET_CONFIG:
+ return handle_set_config(ofconn, oh);
- case OFPUTIL_OFPT_PACKET_OUT:
- return handle_packet_out(ofconn, msg->data);
+ case OFPTYPE_PACKET_OUT:
+ return handle_packet_out(ofconn, oh);
- case OFPUTIL_OFPT_PORT_MOD:
+ case OFPTYPE_PORT_MOD:
return handle_port_mod(ofconn, oh);
- case OFPUTIL_OFPT_FLOW_MOD:
+ case OFPTYPE_FLOW_MOD:
return handle_flow_mod(ofconn, oh);
- case OFPUTIL_OFPT_BARRIER_REQUEST:
+ case OFPTYPE_BARRIER_REQUEST:
return handle_barrier_request(ofconn, oh);
/* OpenFlow replies. */
- case OFPUTIL_OFPT_ECHO_REPLY:
+ case OFPTYPE_ECHO_REPLY:
return 0;
/* Nicira extension requests. */
- case OFPUTIL_NXT_ROLE_REQUEST:
+ case OFPTYPE_ROLE_REQUEST:
return handle_role_request(ofconn, oh);
- case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
+ case OFPTYPE_FLOW_MOD_TABLE_ID:
return handle_nxt_flow_mod_table_id(ofconn, oh);
- case OFPUTIL_NXT_SET_FLOW_FORMAT:
+ case OFPTYPE_SET_FLOW_FORMAT:
return handle_nxt_set_flow_format(ofconn, oh);
- case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
+ case OFPTYPE_SET_PACKET_IN_FORMAT:
return handle_nxt_set_packet_in_format(ofconn, oh);
- case OFPUTIL_NXT_SET_CONTROLLER_ID:
+ case OFPTYPE_SET_CONTROLLER_ID:
return handle_nxt_set_controller_id(ofconn, oh);
- case OFPUTIL_NXT_FLOW_MOD:
- return handle_flow_mod(ofconn, oh);
-
- case OFPUTIL_NXT_FLOW_AGE:
+ case OFPTYPE_FLOW_AGE:
/* Nothing to do. */
return 0;
- case OFPUTIL_NXT_SET_ASYNC_CONFIG:
+ case OFPTYPE_FLOW_MONITOR_CANCEL:
+ return handle_flow_monitor_cancel(ofconn, oh);
+
+ case OFPTYPE_SET_ASYNC_CONFIG:
return handle_nxt_set_async_config(ofconn, oh);
/* Statistics requests. */
- case OFPUTIL_OFPST_DESC_REQUEST:
- return handle_desc_stats_request(ofconn, msg->data);
-
- case OFPUTIL_OFPST_FLOW_REQUEST:
- case OFPUTIL_NXST_FLOW_REQUEST:
- return handle_flow_stats_request(ofconn, msg->data);
-
- case OFPUTIL_OFPST_AGGREGATE_REQUEST:
- case OFPUTIL_NXST_AGGREGATE_REQUEST:
- return handle_aggregate_stats_request(ofconn, msg->data);
-
- case OFPUTIL_OFPST_TABLE_REQUEST:
- return handle_table_stats_request(ofconn, msg->data);
-
- case OFPUTIL_OFPST_PORT_REQUEST:
- return handle_port_stats_request(ofconn, msg->data);
-
- case OFPUTIL_OFPST_QUEUE_REQUEST:
- return handle_queue_stats_request(ofconn, msg->data);
-
- case OFPUTIL_MSG_INVALID:
- case OFPUTIL_OFPT_HELLO:
- case OFPUTIL_OFPT_ERROR:
- case OFPUTIL_OFPT_FEATURES_REPLY:
- case OFPUTIL_OFPT_GET_CONFIG_REPLY:
- case OFPUTIL_OFPT_PACKET_IN:
- case OFPUTIL_OFPT_FLOW_REMOVED:
- case OFPUTIL_OFPT_PORT_STATUS:
- case OFPUTIL_OFPT_BARRIER_REPLY:
- case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST:
- case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY:
- case OFPUTIL_OFPST_DESC_REPLY:
- case OFPUTIL_OFPST_FLOW_REPLY:
- case OFPUTIL_OFPST_QUEUE_REPLY:
- case OFPUTIL_OFPST_PORT_REPLY:
- case OFPUTIL_OFPST_TABLE_REPLY:
- case OFPUTIL_OFPST_AGGREGATE_REPLY:
- case OFPUTIL_NXT_ROLE_REPLY:
- case OFPUTIL_NXT_FLOW_REMOVED:
- case OFPUTIL_NXT_PACKET_IN:
- case OFPUTIL_NXST_FLOW_REPLY:
- case OFPUTIL_NXST_AGGREGATE_REPLY:
+ case OFPTYPE_DESC_STATS_REQUEST:
+ return handle_desc_stats_request(ofconn, oh);
+
+ case OFPTYPE_FLOW_STATS_REQUEST:
+ return handle_flow_stats_request(ofconn, oh);
+
+ case OFPTYPE_AGGREGATE_STATS_REQUEST:
+ return handle_aggregate_stats_request(ofconn, oh);
+
+ case OFPTYPE_TABLE_STATS_REQUEST:
+ return handle_table_stats_request(ofconn, oh);
+
+ case OFPTYPE_PORT_STATS_REQUEST:
+ return handle_port_stats_request(ofconn, oh);
+
+ case OFPTYPE_QUEUE_STATS_REQUEST:
+ return handle_queue_stats_request(ofconn, oh);
+
+ case OFPTYPE_PORT_DESC_STATS_REQUEST:
+ return handle_port_desc_stats_request(ofconn, oh);
+
+ case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+ return handle_flow_monitor_request(ofconn, oh);
+
+ case OFPTYPE_HELLO:
+ case OFPTYPE_ERROR:
+ case OFPTYPE_FEATURES_REPLY:
+ case OFPTYPE_GET_CONFIG_REPLY:
+ case OFPTYPE_PACKET_IN:
+ case OFPTYPE_FLOW_REMOVED:
+ case OFPTYPE_PORT_STATUS:
+ case OFPTYPE_BARRIER_REPLY:
+ case OFPTYPE_DESC_STATS_REPLY:
+ case OFPTYPE_FLOW_STATS_REPLY:
+ case OFPTYPE_QUEUE_STATS_REPLY:
+ case OFPTYPE_PORT_STATS_REPLY:
+ case OFPTYPE_TABLE_STATS_REPLY:
+ case OFPTYPE_AGGREGATE_STATS_REPLY:
+ case OFPTYPE_PORT_DESC_STATS_REPLY:
+ case OFPTYPE_ROLE_REPLY:
+ case OFPTYPE_FLOW_MONITOR_PAUSED:
+ case OFPTYPE_FLOW_MONITOR_RESUMED:
+ case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
default:
- return (oh->type == OFPT10_STATS_REQUEST ||
- oh->type == OFPT10_STATS_REPLY
- ? OFPERR_OFPBRC_BAD_STAT
- : OFPERR_OFPBRC_BAD_TYPE);
+ return OFPERR_OFPBRC_BAD_TYPE;
}
}
static void
ofopgroup_submit(struct ofopgroup *group)
{
- if (list_is_empty(&group->ops)) {
- ofopgroup_destroy(group);
+ if (!group->n_running) {
+ ofopgroup_complete(group);
} else {
list_push_back(&group->ofproto->pending, &group->ofproto_node);
group->ofproto->n_pending++;
}
static void
-ofopgroup_destroy(struct ofopgroup *group)
+ofopgroup_complete(struct ofopgroup *group)
{
- assert(list_is_empty(&group->ops));
+ struct ofproto *ofproto = group->ofproto;
+
+ struct ofconn *abbrev_ofconn;
+ ovs_be32 abbrev_xid;
+
+ struct ofoperation *op, *next_op;
+ int error;
+
+ assert(!group->n_running);
+
+ error = 0;
+ LIST_FOR_EACH (op, group_node, &group->ops) {
+ if (op->error) {
+ error = op->error;
+ break;
+ }
+ }
+
+ if (!error && group->ofconn && group->buffer_id != UINT32_MAX) {
+ LIST_FOR_EACH (op, group_node, &group->ops) {
+ if (op->type != OFOPERATION_DELETE) {
+ struct ofpbuf *packet;
+ uint16_t in_port;
+
+ error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id,
+ &packet, &in_port);
+ if (packet) {
+ assert(!error);
+ error = rule_execute(op->rule, in_port, packet);
+ }
+ break;
+ }
+ }
+ }
+
+ if (!error && !list_is_empty(&group->ofconn_node)) {
+ abbrev_ofconn = group->ofconn;
+ abbrev_xid = group->request->xid;
+ } else {
+ abbrev_ofconn = NULL;
+ abbrev_xid = htonl(0);
+ }
+ LIST_FOR_EACH_SAFE (op, next_op, group_node, &group->ops) {
+ struct rule *rule = op->rule;
+
+ if (!op->error && !ofproto_rule_is_hidden(rule)) {
+ /* Check that we can just cast from ofoperation_type to
+ * nx_flow_update_event. */
+ BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_ADD
+ == NXFME_ADDED);
+ BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_DELETE
+ == NXFME_DELETED);
+ BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_MODIFY
+ == NXFME_MODIFIED);
+
+ ofmonitor_report(ofproto->connmgr, rule,
+ (enum nx_flow_update_event) op->type,
+ op->reason, abbrev_ofconn, abbrev_xid);
+ }
+
+ rule->pending = NULL;
+
+ switch (op->type) {
+ case OFOPERATION_ADD:
+ if (!op->error) {
+ ofproto_rule_destroy__(op->victim);
+ if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK))
+ == htons(VLAN_VID_MASK)) {
+ if (ofproto->vlan_bitmap) {
+ uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci);
+
+ if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) {
+ bitmap_set1(ofproto->vlan_bitmap, vid);
+ ofproto->vlans_changed = true;
+ }
+ } else {
+ ofproto->vlans_changed = true;
+ }
+ }
+ } else {
+ oftable_substitute_rule(rule, op->victim);
+ ofproto_rule_destroy__(rule);
+ }
+ break;
+
+ case OFOPERATION_DELETE:
+ assert(!op->error);
+ ofproto_rule_destroy__(rule);
+ op->rule = NULL;
+ break;
+
+ case OFOPERATION_MODIFY:
+ if (!op->error) {
+ rule->modified = time_msec();
+ } else {
+ rule->flow_cookie = op->flow_cookie;
+ if (op->ofpacts) {
+ free(rule->ofpacts);
+ rule->ofpacts = op->ofpacts;
+ rule->ofpacts_len = op->ofpacts_len;
+ op->ofpacts = NULL;
+ op->ofpacts_len = 0;
+ }
+ }
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ ofoperation_destroy(op);
+ }
+
+ ofmonitor_flush(ofproto->connmgr);
+
if (!list_is_empty(&group->ofproto_node)) {
- assert(group->ofproto->n_pending > 0);
- group->ofproto->n_pending--;
+ assert(ofproto->n_pending > 0);
+ ofproto->n_pending--;
list_remove(&group->ofproto_node);
}
if (!list_is_empty(&group->ofconn_node)) {
list_remove(&group->ofconn_node);
- if (group->error) {
- ofconn_send_error(group->ofconn, group->request, group->error);
+ if (error) {
+ ofconn_send_error(group->ofconn, group->request, error);
}
- connmgr_retry(group->ofproto->connmgr);
+ connmgr_retry(ofproto->connmgr);
}
free(group->request);
free(group);
}
/* Initiates a new operation on 'rule', of the specified 'type', within
- * 'group'. Prior to calling, 'rule' must not have any pending operation. */
-static void
+ * 'group'. Prior to calling, 'rule' must not have any pending operation.
+ *
+ * For a 'type' of OFOPERATION_DELETE, 'reason' should specify the reason that
+ * the flow is being deleted. For other 'type's, 'reason' is ignored (use 0).
+ *
+ * Returns the newly created ofoperation (which is also available as
+ * rule->pending). */
+static struct ofoperation *
ofoperation_create(struct ofopgroup *group, struct rule *rule,
- enum ofoperation_type type)
+ enum ofoperation_type type,
+ enum ofp_flow_removed_reason reason)
{
+ struct ofproto *ofproto = group->ofproto;
struct ofoperation *op;
assert(!rule->pending);
list_push_back(&group->ops, &op->group_node);
op->rule = rule;
op->type = type;
- op->status = -1;
+ op->reason = reason;
op->flow_cookie = rule->flow_cookie;
+ group->n_running++;
+
if (type == OFOPERATION_DELETE) {
- hmap_insert(&op->group->ofproto->deletions, &op->hmap_node,
+ hmap_insert(&ofproto->deletions, &op->hmap_node,
cls_rule_hash(&rule->cr, rule->table_id));
}
+
+ return op;
}
static void
hmap_remove(&group->ofproto->deletions, &op->hmap_node);
}
list_remove(&op->group_node);
- free(op->actions);
+ free(op->ofpacts);
free(op);
-
- if (list_is_empty(&group->ops) && !list_is_empty(&group->ofproto_node)) {
- ofopgroup_destroy(group);
- }
}
/* Indicates that 'op' completed with status 'error', which is either 0 to
ofoperation_complete(struct ofoperation *op, enum ofperr error)
{
struct ofopgroup *group = op->group;
- struct rule *rule = op->rule;
- struct ofproto *ofproto = rule->ofproto;
-
- assert(rule->pending == op);
- assert(op->status < 0);
-
- if (!error
- && !group->error
- && op->type != OFOPERATION_DELETE
- && group->ofconn
- && group->buffer_id != UINT32_MAX
- && list_is_singleton(&op->group_node)) {
- struct ofpbuf *packet;
- uint16_t in_port;
-
- error = ofconn_pktbuf_retrieve(group->ofconn, group->buffer_id,
- &packet, &in_port);
- if (packet) {
- assert(!error);
- error = rule_execute(rule, in_port, packet);
- }
- }
- if (!group->error) {
- group->error = error;
- }
-
- switch (op->type) {
- case OFOPERATION_ADD:
- if (!error) {
- ofproto_rule_destroy__(op->victim);
- if ((rule->cr.wc.vlan_tci_mask & htons(VLAN_VID_MASK))
- == htons(VLAN_VID_MASK)) {
- if (ofproto->vlan_bitmap) {
- uint16_t vid = vlan_tci_to_vid(rule->cr.flow.vlan_tci);
-
- if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) {
- bitmap_set1(ofproto->vlan_bitmap, vid);
- ofproto->vlans_changed = true;
- }
- } else {
- ofproto->vlans_changed = true;
- }
- }
- } else {
- oftable_substitute_rule(rule, op->victim);
- ofproto_rule_destroy__(rule);
- }
- break;
-
- case OFOPERATION_DELETE:
- assert(!error);
- ofproto_rule_destroy__(rule);
- op->rule = NULL;
- break;
- case OFOPERATION_MODIFY:
- if (!error) {
- rule->modified = time_msec();
- } else {
- free(rule->actions);
- rule->actions = op->actions;
- rule->n_actions = op->n_actions;
- op->actions = NULL;
- }
- break;
+ assert(op->rule->pending == op);
+ assert(group->n_running > 0);
+ assert(!error || op->type != OFOPERATION_DELETE);
- default:
- NOT_REACHED();
+ op->error = error;
+ if (!--group->n_running && !list_is_empty(&group->ofproto_node)) {
+ ofopgroup_complete(group);
}
- ofoperation_destroy(op);
}
struct rule *
if (!error) {
return eth_addr_to_uint64(ea);
}
- VLOG_WARN("could not get MAC address for %s (%s)",
- netdev_get_name(port->netdev), strerror(error));
+ VLOG_WARN("%s: could not get MAC address for %s (%s)",
+ ofproto->name, netdev_get_name(port->netdev),
+ strerror(error));
}
return ofproto->fallback_dpid;
}
break;
}
- ofoperation_create(group, rule, OFOPERATION_DELETE);
+ ofoperation_create(group, rule,
+ OFOPERATION_DELETE, OFPRR_EVICTION);
oftable_remove_rule(rule);
ofproto->ofproto_class->rule_destruct(rule);
}
{
memset(table, 0, sizeof *table);
classifier_init(&table->cls);
+ table->max_flows = UINT_MAX;
}
/* Destroys 'table', including its classifier and eviction groups.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ofproto;
struct ofport;
struct shash;
+struct simap;
struct netdev_stats;
struct ofproto_controller_info {
uint8_t dscp; /* DSCP value for controller connection. */
};
-#define DEFAULT_MFR_DESC "Nicira Networks, Inc."
+#define DEFAULT_MFR_DESC "Nicira, Inc."
#define DEFAULT_HW_DESC "Open vSwitch"
#define DEFAULT_SW_DESC VERSION
#define DEFAULT_SERIAL_DESC "None"
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(). */
struct ofproto_port *);
/* Top-level configuration. */
+uint64_t ofproto_get_datapath_id(const struct ofproto *);
void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id);
void ofproto_set_controllers(struct ofproto *,
const struct ofproto_controller *, size_t n);
void ofproto_get_netflow_ids(const struct ofproto *,
uint8_t *engine_type, uint8_t *engine_id);
int ofproto_port_get_cfm_fault(const struct ofproto *, uint16_t ofp_port);
+int ofproto_port_get_cfm_opup(const struct ofproto *, uint16_t ofp_port);
int ofproto_port_get_cfm_remote_mpids(const struct ofproto *,
uint16_t ofp_port, const uint64_t **rmps,
size_t *n_rmps);
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "poll-loop.h"
#include "random.h"
#include "rconn.h"
+#include "sat-math.h"
#include "timeval.h"
+#include "token-bucket.h"
#include "vconn.h"
struct pinqueue {
};
struct pinsched {
- /* Client-supplied parameters. */
- int rate_limit; /* Packets added to bucket per second. */
- int burst_limit; /* Maximum token bucket size, in packets. */
+ struct token_bucket token_bucket;
/* One queue per physical port. */
struct hmap queues; /* Contains "struct pinqueue"s. */
int n_queued; /* Sum over queues[*].n. */
struct pinqueue *next_txq; /* Next pinqueue check in round-robin. */
- /* Token bucket.
- *
- * It costs 1000 tokens to send a single packet_in message. A single token
- * per message would be more straightforward, but this choice lets us avoid
- * round-off error in refill_bucket()'s calculation of how many tokens to
- * add to the bucket, since no division step is needed. */
- long long int last_fill; /* Time at which we last added tokens. */
- int tokens; /* Current number of tokens. */
-
/* Transmission queue. */
int n_txq; /* No. of packets waiting in rconn for tx. */
return packet;
}
+static void
+adjust_limits(int *rate_limit, int *burst_limit)
+{
+ if (*rate_limit <= 0) {
+ *rate_limit = 1000;
+ }
+ if (*burst_limit <= 0) {
+ *burst_limit = *rate_limit / 4;
+ }
+ if (*burst_limit < 1) {
+ *burst_limit = 1;
+ }
+}
+
/* Destroys 'q' and removes it from 'ps''s set of queues.
* (The caller must ensure that 'q' is empty.) */
static void
return packet;
}
-/* Add tokens to the bucket based on elapsed time. */
-static void
-refill_bucket(struct pinsched *ps)
-{
- long long int now = time_msec();
- long long int tokens = (now - ps->last_fill) * ps->rate_limit + ps->tokens;
- if (tokens >= 1000) {
- ps->last_fill = now;
- ps->tokens = MIN(tokens, ps->burst_limit * 1000);
- }
-}
-
/* Attempts to remove enough tokens from 'ps' to transmit a packet. Returns
* true if successful, false otherwise. (In the latter case no tokens are
* removed.) */
static bool
get_token(struct pinsched *ps)
{
- if (ps->tokens >= 1000) {
- ps->tokens -= 1000;
- return true;
- } else {
- return false;
- }
+ return token_bucket_withdraw(&ps->token_bucket, 1000);
}
void
* otherwise wasted space. */
ofpbuf_trim(packet);
- if (ps->n_queued >= ps->burst_limit) {
+ if (ps->n_queued * 1000 >= ps->token_bucket.burst) {
drop_packet(ps);
}
q = pinqueue_get(ps, port_no);
/* Drain some packets out of the bucket if possible, but limit the
* number of iterations to allow other code to get work done too. */
- refill_bucket(ps);
for (i = 0; ps->n_queued && get_token(ps) && i < 50; i++) {
cb(get_tx_packet(ps), aux);
}
pinsched_wait(struct pinsched *ps)
{
if (ps && ps->n_queued) {
- if (ps->tokens >= 1000) {
- /* We can transmit more packets as soon as we're called again. */
- poll_immediate_wake();
- } else {
- /* We have to wait for the bucket to re-fill. We could calculate
- * the exact amount of time here for increased smoothness. */
- poll_timer_wait(TIME_UPDATE_INTERVAL / 2);
- }
+ token_bucket_wait(&ps->token_bucket, 1000);
}
}
struct pinsched *ps;
ps = xzalloc(sizeof *ps);
+
+ adjust_limits(&rate_limit, &burst_limit);
+ token_bucket_init(&ps->token_bucket,
+ rate_limit, sat_mul(burst_limit, 1000));
+
hmap_init(&ps->queues);
ps->n_queued = 0;
ps->next_txq = NULL;
- ps->last_fill = time_msec();
- ps->tokens = rate_limit * 100;
ps->n_txq = 0;
ps->n_normal = 0;
ps->n_limited = 0;
ps->n_queue_dropped = 0;
- pinsched_set_limits(ps, rate_limit, burst_limit);
return ps;
}
pinsched_get_limits(const struct pinsched *ps,
int *rate_limit, int *burst_limit)
{
- *rate_limit = ps->rate_limit;
- *burst_limit = ps->burst_limit;
+ *rate_limit = ps->token_bucket.rate;
+ *burst_limit = ps->token_bucket.burst / 1000;
}
void
pinsched_set_limits(struct pinsched *ps, int rate_limit, int burst_limit)
{
- if (rate_limit <= 0) {
- rate_limit = 1000;
- }
- if (burst_limit <= 0) {
- burst_limit = rate_limit / 4;
- }
- burst_limit = MAX(burst_limit, 1);
- burst_limit = MIN(burst_limit, INT_MAX / 1000);
-
- ps->rate_limit = rate_limit;
- ps->burst_limit = burst_limit;
+ adjust_limits(&rate_limit, &burst_limit);
+ token_bucket_set(&ps->token_bucket,
+ rate_limit, sat_mul(burst_limit, 1000));
while (ps->n_queued > burst_limit) {
drop_packet(ps);
}
}
+
+/* Returns the number of packets scheduled to be sent eventually by 'ps'.
+ * Returns 0 if 'ps' is null. */
+unsigned int
+pinsched_count_txqlen(const struct pinsched *ps)
+{
+ return ps ? ps->n_txq : 0;
+}
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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 */
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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;
+}
/*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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 */
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#! /usr/bin/perl
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
long long int now = time_msec();
struct ovsdb_file *file;
+ char *deref_name;
char *abs_name;
/* Use the absolute name of the file because ovsdb-server opens its
* database before daemonize() chdirs to "/". */
- abs_name = abs_file_name(NULL, file_name);
+ deref_name = follow_symlinks(file_name);
+ abs_name = abs_file_name(NULL, deref_name);
+ free(deref_name);
if (!abs_name) {
*filep = NULL;
return ovsdb_io_error(0, "could not determine current "
/* Lock temporary file. */
tmp_name = xasprintf("%s.tmp", file->file_name);
- retval = lockfile_lock(tmp_name, 0, &tmp_lock);
+ retval = lockfile_lock(tmp_name, &tmp_lock);
if (retval) {
error = ovsdb_io_error(retval, "could not get lock on %s", tmp_name);
goto exit;
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "reconnect.h"
#include "row.h"
#include "server.h"
+#include "simap.h"
#include "stream.h"
#include "table.h"
#include "timeval.h"
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(
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. */
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 *,
}
}
+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)
{
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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 *);
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 */
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
locking = open_mode != OVSDB_LOG_READ_ONLY;
}
if (locking) {
- int retval = lockfile_lock(name, 0, &lockfile);
+ int retval = lockfile_lock(name, &lockfile);
if (retval) {
error = ovsdb_io_error(retval, "%s: failed to lock lockfile",
name);
} else if (open_mode == OVSDB_LOG_READ_WRITE) {
flags = O_RDWR;
} else if (open_mode == OVSDB_LOG_CREATE) {
- flags = O_RDWR | O_CREAT | O_EXCL;
+ if (stat(name, &s) == -1 && errno == ENOENT
+ && lstat(name, &s) == 0 && S_ISLNK(s.st_mode)) {
+ /* 'name' is a dangling symlink. We want to create the file that
+ * the symlink points to, but POSIX says that open() with O_EXCL
+ * must fail with EEXIST if the named file is a symlink. So, we
+ * have to leave off O_EXCL and accept the race. */
+ flags = O_RDWR | O_CREAT;
+ } else {
+ flags = O_RDWR | O_CREAT | O_EXCL;
+ }
} else {
NOT_REACHED();
}
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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.
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
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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 = ''
#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()):
(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()}
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()])
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())
{
struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName,
'c': columnName}
-
type = column.type
if type.value:
keyVar = "row->key_%s" % columnName
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) {"
# 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_)
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 '''
/* 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 *
# 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']
'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())
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.
over active or passive TCP/IP or Unix domain sockets.
.PP
The OVSDB file may be specified on the command line as \fIdatabase\fR.
-The default is \fB@sysconfdir@/openvswitch/conf.db\fR. The database
+The default is \fB@DBDIR@/conf.db\fR. The database
file must already have been created and initialized using, for
example, \fBovsdb\-tool create\fR.
.
clients.
.
.so lib/vlog-unixctl.man
+.so lib/memory-unixctl.man
.so lib/coverage-unixctl.man
.so lib/stress-unixctl.man
.SH "SEE ALSO"
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#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"
#include "poll-loop.h"
#include "process.h"
#include "row.h"
+#include "simap.h"
#include "stream-ssl.h"
#include "stream.h"
#include "stress.h"
daemonize_complete();
+ if (!run_command) {
+ /* ovsdb-server is usually a long-running process, in which case it
+ * makes plenty of sense to log the version, but --run makes
+ * ovsdb-server more like a command-line tool, so skip it. */
+ VLOG_INFO("%s (Open vSwitch) %s", program_name, VERSION);
+ }
+
unixctl_command_register("exit", "", 0, 0, ovsdb_server_exit, &exiting);
unixctl_command_register("ovsdb-server/compact", "", 0, 0,
ovsdb_server_compact, file);
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);
update_remote_status(jsonrpc, &remotes, db);
}
+ memory_wait();
ovsdb_jsonrpc_server_wait(jsonrpc);
unixctl_server_wait(unixctl);
ovsdb_trigger_wait(db, time_msec());
union ovsdb_atom *atom_key = NULL, *atom_value = NULL;
size_t i;
- datum = get_datum((struct ovsdb_row *) row, column_name, OVSDB_TYPE_STRING,
- OVSDB_TYPE_STRING, UINT_MAX);
+ datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name,
+ OVSDB_TYPE_STRING, OVSDB_TYPE_STRING, UINT_MAX);
if (!datum) {
return NULL;
{
const struct ovsdb_datum *datum;
- datum = get_datum((struct ovsdb_row *) row, column_name, type, OVSDB_TYPE_VOID,
- 1);
+ datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name, type,
+ OVSDB_TYPE_VOID, 1);
return datum && datum->n ? datum->keys : NULL;
}
switch (argc) {
case 0:
- *file_namep = xasprintf("%s/openvswitch/conf.db", ovs_sysconfdir());
+ *file_namep = xasprintf("%s/conf.db", ovs_dbdir());
break;
case 1:
.SS "Other Options"
.so lib/common.man
.SH "FILES"
-The default \fIdb\fR is \fB@sysconfdir@/openvswitch/conf.db\fR. The
+The default \fIdb\fR is \fB@DBDIR@/conf.db\fR. The
default \fIschema\fR is \fB@pkgdatadir@/vswitch.ovsschema\fR. The
\fBhelp\fR command also displays these defaults.
.SH "SEE ALSO"
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
static char *db;
if (!db) {
- db = xasprintf("%s/conf.db", ovs_sysconfdir());
+ db = xasprintf("%s/conf.db", ovs_dbdir());
}
return db;
}
}
static void
-compact_or_convert(const char *src_name, const char *dst_name,
+compact_or_convert(const char *src_name_, const char *dst_name_,
const struct ovsdb_schema *new_schema,
const char *comment)
{
+ char *src_name, *dst_name;
struct lockfile *src_lock;
struct lockfile *dst_lock;
- bool in_place = dst_name == NULL;
+ bool in_place = dst_name_ == NULL;
struct ovsdb *db;
int retval;
+ /* Dereference symlinks for source and destination names. In the in-place
+ * case this ensures that, if the source name is a symlink, we replace its
+ * target instead of replacing the symlink by a regular file. In the
+ * non-in-place, this has the same effect for the destination name. */
+ src_name = follow_symlinks(src_name_);
+ dst_name = (in_place
+ ? xasprintf("%s.tmp", src_name)
+ : follow_symlinks(dst_name_));
+
/* Lock the source, if we will be replacing it. */
if (in_place) {
- retval = lockfile_lock(src_name, 0, &src_lock);
+ retval = lockfile_lock(src_name, &src_lock);
if (retval) {
ovs_fatal(retval, "%s: failed to lock lockfile", src_name);
}
}
/* Get (temporary) destination and lock it. */
- if (in_place) {
- dst_name = xasprintf("%s.tmp", src_name);
- }
- retval = lockfile_lock(dst_name, 0, &dst_lock);
+ retval = lockfile_lock(dst_name, &dst_lock);
if (retval) {
ovs_fatal(retval, "%s: failed to lock lockfile", dst_name);
}
lockfile_unlock(dst_lock);
- if (in_place) {
- free((char *) dst_name);
- }
+ free(src_name);
+ free(dst_name);
}
static void
time_t t = json_integer(date);
char s[128];
- strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S", localtime(&t));
+ strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S", gmtime(&t));
printf(" %s", s);
}
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "ovsdb-error.h"
#include "ovsdb-parser.h"
#include "ovsdb-types.h"
+#include "simap.h"
#include "table.h"
#include "transaction.h"
}
}
+/* 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)
{
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ovsdb_log;
struct ovsdb_session;
struct ovsdb_txn;
+struct simap;
struct uuid;
/* Database schema. */
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 *);
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
# Copyright (c) 2010 Citrix Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
# Copyright (c) 2010 Citrix Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
# Copyright (c) 2010 Citrix Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
# Copyright (c) 2010 Citrix Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
# Copyright (c) 2010 Citrix Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
+ sizeof(struct ovsdb_datum) * n_fields
+ sizeof(struct hmap_node) * n_indexes);
struct ovsdb_row *row = xmalloc(row_size);
- row->table = (struct ovsdb_table *) table;
+ row->table = CONST_CAST(struct ovsdb_table *, table);
row->txn_row = NULL;
list_init(&row->src_refs);
list_init(&row->dst_refs);
HMAP_FOR_EACH_SAFE (node, next, hmap_node, &rh->rows) {
hmap_remove(&rh->rows, &node->hmap_node);
if (destroy_rows) {
- ovsdb_row_destroy((struct ovsdb_row *) node->row);
+ ovsdb_row_destroy(CONST_CAST(struct ovsdb_row *, node->row));
}
free(node);
}
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2011 Nicira Networks
+/* Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2011, 2012 Nicira Networks
+/* Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
add_weak_ref(struct ovsdb_txn *txn,
const struct ovsdb_row *src_, const struct ovsdb_row *dst_)
{
- struct ovsdb_row *src = (struct ovsdb_row *) src_;
- struct ovsdb_row *dst = (struct ovsdb_row *) dst_;
+ struct ovsdb_row *src = CONST_CAST(struct ovsdb_row *, src_);
+ struct ovsdb_row *dst = CONST_CAST(struct ovsdb_row *, dst_);
struct ovsdb_weak_ref *weak;
if (src == dst) {
const struct ovsdb_row *old_, struct ovsdb_row *new)
{
const struct ovsdb_row *row = old_ ? old_ : new;
- struct ovsdb_row *old = (struct ovsdb_row *) old_;
+ struct ovsdb_row *old = CONST_CAST(struct ovsdb_row *, old_);
size_t n_columns = shash_count(&table->schema->columns);
struct ovsdb_txn_table *txn_table;
struct ovsdb_txn_row *txn_row;
struct ovsdb_row *
ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_)
{
- struct ovsdb_row *ro_row = (struct ovsdb_row *) ro_row_;
+ struct ovsdb_row *ro_row = CONST_CAST(struct ovsdb_row *, ro_row_);
if (ro_row->txn_row) {
assert(ro_row == ro_row->txn_row->new);
void
ovsdb_txn_row_delete(struct ovsdb_txn *txn, const struct ovsdb_row *row_)
{
- struct ovsdb_row *row = (struct ovsdb_row *) row_;
+ struct ovsdb_row *row = CONST_CAST(struct ovsdb_row *, row_);
struct ovsdb_table *table = row->table;
struct ovsdb_txn_row *txn_row = row->txn_row;
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009, 2011 Nicira Networks
+/* Copyright (c) 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
SSH_KEY_OPTION ?= -i $(SSH_KEY)
endif
-SSH_OPTIONS ?= $(SSH_KEY_OPTION) -l $(SLICE)
+SSH_OPTIONS ?= $(SSH_KEY_OPTION) -l $(SLICE) -t
SSH = ssh $(SSH_OPTIONS)
ALL_NODE_IDS=$(shell (cut -d- -f1 < links; cut -d- -f2 < links) | sort -u)
@host $(HOST_$*) | sed -n 's/^.*has address *//p' > $@
cache/db.%:
+<<<<<<< HEAD
@echo "Starting db server on $(call display,$*)"
@$(SSH) $(HOST_$*) sudo start_ovsdb-server > $@ \
|| { rm $@; exit 1; }
-run_python = PYTHONPATH=$(top_srcdir)/python:$$PYTHON_PATH $(PYTHON)
-
ovstest_pyfiles = \
python/ovstest/__init__.py \
python/ovstest/args.py \
python/ovstest/rpcserver.py \
python/ovstest/tcp.py \
+ python/ovstest/tests.py \
python/ovstest/udp.py \
python/ovstest/util.py \
python/ovstest/vswitch.py
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
nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles)
ovs-install-data-local:
$(MKDIR_P) python/ovs
- (echo "import os" && \
- echo 'PKGDATADIR = os.environ.get("OVS_PKGDATADIR", """$(pkgdatadir)""")' && \
- echo 'RUNDIR = os.environ.get("OVS_RUNDIR", """@RUNDIR@""")' && \
- echo 'LOGDIR = os.environ.get("OVS_LOGDIR", """@LOGDIR@""")' && \
- echo 'BINDIR = os.environ.get("OVS_BINDIR", """$(bindir)""")') \
+ sed \
+ -e '/^##/d' \
+ -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \
+ -e 's,[@]RUNDIR[@],$(RUNDIR),g' \
+ -e 's,[@]LOGDIR[@],$(LOGDIR),g' \
+ -e 's,[@]bindir[@],$(bindir),g' \
+ -e 's,[@]sysconfdir[@],$(sysconfdir),g' \
+ -e 's,[@]DBDIR[@],$(DBDIR),g' \
+ < $(srcdir)/python/ovs/dirs.py.template \
> python/ovs/dirs.py.tmp
$(MKDIR_P) $(DESTDIR)$(pkgdatadir)/python/ovs
$(INSTALL_DATA) python/ovs/dirs.py.tmp $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py
$(ro_shell) > $(@F).tmp
echo 'VERSION = "$(VERSION)"' >> $(@F).tmp
if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi
+
+ALL_LOCAL += $(srcdir)/python/ovs/dirs.py
+$(srcdir)/python/ovs/dirs.py: python/ovs/dirs.py.template
+ sed \
+ -e '/^##/d' \
+ -e 's,[@]pkgdatadir[@],/usr/local/share/openvswitch,g' \
+ -e 's,[@]RUNDIR[@],/var/run,g' \
+ -e 's,[@]LOGDIR[@],/usr/local/var/log,g' \
+ -e 's,[@]bindir[@],/usr/local/bin,g' \
+ -e 's,[@]sysconfdir[@],/usr/local/etc,g' \
+ -e 's,[@]DBDIR[@],/usr/local/etc/openvswitch,g' \
+ < $? > $@.tmp
+ mv $@.tmp $@
+EXTRA_DIST += python/ovs/dirs.py python/ovs/dirs.py.template
-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
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))
-# These are the default directories. They will be replaced by the
-# configured directories at install time.
-
import os
-PKGDATADIR = os.environ.get("OVS_PKGDATADIR", "/usr/local/share/openvswitch")
-RUNDIR = os.environ.get("OVS_RUNDIR", "/var/run")
-LOGDIR = os.environ.get("OVS_LOGDIR", "/usr/local/var/log")
-BINDIR = os.environ.get("OVS_BINDIR", "/usr/local/bin")
+PKGDATADIR = os.environ.get("OVS_PKGDATADIR", """/usr/local/share/openvswitch""")
+RUNDIR = os.environ.get("OVS_RUNDIR", """/var/run""")
+LOGDIR = os.environ.get("OVS_LOGDIR", """/usr/local/var/log""")
+BINDIR = os.environ.get("OVS_BINDIR", """/usr/local/bin""")
+
+DBDIR = os.environ.get("OVS_DBDIR")
+if not DBDIR:
+ sysconfdir = os.environ.get("OVS_SYSCONFDIR")
+ if sysconfdir:
+ DBDIR = "%s/openvswitch" % sysconfdir
+ else:
+ DBDIR = """/usr/local/etc/openvswitch"""
--- /dev/null
+## The @variables@ in this file are replaced by default directories for
+## use in python/ovs/dirs.py in the source directory and replaced by the
+## configured directories for use in the installed python/ovs/dirs.py.
+##
+import os
+PKGDATADIR = os.environ.get("OVS_PKGDATADIR", """@pkgdatadir@""")
+RUNDIR = os.environ.get("OVS_RUNDIR", """@RUNDIR@""")
+LOGDIR = os.environ.get("OVS_LOGDIR", """@LOGDIR@""")
+BINDIR = os.environ.get("OVS_BINDIR", """@bindir@""")
+
+DBDIR = os.environ.get("OVS_DBDIR")
+if not DBDIR:
+ sysconfdir = os.environ.get("OVS_SYSCONFDIR")
+ if sysconfdir:
+ DBDIR = "%s/openvswitch" % sysconfdir
+ else:
+ DBDIR = """@DBDIR@"""
-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
if not self.status:
self.stream.run_wait(poller)
if len(self.output):
- self.stream.send_wait()
+ self.stream.send_wait(poller)
def get_status(self):
return self.status
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2010 Nicira Networks
+# Copyright (c) 2010 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2010, 2011 Nicira Networks
+# Copyright (c) 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2010, 2012 Nicira Networks
+# Copyright (c) 2010, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
def stream_or_pstream_needs_probes(name):
""" 1 if the stream or pstream specified by 'name' needs periodic probes to
- verify connectivty. For [p]streams which need probes, it can take a long
+ verify connectivity. For [p]streams which need probes, it can take a long
time to notice the connection was dropped. Returns 0 if probes aren't
needed, and -1 if 'name' is invalid"""
break
stream.run()
poller = ovs.poller.Poller()
- stream.run_wait()
+ stream.run_wait(poller)
stream.connect_wait(poller)
poller.block()
assert error != errno.EINPROGRESS
-# Copyright (c) 2009, 2010 Nicira Networks
+# Copyright (c) 2009, 2010 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
--- /dev/null
+# 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)
--- /dev/null
+# 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))
-# Copyright (c) 2012 Nicira Networks
+# Copyright (c) 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
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)
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:
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)
% 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)
-# Copyright (c) 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2011 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
import datetime
import logging
import logging.handlers
+import re
import socket
import sys
import ovs.dirs
+import ovs.unixctl
import ovs.util
FACILITIES = {"console": "info", "file": "info", "syslog": "info"}
__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
if not Vlog.__inited:
return
- now = datetime.datetime.now().strftime("%b %d %H:%M:%S")
+ now = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
message = ("%s|%s|%s|%s|%s"
% (now, Vlog.__msg_num, self.name, level, message))
Vlog.__inited = True
logging.raiseExceptions = False
+ Vlog.__log_file = log_file
for f in FACILITIES:
logger = logging.getLogger(f)
logger.setLevel(logging.DEBUG)
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'.
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
" 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):
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)
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
"must be colon-separated")
+def ip_optional_port_port(string, default_port1, default_port2, ip_callback):
+ """Convert a string into IP, Port1, Port2 tuple. If any of ports were
+ missing, then default ports will be used. The fourth argument is a
+ callback that verifies whether IP address is given in the expected
+ format."""
+ value = string.split(':')
+ if len(value) == 1:
+ return (ip_callback(value[0]), default_port1, default_port2)
+ elif len(value) == 2:
+ return (ip_callback(value[0]), port(value[1]), default_port2)
+ elif len(value) == 3:
+ return (ip_callback(value[0]), port(value[1]), port(value[2]))
+ else:
+ raise argparse.ArgumentTypeError("Expected IP address and at most "
+ "two colon-separated ports")
+
+
def vlan_tag(string):
"""
This function verifies whether given string is a correct VLAN tag.
return string.split(',')
+def l3_endpoint_client(string):
+ """
+ This function parses command line argument string in
+ remoteIP,localInnerIP[/mask][:ControlPort[:TestPort]],remoteInnerIP[:
+ ControlPort[:TestPort]] format.
+ """
+ try:
+ remote_ip, me, he = string.split(',')
+ except ValueError:
+ raise argparse.ArgumentTypeError("All 3 IP addresses must be comma "
+ "separated.")
+ r = (ip_address(remote_ip),
+ ip_optional_port_port(me, CONTROL_PORT, DATA_PORT, ip_optional_mask),
+ ip_optional_port_port(he, CONTROL_PORT, DATA_PORT, ip_address))
+ return r
+
+
+def l3_endpoint_server(string):
+ """
+ This function parses a command line argument string in
+ remoteIP,localInnerIP[/mask][:ControlPort] format.
+ """
+ try:
+ remote_ip, me = string.split(',')
+ except ValueError:
+ raise argparse.ArgumentTypeError("Both IP addresses must be comma "
+ "separated.")
+ return (ip_address(remote_ip),
+ ip_optional_port(me, CONTROL_PORT, ip_optional_mask))
+
+
def ovs_initialize_args():
"""
Initialize argument parsing for ovs-test utility.
'ovs-test server in the client mode by using 127.0.0.1 as '
'OuterIP.')
return parser.parse_args()
+
+def l3_initialize_args():
+ """
+ Initialize argument parsing for ovs-l3ping utility.
+ """
+ parser = argparse.ArgumentParser(description='Test L3 tunnel '
+ 'connectivity between two Open vSwitch instances.')
+
+ parser.add_argument('-v', '--version', action='version',
+ version='ovs-l3ping (Open vSwitch) @VERSION@')
+
+ parser.add_argument("-b", "--bandwidth", action='store',
+ dest="targetBandwidth", default="1M", type=bandwidth,
+ help='Target bandwidth for UDP tests in bits/second. Use '
+ 'postfix M or K to alter unit magnitude.')
+ parser.add_argument("-i", "--interval", action='store',
+ dest="testInterval", default=5, type=int,
+ help='Interval for how long to run each test in seconds.')
+
+ parser.add_argument("-t", "--tunnel-mode", action='store',
+ dest="tunnelMode", required=True,
+ help='Do L3 tests with this tunnel type.')
+
+ group = parser.add_mutually_exclusive_group(required=True)
+ group.add_argument("-s", "--server", action="store", dest="server",
+ metavar="TUNNELIP,SERVER",
+ type=l3_endpoint_server,
+ help='Run in server mode and wait for the client to '
+ 'connect.')
+ group.add_argument('-c', "--client", action="store", dest="client",
+ metavar="TUNNELIP,CLIENT,SERVER",
+ type=l3_endpoint_client,
+ help='Run in client mode and connect to the server.')
+ return parser.parse_args()
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
"""
return util.get_driver(iface)
+ def xmlrpc_get_interface_from_routing_decision(self, ip):
+ """
+ Returns driver version
+ """
+ return util.get_interface_from_routing_decision(ip)
+
def start_rpc_server(port):
"""
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
--- /dev/null
+import math
+import time
+
+import ovstest.util as util
+
+DEFAULT_TEST_BRIDGE = "ovstestbr0"
+DEFAULT_TEST_PORT = "ovstestport0"
+DEFAULT_TEST_TUN = "ovstestport1"
+NO_HANDLE = -1
+
+
+def do_udp_tests(receiver, sender, tbwidth, duration, port_sizes):
+ """Schedule UDP tests between receiver and sender"""
+ server1 = util.rpc_client(receiver[0], receiver[1])
+ server2 = util.rpc_client(sender[0], sender[1])
+
+ udpformat = '{0:>15} {1:>15} {2:>15} {3:>15} {4:>15}'
+
+ print ("UDP test from %s:%u to %s:%u with target bandwidth %s" %
+ (sender[0], sender[1], receiver[0], receiver[1],
+ util.bandwidth_to_string(tbwidth)))
+ print udpformat.format("Datagram Size", "Snt Datagrams", "Rcv Datagrams",
+ "Datagram Loss", "Bandwidth")
+
+ for size in port_sizes:
+ listen_handle = NO_HANDLE
+ send_handle = NO_HANDLE
+ try:
+ packetcnt = (tbwidth * duration) / size
+
+ listen_handle = server1.create_udp_listener(receiver[3])
+ if listen_handle == NO_HANDLE:
+ print ("Server could not open UDP listening socket on port"
+ " %u. Try to restart the server.\n" % receiver[3])
+ return
+ send_handle = server2.create_udp_sender(
+ (util.ip_from_cidr(receiver[2]),
+ receiver[3]), packetcnt, size,
+ duration)
+
+ # Using sleep here because there is no other synchronization
+ # source that would notify us when all sent packets were received
+ time.sleep(duration + 1)
+
+ rcv_packets = server1.get_udp_listener_results(listen_handle)
+ snt_packets = server2.get_udp_sender_results(send_handle)
+
+ loss = math.ceil(((snt_packets - rcv_packets) * 10000.0) /
+ snt_packets) / 100
+ bwidth = (rcv_packets * size) / duration
+
+ print udpformat.format(size, snt_packets, rcv_packets,
+ '%.2f%%' % loss, util.bandwidth_to_string(bwidth))
+ finally:
+ if listen_handle != NO_HANDLE:
+ server1.close_udp_listener(listen_handle)
+ if send_handle != NO_HANDLE:
+ server2.close_udp_sender(send_handle)
+ print "\n"
+
+
+def do_tcp_tests(receiver, sender, duration):
+ """Schedule TCP tests between receiver and sender"""
+ server1 = util.rpc_client(receiver[0], receiver[1])
+ server2 = util.rpc_client(sender[0], sender[1])
+
+ tcpformat = '{0:>15} {1:>15} {2:>15}'
+ print "TCP test from %s:%u to %s:%u (full speed)" % (sender[0], sender[1],
+ receiver[0], receiver[1])
+ print tcpformat.format("Snt Bytes", "Rcv Bytes", "Bandwidth")
+
+ listen_handle = NO_HANDLE
+ send_handle = NO_HANDLE
+ try:
+ listen_handle = server1.create_tcp_listener(receiver[3])
+ if listen_handle == NO_HANDLE:
+ print ("Server was unable to open TCP listening socket on port"
+ " %u. Try to restart the server.\n" % receiver[3])
+ return
+ send_handle = server2.create_tcp_sender(util.ip_from_cidr(receiver[2]),
+ receiver[3], duration)
+
+ time.sleep(duration + 1)
+
+ rcv_bytes = long(server1.get_tcp_listener_results(listen_handle))
+ snt_bytes = long(server2.get_tcp_sender_results(send_handle))
+
+ bwidth = rcv_bytes / duration
+
+ print tcpformat.format(snt_bytes, rcv_bytes,
+ util.bandwidth_to_string(bwidth))
+ finally:
+ if listen_handle != NO_HANDLE:
+ server1.close_tcp_listener(listen_handle)
+ if send_handle != NO_HANDLE:
+ server2.close_tcp_sender(send_handle)
+ print "\n"
+
+
+def do_l3_tests(node1, node2, bandwidth, duration, ps, type):
+ """
+ Do L3 tunneling tests. Each node is given as 4 tuple - physical
+ interface IP, control port, test IP and test port.
+ """
+ server1 = util.rpc_client(node1[0], node1[1])
+ server2 = util.rpc_client(node2[0], node2[1])
+ servers_with_bridges = []
+ try:
+ server1.create_bridge(DEFAULT_TEST_BRIDGE)
+ servers_with_bridges.append(server1)
+ server2.create_bridge(DEFAULT_TEST_BRIDGE)
+ servers_with_bridges.append(server2)
+
+ server1.interface_up(DEFAULT_TEST_BRIDGE)
+ server2.interface_up(DEFAULT_TEST_BRIDGE)
+
+ server1.interface_assign_ip(DEFAULT_TEST_BRIDGE, node1[2], None)
+ server2.interface_assign_ip(DEFAULT_TEST_BRIDGE, node2[2], None)
+
+ server1.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
+ server2.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
+
+ server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
+ None, type)
+ server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
+ None, type)
+ server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
+ "remote_ip", node2[0])
+ server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
+ "remote_ip", node1[0])
+
+ do_udp_tests(node1, node2, bandwidth, duration, ps)
+ do_udp_tests(node2, node1, bandwidth, duration, ps)
+ do_tcp_tests(node1, node2, duration)
+ do_tcp_tests(node2, node1, duration)
+
+ finally:
+ for server in servers_with_bridges:
+ server.del_bridge(DEFAULT_TEST_BRIDGE)
+
+
+
+def do_vlan_tests(node1, node2, bandwidth, duration, ps, tag):
+ """
+ Do VLAN tests between node1 and node2. Each node is given
+ as 4 tuple - physical interface IP, control port, test IP and
+ test port.
+ """
+ server1 = util.rpc_client(node1[0], node1[1])
+ server2 = util.rpc_client(node2[0], node2[1])
+
+ br_name1 = None
+ br_name2 = None
+
+ servers_with_test_ports = []
+
+ try:
+ interface_node1 = server1.get_interface(node1[0])
+ interface_node2 = server2.get_interface(node2[0])
+
+ if server1.is_ovs_bridge(interface_node1):
+ br_name1 = interface_node1
+ else:
+ br_name1 = DEFAULT_TEST_BRIDGE
+ server1.create_test_bridge(br_name1, interface_node1)
+
+ if server2.is_ovs_bridge(interface_node2):
+ br_name2 = interface_node2
+ else:
+ br_name2 = DEFAULT_TEST_BRIDGE
+ server2.create_test_bridge(br_name2, interface_node2)
+
+ server1.add_port_to_bridge(br_name1, DEFAULT_TEST_PORT)
+ servers_with_test_ports.append(server1)
+ server2.add_port_to_bridge(br_name2, DEFAULT_TEST_PORT)
+ servers_with_test_ports.append(server2)
+
+ server1.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
+ server2.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
+
+ server1.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
+ "internal")
+ server2.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
+ "internal")
+
+ server1.interface_assign_ip(DEFAULT_TEST_PORT, node1[2], None)
+ server2.interface_assign_ip(DEFAULT_TEST_PORT, node2[2], None)
+
+ server1.interface_up(DEFAULT_TEST_PORT)
+ server2.interface_up(DEFAULT_TEST_PORT)
+
+ do_udp_tests(node1, node2, bandwidth, duration, ps)
+ do_udp_tests(node2, node1, bandwidth, duration, ps)
+ do_tcp_tests(node1, node2, duration)
+ do_tcp_tests(node2, node1, duration)
+
+ finally:
+ for server in servers_with_test_ports:
+ server.del_port_from_bridge(DEFAULT_TEST_PORT)
+ if br_name1 == DEFAULT_TEST_BRIDGE:
+ server1.del_test_bridge(br_name1, interface_node1)
+ if br_name2 == DEFAULT_TEST_BRIDGE:
+ server2.del_test_bridge(br_name2, interface_node2)
+
+
+def do_direct_tests(node1, node2, bandwidth, duration, ps):
+ """
+ Do tests between outer IPs without involving Open vSwitch. Each
+ node is given as 4 tuple - physical interface IP, control port,
+ test IP and test port. Direct tests will use physical interface
+ IP as the test IP address.
+ """
+ n1 = (node1[0], node1[1], node1[0], node1[3])
+ n2 = (node2[0], node2[1], node2[0], node2[3])
+
+ do_udp_tests(n1, n2, bandwidth, duration, ps)
+ do_udp_tests(n2, n1, bandwidth, duration, ps)
+ do_tcp_tests(n1, n2, duration)
+ do_tcp_tests(n2, n1, duration)
+
+
+def configure_l3(conf, tunnel_mode):
+ """
+ This function creates a temporary test bridge and adds an L3 tunnel.
+ """
+ s = util.start_local_server(conf[1][1])
+ server = util.rpc_client("127.0.0.1", conf[1][1])
+ server.create_bridge(DEFAULT_TEST_BRIDGE)
+ server.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_PORT)
+ server.interface_up(DEFAULT_TEST_BRIDGE)
+ server.interface_assign_ip(DEFAULT_TEST_BRIDGE, conf[1][0],
+ None)
+ server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type",
+ None, tunnel_mode)
+ server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "options",
+ "remote_ip", conf[0])
+ return s
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (c) 2011, 2012 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
import exceptions
import fcntl
import os
+import select
import socket
import struct
+import signal
import subprocess
import re
+import xmlrpclib
def str_ip(ip_address):
for route in out.splitlines():
args = ["ip", "route", "replace", "dev", iface2] + route.split()
start_process(args)
+
+
+def get_interface_from_routing_decision(ip):
+ """
+ This function returns the interface through which the given ip address
+ is reachable.
+ """
+ args = ["ip", "route", "get", ip]
+ ret, out, _err = start_process(args)
+ if ret == 0:
+ iface = re.search(r'dev (\S+)', out)
+ if iface:
+ return iface.group(1)
+ return None
+
+
+def rpc_client(ip, port):
+ return xmlrpclib.Server("http://%s:%u/" % (ip, port), allow_none=True)
+
+
+def sigint_intercept():
+ """
+ Intercept SIGINT from child (the local ovs-test server process).
+ """
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+
+def start_local_server(port):
+ """
+ This function spawns an ovs-test server that listens on specified port
+ and blocks till the spawned ovs-test server is ready to accept XML RPC
+ connections.
+ """
+ p = subprocess.Popen(["ovs-test", "-s", str(port)],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ preexec_fn=sigint_intercept)
+ fcntl.fcntl( p.stdout.fileno(),fcntl.F_SETFL,
+ fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
+
+ while p.poll() is None:
+ fd = select.select([p.stdout.fileno()], [], [])[0]
+ if fd:
+ out = p.stdout.readline()
+ if out.startswith("Starting RPC server"):
+ break
+ if p.poll() is not None:
+ raise RuntimeError("Couldn't start local instance of ovs-test server")
+ return p
+
+
+def get_datagram_sizes(mtu1, mtu2):
+ """
+ This function calculates all the "interesting" datagram sizes so that
+ we test both - receive and send side with different packets sizes.
+ """
+ s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1])
+ s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2])
+ return sorted(s1.union(s2))
+
+
+def ip_from_cidr(string):
+ """
+ This function removes the netmask (if present) from the given string and
+ returns the IP address.
+ """
+ token = string.split("/")
+ return token[0]
+
+
+def bandwidth_to_string(bwidth):
+ """Convert bandwidth from long to string and add units."""
+ bwidth = bwidth * 8 # Convert back to bits/second
+ if bwidth >= 10000000:
+ return str(int(bwidth / 1000000)) + "Mbps"
+ elif bwidth > 10000:
+ return str(int(bwidth / 1000)) + "Kbps"
+ else:
+ return str(int(bwidth)) + "bps"
-# Copyright (c) 2012 Nicira Networks
+# Copyright (c) 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# chkconfig: 2345 09 91
# description: Manage Open vSwitch kernel modules and user-space daemons
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
if test X"$BRCOMPAT" = Xyes; then
set "$@" --brcompat
fi
+ set "$@" $OVS_CTL_OPTS
"$@"
$ovs_ctl --protocol=gre enable-protocol
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
missingok
postrotate
# Tell Open vSwitch daemons to reopen their log files
- if [ -e /var/run/openvswitch/ovs-vswitchd.pid ]; then
- /usr/bin/ovs-appctl -t ovs-vswitchd vlog/reopen
- fi
- if [ -e /var/run/openvswitch/ovsdb-server.pid ]; then
- /usr/bin/ovs-appctl -t ovsdb-server vlog/reopen
- fi
+ for pidfile in `cd /var/run/openvswitch && echo *.pid`; do
+ ovs-appctl -t "${pidfile%%.pid}" vlog/reopen
+ done
endscript
}
[ -f ../network ] && . ../network
CONFIG=${1}
+TIMEOUT=10
source_config
OVSBridge)
${OTHERSCRIPT} ${CONFIG} $2
retval=$?
- ovs-vsctl -- --if-exists del-br "$DEVICE"
+ ovs-vsctl -t ${TIMEOUT} -- --if-exists del-br "$DEVICE"
;;
OVSPort|OVSIntPort|OVSBond)
${OTHERSCRIPT} ${CONFIG} $2
retval=$?
- ovs-vsctl -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE"
+ ovs-vsctl -t ${TIMEOUT} -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE"
;;
*)
echo $"Invalid OVS interface type $TYPE"
[ -f ../network ] && . ../network
CONFIG=${1}
+TIMEOUT=10
need_config ${CONFIG}
case "$TYPE" in
OVSBridge)
- ovs-vsctl -- --may-exist add-br "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
+ ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
if [ "${OVSBOOTPROTO}" = "dhcp" ] && [ -n "${OVSINTF}" ]; then
case " ${OVSDHCPINTERFACES} " in
*" ${OVSINTF} "*)
OVSPort)
/sbin/ifup "$OVS_BRIDGE"
${OTHERSCRIPT} ${CONFIG} ${2}
- ovs-vsctl -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
+ ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE"
;;
OVSIntPort)
/sbin/ifup "$OVS_BRIDGE"
- ovs-vsctl -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=internal ${OVS_EXTRA+-- $OVS_EXTRA}
+ ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=internal ${OVS_EXTRA+-- $OVS_EXTRA}
${OTHERSCRIPT} ${CONFIG} ${2}
;;
OVSBond)
for _iface in $BOND_IFACES; do
/sbin/ifup ${_iface}
done
- ovs-vsctl -- --fake-iface add-bond "$OVS_BRIDGE" "$DEVICE" ${BOND_IFACES} $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
+ ovs-vsctl -t ${TIMEOUT} -- --fake-iface add-bond "$OVS_BRIDGE" "$DEVICE" ${BOND_IFACES} $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
${OTHERSCRIPT} ${CONFIG} ${2}
OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE"
;;
# Spec file for Open vSwitch kernel modules on Red Hat Enterprise
# Linux 5.
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# Spec file for Open vSwitch kernel modules on Red Hat Enterprise
# Linux 6.
-# Copyright (C) 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# Spec file for Open vSwitch on Red Hat Enterprise Linux.
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
Summary: Open vSwitch daemon/database/utilities
Group: System Environment/Daemons
URL: http://www.openvswitch.org/
-Vendor: Nicira Networks, Inc.
+Vendor: Nicira, Inc.
Version: @VERSION@
License: ASL 2.0
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
$RPM_BUILD_ROOT/usr/bin/ovs-controller \
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
$RPM_BUILD_ROOT/usr/bin/ovs-test \
+ $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \
+ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \
$RPM_BUILD_ROOT/usr/sbin/ovs-vlan-bug-workaround \
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-vlan-bug-workaround.8
/usr/share/openvswitch/bugtool-plugins/
/usr/share/openvswitch/python/
/usr/share/openvswitch/scripts/ovs-bugtool-*
+/usr/share/openvswitch/scripts/ovs-check-dead-ifs
/usr/share/openvswitch/scripts/ovs-ctl
/usr/share/openvswitch/scripts/ovs-lib
/usr/share/openvswitch/scripts/ovs-save
/usr/share/openvswitch/scripts/sysconfig.template
/usr/share/openvswitch/vswitch.ovsschema
+/usr/share/doc/openvswitch-%{version}/FAQ
/usr/share/doc/openvswitch-%{version}/README.RHEL
/var/lib/openvswitch
### Configuration options for openvswitch
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
# FORCE_COREFILES: If 'yes' then core files will be enabled.
# FORCE_COREFILES=yes
# 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=
-# Copyright (c) 2011 Nicira Networks
+# Copyright (c) 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# See the License for the specific language governing permissions and
# limitations under the License.
+import re
+
def xapi_local():
return Session()
def get_all(self):
return [RecordRef(rec) for rec in self.records]
+ def get_all_records_where(self, condition):
+ k, v = re.match(r'field "([^"]*)"="([^"]*)"$', condition).groups()
+ d = {}
+
+ # I'm sure that the keys used in the dictionary below are wrong
+ # but I can't find any documentation on get_all_records_where
+ # and this satisfies the current test case.
+ i = 0
+ for rec in self.records:
+ if rec[k] == v:
+ d[i] = rec
+ i += 1
+ return d
+
def get_by_uuid(self, uuid):
recs = [rec for rec in self.records if rec["uuid"] == uuid]
if len(recs) != 1:
-# Copyright (c) 2012 Nicira Networks.
+# Copyright (c) 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
import ovs.daemon
import ovs.unixctl
+import ovs.unixctl.client
import ovs.util
import ovs.vlog
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)
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
:
tests/check-structs.at \
tests/daemon.at \
tests/daemon-py.at \
+ tests/ofp-actions.at \
tests/ofp-print.at \
tests/ofp-errors.at \
tests/ovs-ofctl.at \
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 '----------------------------------------------------------------------'
@echo
@COVERAGE_FILE=$(COVERAGE_FILE) $(COVERAGE) report
\f
-# lcov support
-
-lcov_wrappers = \
- tests/lcov/ovs-appctl \
- tests/lcov/ovs-ofctl \
- tests/lcov/ovs-vsctl \
- tests/lcov/ovs-vswitchd \
- tests/lcov/ovsdb-client \
- tests/lcov/ovsdb-server \
- tests/lcov/ovsdb-tool \
- tests/lcov/test-aes128 \
- tests/lcov/test-bundle \
- tests/lcov/test-byte-order \
- tests/lcov/test-classifier \
- tests/lcov/test-csum \
- tests/lcov/test-file_name \
- tests/lcov/test-flows \
- tests/lcov/test-hash \
- tests/lcov/test-heap \
- tests/lcov/test-hmap \
- tests/lcov/test-json \
- tests/lcov/test-jsonrpc \
- tests/lcov/test-list \
- tests/lcov/test-lockfile \
- tests/lcov/test-multipath \
- tests/lcov/test-odp \
- tests/lcov/test-ovsdb \
- tests/lcov/test-packets \
- tests/lcov/test-random \
- tests/lcov/test-reconnect \
- tests/lcov/test-sha1 \
- tests/lcov/test-stp \
- tests/lcov/test-timeval \
- tests/lcov/test-type-props \
- tests/lcov/test-unix-socket \
- tests/lcov/test-uuid \
- tests/lcov/test-vconn
-
-$(lcov_wrappers): tests/lcov-wrapper.in
- @test -d tests/lcov || mkdir tests/lcov
- sed -e 's,[@]abs_top_builddir[@],$(abs_top_builddir),' \
- -e 's,[@]wrap_program[@],$@,' \
- $(top_srcdir)/tests/lcov-wrapper.in > $@.tmp
- chmod +x $@.tmp
- mv $@.tmp $@
-CLEANFILES += $(lcov_wrappers)
-EXTRA_DIST += tests/lcov-wrapper.in
-
-LCOV = lcov -b $(abs_top_builddir) -d $(abs_top_builddir) -q
-check-lcov: all tests/atconfig tests/atlocal $(TESTSUITE) $(lcov_wrappers)
- rm -fr tests/coverage.html tests/coverage.info
- $(LCOV) -c -i -o - > tests/coverage.info
- $(SHELL) '$(TESTSUITE)' -C tests CHECK_LCOV=true DISABLE_LCOV=false AUTOTEST_PATH='tests/lcov:$(AUTOTEST_PATH)' $(TESTSUITEFLAGS); \
- rc=$$?; \
- echo "Producing coverage.html..."; \
- cd tests && genhtml -q -o coverage.html coverage.info; \
- exit $$rc
-\f
# valgrind support
valgrind_wrappers = \
--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 '----------------------------------------------------------------------'
[usable protocols: any
chosen protocol: OpenFlow10-table_id
OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(1,NXM_NX_REG0[[]])
-])
+], [stderr])
AT_CHECK([ovs-ofctl parse-flow 'actions=autopath(2, NXM_NX_REG0[[2..30]])'], [0],
[usable protocols: any
chosen protocol: OpenFlow10-table_id
OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(2,NXM_NX_REG0[[2..30]])
+], [stderr])
+AT_CHECK([[sed 's/^[^|]*|[^|]*|//' stderr]], [0], [dnl
+autopath|WARN|The autopath action is deprecated and may be removed in February 2013. Please email dev@openvswitch.org with concerns.
])
AT_CLEANUP
AT_BANNER([daemon unit tests - C])
AT_SETUP([daemon])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov wrapper make pids differ
OVSDB_INIT([db])
AT_CAPTURE_FILE([pid])
AT_CAPTURE_FILE([expected])
AT_CLEANUP
AT_SETUP([daemon --monitor])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov wrapper make pids differ
OVSDB_INIT([db])
AT_CAPTURE_FILE([pid])
AT_CAPTURE_FILE([parent])
AT_CLEANUP
AT_SETUP([daemon --detach])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov wrapper make pids differ
AT_CAPTURE_FILE([pid])
OVSDB_INIT([db])
# Start the daemon and make sure that the pidfile exists immediately.
# We don't wait for the pidfile to get created because the daemon is
# supposed to do so before the parent exits.
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0])
AT_CHECK([test -s pid])
AT_CHECK([kill -0 `cat pid`])
# Kill the daemon and make sure that the pidfile gets deleted.
AT_CLEANUP
AT_SETUP([daemon --detach --monitor])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov wrapper make pids differ
m4_define([CHECK],
[AT_CHECK([$1], [$2], [$3], [$4], [kill `cat daemon monitor`])])
OVSDB_INIT([db])
# Start the daemon and make sure that the pidfile exists immediately.
# We don't wait for the pidfile to get created because the daemon is
# supposed to do so before the parent exits.
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/daemon --monitor --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/daemon --monitor --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0])
AT_CHECK([test -s daemon])
# Check that the pidfile names a running process,
# and that the parent process of that process is a running process,
AT_SETUP([daemon --detach startup errors])
AT_CAPTURE_FILE([pid])
OVSDB_INIT([db])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/nonexistent/unixctl db], [1], [], [stderr])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/nonexistent/unixctl db], [1], [], [stderr])
AT_CHECK([grep 'ovsdb-server: could not initialize control socket' stderr],
[0], [ignore], [])
AT_CHECK([test ! -s pid])
AT_SETUP([daemon --detach --monitor startup errors])
AT_CAPTURE_FILE([pid])
OVSDB_INIT([db])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --monitor --unixctl="`pwd`"/nonexistent/unixctl db], [1], [], [stderr])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --monitor --unixctl="`pwd`"/nonexistent/unixctl db], [1], [], [stderr])
AT_CHECK([grep 'ovsdb-server: could not initialize control socket' stderr],
[0], [ignore], [])
AT_CHECK([test ! -s pid])
AT_CLEANUP
-
-# xxx This test hangs frequently, which often prevents builds from
-# xxx completing. Skip this test until we have time to debug it.
-AT_SETUP([daemon --detach closes standard fds])
-AT_SKIP_IF([:])
-AT_CAPTURE_FILE([pid])
-AT_CAPTURE_FILE([status])
-AT_CAPTURE_FILE([stderr])
-OVSDB_INIT([db])
-AT_CHECK([(yes 2>stderr; echo $? > status) | ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl db], [0], [], [])
-AT_CHECK([kill `cat pid`])
-AT_CHECK([test -s status])
-if grep '[[bB]]roken pipe' stderr >/dev/null 2>&1; then
- # Something in the environment caused SIGPIPE to be ignored, but
- # 'yes' at least told us that it got EPIPE. Good enough; we know
- # that stdout was closed.
- :
-else
- # Otherwise make sure that 'yes' died from SIGPIPE.
- AT_CHECK([kill -l `cat status`], [0], [PIPE
-])
-fi
-AT_CLEANUP
-
-# xxx This test hangs frequently, which often prevents builds from
-# xxx completing. Skip this test until we have time to debug it.
-AT_SETUP([daemon --detach --monitor closes standard fds])
-AT_SKIP_IF([:])
-AT_CAPTURE_FILE([pid])
-AT_CAPTURE_FILE([status])
-AT_CAPTURE_FILE([stderr])
-OVSDB_INIT([db])
-AT_CHECK([(yes 2>stderr; echo $? > status) | ovsdb-server --detach --monitor --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl db], [0], [], [])
-AT_CHECK([kill `cat pid`])
-AT_CHECK([test -s status])
-if grep '[[bB]]roken pipe' stderr >/dev/null 2>&1; then
- # Something in the environment caused SIGPIPE to be ignored, but
- # 'yes' at least told us that it got EPIPE. Good enough; we know
- # that stdout was closed.
- :
-else
- # Otherwise make sure that 'yes' died from SIGPIPE.
- AT_CHECK([kill -l `cat status`], [0], [PIPE
-])
-fi
-AT_CLEANUP
CHECK_FILE_NAME([dir/file/], [dir], [file])
CHECK_FILE_NAME([dir/file//], [dir], [file])
CHECK_FILE_NAME([///foo], [/], [foo])
+
+AT_BANNER([test follow_symlinks function])
+
+m4_define([CHECK_FOLLOW],
+ [echo "check $1 -> $2"
+ AT_CHECK_UNQUOTED([test-util follow-symlinks "$1"], [0], [$2
+])
+ echo])
+
+AT_SETUP([follow_symlinks - relative symlinks])
+: > target
+ln -s target source
+AT_SKIP_IF([test ! -h source])
+CHECK_FOLLOW([source], [target])
+
+mkdir dir
+ln -s target2 dir/source2
+CHECK_FOLLOW([dir/source2], [dir/target2])
+
+mkdir dir/dir2
+ln -s dir/b a
+ln -s c dir/b
+ln -s dir2/d dir/c
+CHECK_FOLLOW([a], [dir/dir2/d])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - absolute symlinks])
+: > target
+ln -s "`pwd`/target" source
+AT_SKIP_IF([test ! -h source])
+CHECK_FOLLOW([source], [`pwd`/target])
+
+mkdir dir
+ln -s "`pwd`/dir/target2" dir/source2
+CHECK_FOLLOW([dir/source2], [`pwd`/dir/target2])
+
+mkdir dir/dir2
+ln -s "`pwd`/dir/b" a
+ln -s "`pwd`/dir/c" dir/b
+ln -s "`pwd`/dir/dir2/d" dir/c
+CHECK_FOLLOW([a], [`pwd`/dir/dir2/d])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - symlinks to directories])
+mkdir target
+ln -s target source
+AT_SKIP_IF([test ! -h source])
+ln -s target/ source2
+CHECK_FOLLOW([source], [target])
+CHECK_FOLLOW([source2], [target/])
+
+# follow_symlinks() doesn't expand symlinks in the middle of a name.
+: > source/x
+CHECK_FOLLOW([source/x], [source/x])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - nonexistent targets])
+ln -s target source
+AT_SKIP_IF([test ! -h source])
+CHECK_FOLLOW([source], [target])
+CHECK_FOLLOW([target], [target])
+CHECK_FOLLOW([target], [target])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - regular files])
+touch x
+CHECK_FOLLOW([x], [x])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - device targets])
+AT_SKIP_IF([test ! -e /dev/null])
+AT_SKIP_IF([test ! -e /dev/full])
+ln -s /dev/null x
+ln -s /dev/full y
+CHECK_FOLLOW([x], [/dev/null])
+CHECK_FOLLOW([y], [/dev/full])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - nonexistent files])
+CHECK_FOLLOW([nonexistent], [nonexistent])
+CHECK_FOLLOW([a/b/c], [a/b/c])
+CHECK_FOLLOW([/a/b/c], [/a/b/c])
+CHECK_FOLLOW([//a/b/c], [//a/b/c])
+AT_CLEANUP
+
+AT_SETUP([follow_symlinks - symlink loop])
+ln -s a b
+AT_SKIP_IF([test ! -h b])
+ln -s b a
+AT_SKIP_IF([test ! -h a])
+
+AT_CHECK([test-util follow-symlinks a], [0], [a
+], [stderr])
+AT_CHECK([sed 's/^[[^|]]*|//' stderr], [0],
+ [00001|util|WARN|a: too many levels of symlinks
+])
+AT_CLEANUP
#! /usr/bin/perl
-# Copyright (c) 2009, 2010, 2011 Nicira Networks.
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# Compose packet.
my $packet = '';
+ my $wildcards = 0;
$packet .= pack_ethaddr($flow{DL_DST});
$packet .= pack_ethaddr($flow{DL_SRC});
if ($flow{DL_VLAN} != 0xffff) {
$packet .= pack('nn', 0x8100, $flow{DL_VLAN});
+ } else {
+ $wildcards |= 1 << 20; # OFPFW10_DL_VLAN_PCP
}
my $len_ofs = length($packet);
$packet .= pack('n', 0) if $attrs{DL_HEADER} =~ /^802.2/;
print "\n";
print FLOWS pack('Nn',
- 0, # wildcards
- 1); # in_port
+ $wildcards, # wildcards
+ 1); # in_port
print FLOWS pack_ethaddr($flow{DL_SRC});
print FLOWS pack_ethaddr($flow{DL_DST});
print FLOWS pack('nCxnCCxxNNnn',
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
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
AT_BANNER([JSON-RPC - C])
AT_SETUP([JSON-RPC request and successful reply])
-AT_CHECK([test-jsonrpc --detach --pidfile="`pwd`"/pid listen punix:socket])
+AT_CHECK([test-jsonrpc --detach --no-chdir --pidfile="`pwd`"/pid listen punix:socket])
AT_CHECK([test -s pid])
AT_CHECK([kill -0 `cat pid`])
AT_CHECK(
AT_CLEANUP
AT_SETUP([JSON-RPC request and error reply])
-AT_CHECK([test-jsonrpc --detach --pidfile="`pwd`"/pid listen punix:socket])
+AT_CHECK([test-jsonrpc --detach --no-chdir --pidfile="`pwd`"/pid listen punix:socket])
AT_CHECK([test -s pid])
AT_CHECK([kill -0 `cat pid`])
AT_CHECK(
AT_CLEANUP
AT_SETUP([JSON-RPC notification])
-AT_CHECK([test-jsonrpc --detach --pidfile="`pwd`"/pid listen punix:socket])
+AT_CHECK([test-jsonrpc --detach --no-chdir --pidfile="`pwd`"/pid listen punix:socket])
AT_CHECK([test -s pid])
# When a daemon dies it deletes its pidfile, so make a copy.
AT_CHECK([cp pid pid2])
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
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
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
+++ /dev/null
-#! /bin/sh
-
-abs_top_builddir='@abs_top_builddir@'
-wrap_program=`basename '@wrap_program@'`
-
-# Strip the first directory from $PATH that contains $wrap_program,
-# so that below we run the real $wrap_program, not ourselves.
-not_found=true
-new_path=
-first=true
-save_IFS=$IFS
-IFS=:
-for dir in $PATH; do
- IFS=$save_IFS
- if $not_found && test -x "$dir/$wrap_program"; then
- not_found=false
- else
- if $first; then
- first=false
- new_path=$dir
- else
- new_path=$new_path:$dir
- fi
- fi
-done
-IFS=$save_IFS
-if $not_found; then
- echo "$0: error: cannot find $wrap_program in \$PATH" >&2
- exit 1
-fi
-PATH=$new_path
-export PATH
-
-if test "$DISABLE_LCOV" = true; then
- exec $wrap_program "$@"
- exit 1
-fi
-
-# XXX Probably want some kind of synchronization here to deal with
-# programs running in parallel.
-LCOV="lcov -b $abs_top_builddir -d $abs_top_builddir -q"
-$LCOV -z
-
-# Run the subprocess and propagate signals to it.
-for signal in 1 2 3 5 15; do
- trap "kill -$signal \$! # Propagate signal
- trap - $signal # Reset signal to default
- wait # Wait for child to die
- kill -$signal $$ # Kill ourselves with same signal
- exit 1 # Exit in case 'kill' failed" $signal
-done
-$wrap_program 0<&0 "$@" & # 0<&0 prevents shell from closing stdin
-exec 0</dev/null # Don't hold stdin open unnecessarily
-wait $!; rc=$?
-
-# Run lcov, but only if some .gcda files were produced, since lcov
-# complains otherwise.
-for file in `find "$abs_top_builddir" -name '*.gcda'`; do
- $LCOV -c -o - >> "$abs_top_builddir/tests/coverage.info"
- break
-done
-
-exit $rc
[[usable protocols: any
chosen protocol: OpenFlow10-table_id
OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1)
-OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0x000a->NXM_NX_REG0[5..10])
+OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0xa->NXM_NX_REG0[5..10])
OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
]])
AT_CLEANUP
AT_DATA([flows.txt],
[[actions=learn(eth_type=0x800,load:5->NXM_OF_IP_DST[])
ip,actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[])
-ip,actions=learn(eth_type=0x800,NXM_OF_IP_DST[])
+ip,actions=learn(eth_type=0x800,OXM_OF_IPV4_DST[])
]])
AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
[[usable protocols: any
chosen protocol: OpenFlow10-table_id
-OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x00000005->NXM_OF_IP_DST[])
+OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x5->NXM_OF_IP_DST[])
OFPT_FLOW_MOD (xid=0x2): ADD ip actions=learn(table=1,load:NXM_OF_IP_DST[]->NXM_NX_REG1[])
OFPT_FLOW_MOD (xid=0x3): ADD ip actions=learn(table=1,eth_type=0x800,NXM_OF_IP_DST[])
]])
# Check for the MAC learning entry.
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
table=1, priority=0 actions=FLOOD
NXST_FLOW reply:
])
# Check for both MAC learning entries.
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip |sort], [0], [dnl
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:3
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
table=1, priority=0 actions=FLOOD
NXST_FLOW reply:
])
# Check that the MAC learning entry was updated.
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:2
- table=1, hard_timeout=60,vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:05 actions=output:2
+ table=1, hard_timeout=60, vlan_tci=0x0000/0x0fff,dl_dst=50:54:00:00:00:06 actions=output:1
table=1, priority=0 actions=FLOOD
NXST_FLOW reply:
])
# Check for the learning entry.
AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
- table=1, hard_timeout=60,tcp,nw_src=192.168.0.1,nw_dst=192.168.0.2,tp_src=80,tp_dst=40000 actions=drop
+ table=1, hard_timeout=60, tcp,nw_src=192.168.0.1,nw_dst=192.168.0.2,tp_src=80,tp_dst=40000 actions=drop
NXST_FLOW reply:
])
OVS_VSWITCHD_STOP
# Check for the learning entry.
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- table=1, hard_timeout=60,tcp6,ipv6_src=fec0::1,ipv6_dst=2001:db8:85a3::8a2e:370:7334,tp_src=80,tp_dst=40000 actions=load:0x13198a2e03707348->NXM_NX_IPV6_DST[[0..63]],load:0x20010db885a308d3->NXM_NX_IPV6_DST[[64..127]]
+ table=1, hard_timeout=60, tcp6,ipv6_src=fec0::1,ipv6_dst=2001:db8:85a3::8a2e:370:7334,tp_src=80,tp_dst=40000 actions=load:0x13198a2e03707348->NXM_NX_IPV6_DST[[0..63]],load:0x20010db885a308d3->NXM_NX_IPV6_DST[[64..127]]
tcp6 actions=learn(table=1,hard_timeout=60,eth_type=0x86dd,nw_proto=6,NXM_NX_IPV6_SRC[[]]=NXM_NX_IPV6_DST[[]],ipv6_dst=2001:db8:85a3::8a2e:370:7334,NXM_OF_TCP_SRC[[]]=NXM_OF_TCP_DST[[]],NXM_OF_TCP_DST[[]]=NXM_OF_TCP_SRC[[]],load:0x20010db885a308d313198a2e03707348->NXM_NX_IPV6_DST[[]]),FLOOD
NXST_FLOW reply:
])
])
AT_CLEANUP
-AT_SETUP([test utility functions])
-AT_KEYWORDS([util])
-AT_CHECK([test-util])
-AT_CLEANUP
+m4_foreach(
+ [testname],
+ [[ctz],
+ [log_2_floor],
+ [bitwise_copy],
+ [bitwise_zero],
+ [bitwise_one],
+ [bitwise_is_all_zeros]],
+ [AT_SETUP([testname[()] function])
+ AT_CHECK([test-util testname], [0], [], [])
+ AT_CLEANUP])
AT_SETUP([test unix socket -- short pathname])
AT_CHECK([test-unix-socket x])
CHECK_LOCKFILE([lock_blocks_other_process], [1])
CHECK_LOCKFILE([lock_twice_blocks_other_process], [1])
CHECK_LOCKFILE([lock_and_unlock_allows_other_process], [1])
-CHECK_LOCKFILE([lock_timeout_gets_the_lock], [1])
-CHECK_LOCKFILE([lock_timeout_runs_out], [1])
CHECK_LOCKFILE([lock_multiple], [0])
+CHECK_LOCKFILE([lock_symlink], [0])
+CHECK_LOCKFILE([lock_symlink_to_dir], [0])
AT_BANNER([datapath parsing and formatting])
-AT_SETUP([OVS datapath parsing and formatting - valid forms])
+AT_SETUP([OVS datapath key parsing and formatting - valid forms])
+dnl We could add a test for invalid forms, but that's less important.
AT_DATA([odp-base.txt], [dnl
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15)
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234)
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
echo '# Valid forms with IP later fragment.'
sed -n 's/,frag=no),.*/,frag=later)/p' odp-base.txt) > odp.txt
AT_CAPTURE_FILE([odp.txt])
-AT_CHECK_UNQUOTED([test-odp < odp.txt], [0], [`cat odp.txt`
+AT_CHECK_UNQUOTED([test-odp parse-keys < odp.txt], [0], [`cat odp.txt`
])
AT_CLEANUP
-dnl We could add a test for invalid forms, but that's less important.
+AT_SETUP([OVS datapath actions parsing and formatting - valid forms])
+AT_DATA([actions.txt], [dnl
+1,2,3
+userspace(pid=555666777)
+userspace(pid=6633,sFlow(vid=9,pcp=7,output=10))
+userspace(pid=9765,slow_path())
+userspace(pid=9765,slow_path(cfm))
+userspace(pid=9765,slow_path(cfm,match))
+userspace(pid=9123,userdata=0x815309)
+set(tun_id(0x7f10354))
+set(in_port(2))
+set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))
+set(eth_type(0x1234))
+set(ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=no))
+set(tcp(src=80,dst=8080))
+set(udp(src=81,dst=6632))
+set(icmp(type=1,code=2))
+set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no))
+set(icmpv6(type=1,code=2))
+push_vlan(vid=12,pcp=0)
+push_vlan(vid=13,pcp=5,cfi=0)
+push_vlan(tpid=0x9100,vid=13,pcp=5)
+push_vlan(tpid=0x9100,vid=13,pcp=5,cfi=0)
+pop_vlan
+sample(sample=9.7%,actions(1,2,3,push_vlan(vid=1,pcp=2)))
+])
+AT_CHECK_UNQUOTED([test-odp parse-actions < actions.txt], [0],
+ [`cat actions.txt`
+])
+AT_CLEANUP
--- /dev/null
+AT_BANNER([OpenFlow actions])
+
+AT_SETUP([OpenFlow 1.0 action translation])
+AT_KEYWORDS([OF1.0])
+AT_DATA([test-data], [dnl
+# actions=LOCAL
+0000 0008 fffe 04d2
+
+# actions=CONTROLLER:1234
+0000 0008 fffd 04d2
+
+# actions=mod_vlan_vid:9
+0001 0008 0009 0000
+
+# actions=mod_vlan_pcp:6
+0002 0008 06 000000
+
+# actions=strip_vlan
+0003 0008 00000000
+
+# actions=mod_dl_src:00:11:22:33:44:55
+0004 0010 001122334455 000000000000
+
+# actions=mod_dl_dst:10:20:30:40:50:60
+0005 0010 102030405060 000000000000
+
+# actions=mod_nw_src:1.2.3.4
+0006 0008 01020304
+
+# actions=mod_nw_dst:192.168.0.1
+0007 0008 c0a80001
+
+# actions=mod_nw_tos:48
+0008 0008 30 000000
+
+# actions=mod_tp_src:80
+0009 0008 0050 0000
+
+# actions=mod_tp_dst:443
+000a 0008 01bb 0000
+
+# actions=enqueue:10q55
+000b 0010 000a 000000000000 00000037
+
+# actions=resubmit:5
+ffff 0010 00002320 0001 0005 00000000
+
+# actions=set_tunnel:0x12345678
+ffff 0010 00002320 0002 0000 12345678
+
+# actions=set_queue:2309737729
+ffff 0010 00002320 0004 0000 89abcd01
+
+# actions=pop_queue
+ffff 0010 00002320 0005 000000000000
+
+# actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0006 0010 0000 0000 00000002 00000802
+
+# actions=load:0xf009->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0007 000f 00000802 000000000000f009
+
+# actions=note:11.e9.9a.ad.67.f3
+ffff 0010 00002320 0008 11e99aad67f3
+
+# actions=set_tunnel64:0xc426384d49c53d60
+ffff 0018 00002320 0009 000000000000 c426384d49c53d60
+
+# actions=set_tunnel64:0x885f3298
+ffff 0018 00002320 0009 000000000000 00000000885f3298
+
+# actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[])
+ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004
+
+# actions=autopath(2,NXM_NX_REG0[2..30])
+& autopath|WARN|The autopath action is deprecated and may be removed in February 2013. Please email dev@openvswitch.org with concerns.
+ffff 0018 00002320 000b 009c 00010004 00000002 00000000
+
+# actions=bundle(eth_src,0,hrw,ofport,slaves:4,8)
+ffff 0028 00002320 000c 0001 0000 0000 00000002 0002 0000 00000000 00000000 dnl
+0004 0008 00000000
+
+# actions=bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[],slaves:4,8)
+ffff 0028 00002320 000d 0001 0000 0000 00000002 0002 001f 00010004 00000000 dnl
+0004 0008 00000000
+
+# actions=resubmit(10,5)
+ffff 0010 00002320 000e 000a 05 000000
+
+# actions=output:NXM_NX_REG1[5..10]
+ffff 0018 00002320 000f 0145 00010204 ffff 000000000000
+
+# actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
+ffff 0048 00002320 0010 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 dnl
+000c 00000802 0000 00000802 0000 dnl
+0030 00000406 0000 00000206 0000 dnl
+1010 00000002 0000 dnl
+00000000
+
+# actions=exit
+ffff 0010 00002320 0011 000000000000
+
+# actions=dec_ttl
+ffff 0010 00002320 0012 000000000000
+
+# actions=fin_timeout(idle_timeout=10,hard_timeout=20)
+ffff 0010 00002320 0013 000a 0014 0000
+
+# actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
+ffff 0010 00002320 0014 04d2 162e 02 00
+
+# actions=dec_ttl(32768,12345,90,765,1024)
+ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+ [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp10-actions < input.txt],
+ [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([OpenFlow 1.1 action translation])
+AT_KEYWORDS([OF1.1])
+AT_DATA([test-data], [dnl
+# actions=LOCAL
+0000 0010 fffffffe 04d2 000000000000
+
+# actions=CONTROLLER:1234
+0000 0010 fffffffd 04d2 000000000000
+
+# actions=mod_vlan_vid:9
+0001 0008 0009 0000
+
+# actions=mod_vlan_pcp:6
+0002 0008 06 000000
+
+# actions=mod_dl_src:00:11:22:33:44:55
+0003 0010 001122334455 000000000000
+
+# actions=mod_dl_dst:10:20:30:40:50:60
+0004 0010 102030405060 000000000000
+
+# actions=mod_nw_src:1.2.3.4
+0005 0008 01020304
+
+# actions=mod_nw_dst:192.168.0.1
+0006 0008 c0a80001
+
+# actions=mod_nw_tos:48
+0007 0008 30 000000
+
+# actions=mod_tp_src:80
+0009 0008 0050 0000
+
+# actions=mod_tp_dst:443
+000a 0008 01bb 0000
+
+# actions=resubmit:5
+ffff 0010 00002320 0001 0005 00000000
+
+# actions=set_tunnel:0x12345678
+ffff 0010 00002320 0002 0000 12345678
+
+# actions=set_queue:2309737729
+ffff 0010 00002320 0004 0000 89abcd01
+
+# actions=pop_queue
+ffff 0010 00002320 0005 000000000000
+
+# actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0006 0010 0000 0000 00000002 00000802
+
+# actions=load:0xf009->NXM_OF_VLAN_TCI[]
+ffff 0018 00002320 0007 000f 00000802 000000000000f009
+
+# actions=note:11.e9.9a.ad.67.f3
+ffff 0010 00002320 0008 11e99aad67f3
+
+# actions=set_tunnel64:0xc426384d49c53d60
+ffff 0018 00002320 0009 000000000000 c426384d49c53d60
+
+# actions=set_tunnel64:0x885f3298
+ffff 0018 00002320 0009 000000000000 00000000885f3298
+
+# actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[])
+ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004
+
+# actions=autopath(2,NXM_NX_REG0[2..30])
+& autopath|WARN|The autopath action is deprecated and may be removed in February 2013. Please email dev@openvswitch.org with concerns.
+ffff 0018 00002320 000b 009c 00010004 00000002 00000000
+
+# actions=bundle(eth_src,0,hrw,ofport,slaves:4,8)
+ffff 0028 00002320 000c 0001 0000 0000 00000002 0002 0000 00000000 00000000 dnl
+0004 0008 00000000
+
+# actions=bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[],slaves:4,8)
+ffff 0028 00002320 000d 0001 0000 0000 00000002 0002 001f 00010004 00000000 dnl
+0004 0008 00000000
+
+# actions=resubmit(10,5)
+ffff 0010 00002320 000e 000a 05 000000
+
+# actions=output:NXM_NX_REG1[5..10]
+ffff 0018 00002320 000f 0145 00010204 ffff 000000000000
+
+# actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
+ffff 0048 00002320 0010 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 dnl
+000c 00000802 0000 00000802 0000 dnl
+0030 00000406 0000 00000206 0000 dnl
+1010 00000002 0000 dnl
+00000000
+
+# actions=exit
+ffff 0010 00002320 0011 000000000000
+
+# actions=dec_ttl
+ffff 0010 00002320 0012 000000000000
+
+# actions=fin_timeout(idle_timeout=10,hard_timeout=20)
+ffff 0010 00002320 0013 000a 0014 0000
+
+# actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
+ffff 0010 00002320 0014 04d2 162e 02 00
+
+# actions=dec_ttl(32768,12345,90,765,1024)
+ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+ [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp11-actions < input.txt],
+ [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([OpenFlow 1.1 instruction translation])
+AT_KEYWORDS([OF1.1])
+AT_DATA([test-data], [dnl
+# actions=LOCAL
+0004 0018 00000000 dnl
+0000 0010 fffffffe 04d2 000000000000
+
+dnl Check that an empty Apply-Actions instruction gets dropped.
+# actions=drop
+# 0: 00 -> (none)
+# 1: 04 -> (none)
+# 2: 00 -> (none)
+# 3: 08 -> (none)
+# 4: 00 -> (none)
+# 5: 00 -> (none)
+# 6: 00 -> (none)
+# 7: 00 -> (none)
+0004 0008 00000000
+
+# bad OF1.1 instructions: NXBIC_DUP_TYPE
+0004 0008 00000000 0004 0008 00000000
+
+dnl Instructions not multiple of 8 in length.
+& ofp_actions|WARN|OpenFlow message instructions length 9 is not a multiple of 8
+# bad OF1.1 instructions: OFPBIC_BAD_LEN
+0004 0009 01 00000000
+
+dnl Goto-Table instruction too long.
+# bad OF1.1 instructions: OFPBIC_BAD_LEN
+0001 0010 01 000000 0000000000000000
+
+dnl Goto-Table not supported yet.
+# bad OF1.1 instructions: OFPBIC_UNSUP_INST
+0001 0008 01 000000
+
+dnl Write-Metadata not supported yet.
+# bad OF1.1 instructions: OFPBIC_UNSUP_INST
+0002 0018 00000000 fedcba9876543210 ffffffffffffffff
+
+dnl Write-Metadata too short.
+# bad OF1.1 instructions: OFPBIC_BAD_LEN
+0002 0010 00000000 fedcba9876543210
+
+dnl Write-Metadata too long.
+# bad OF1.1 instructions: OFPBIC_BAD_LEN
+0002 0020 00000000 fedcba9876543210 ffffffffffffffff 0000000000000000
+
+dnl Write-Actions not supported yet.
+# bad OF1.1 instructions: OFPBIC_UNSUP_INST
+0003 0008 01 000000
+
+dnl Clear-Actions not supported yet.
+# bad OF1.1 instructions: OFPBIC_UNSUP_INST
+0005 0008 01 000000
+
+dnl Experimenter actions not supported yet.
+# bad OF1.1 instructions: OFPBIC_BAD_EXPERIMENTER
+ffff 0008 01 000000
+
+dnl Bad instruction number (0 not assigned).
+# bad OF1.1 instructions: OFPBIC_UNKNOWN_INST
+0000 0008 01 000000
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+ [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp11-instructions < input.txt],
+ [0], [expout], [experr])
+AT_CLEANUP
AT_SETUP([OFPT_ERROR with type OFPET_BAD_REQUEST - OF1.0])
AT_KEYWORDS([ofp-print ofp-errors])
-AT_CHECK([ovs-ofctl ofp-print 01010014000000000001000601bbccddeeff0011], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 0101001400000000000100060105ccddeeff0011], [0], [dnl
OFPT_ERROR (xid=0x0): OFPBRC_BAD_LEN
+OFPT_FEATURES_REQUEST (xid=0xeeff0011):
(***truncated to 8 bytes from 52445***)
-00000000 01 bb cc dd ee ff 00 11- |........ |
+00000000 01 05 cc dd ee ff 00 11- |........ |
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_ERROR prints type of truncated inner messages])
+AT_KEYWORDS([ofp-print ofp-errors])
+AT_CHECK([ovs-ofctl ofp-print "0101004c092529d500010006 \
+01 06 00 e0 00 00 00 01 00 00 50 54 00 00 00 01 \
+00 00 01 00 02 00 00 00 00 00 00 87 00 00 0f ff \
+ff fe 50 54 00 00 00 01 62 72 30 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01"], [0], [dnl
+OFPT_ERROR (xid=0x92529d5): OFPBRC_BAD_LEN
+OFPT_FEATURES_REPLY (xid=0x1):
+(***truncated to 64 bytes from 224***)
+00000000 01 06 00 e0 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....|
+00000010 00 00 01 00 02 00 00 00-00 00 00 87 00 00 0f ff |................|
+00000020 ff fe 50 54 00 00 00 01-62 72 30 00 00 00 00 00 |..PT....br0.....|
+00000030 00 00 00 00 00 00 00 00-00 00 00 01 00 00 00 01 |................|
])
AT_CLEANUP
dnl and OFPBIC_UNSUP_INST in OF1.1, so check that value in both versions.
AT_SETUP([OFPT_ERROR with type OFPFMFC_OVERLAP - OF1.0])
AT_KEYWORDS([ofp-print ofp-errors])
-AT_CHECK([ovs-ofctl ofp-print 01010014000000000003000101bbccddeeff0011], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 0101001400000000000300010106ccddeeff0011], [0], [dnl
OFPT_ERROR (xid=0x0): OFPFMFC_OVERLAP
+OFPT_FEATURES_REPLY (xid=0xeeff0011):
(***truncated to 8 bytes from 52445***)
-00000000 01 bb cc dd ee ff 00 11- |........ |
+00000000 01 06 cc dd ee ff 00 11- |........ |
])
AT_CLEANUP
AT_SETUP([OFPT_ERROR with type OFPBIC_UNSUP_INST - OF1.1])
AT_KEYWORDS([ofp-print ofp-errors])
-AT_CHECK([ovs-ofctl ofp-print 02010014000000000003000102bbccddeeff0011], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 0201001400000000000300010206ccddeeff0011], [0], [dnl
OFPT_ERROR (OF1.1) (xid=0x0): OFPBIC_UNSUP_INST
+OFPT_FEATURES_REPLY (OF1.1) (xid=0xeeff0011):
(***truncated to 8 bytes from 52445***)
-00000000 02 bb cc dd ee ff 00 11- |........ |
+00000000 02 06 cc dd ee ff 00 11- |........ |
])
AT_CLEANUP
])
AT_KEYWORDS([ofp-print ofp-errors])
AT_CHECK([ovs-ofctl ofp-print '0301001455555555 00030005 0102000811111111'], [0], [dnl
-OFPT_ERROR (OF 0x03) (xid=0x55555555): OFPBIC_BAD_EXPERIMENTER
+OFPT_ERROR (OF1.2) (xid=0x55555555): OFPBIC_BAD_EXPERIMENTER
OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
])
AT_KEYWORDS([ofp-print ofp-errors])
AT_CHECK([ovs-ofctl ofp-print '0301001455555555 00030006 0102000811111111'], [0], [dnl
-OFPT_ERROR (OF 0x03) (xid=0x55555555): OFPBIC_BAD_EXP_TYPE
+OFPT_ERROR (OF1.2) (xid=0x55555555): OFPBIC_BAD_EXP_TYPE
OFPT_ECHO_REQUEST (xid=0x11111111): 0 bytes of payload
])
AT_CLEANUP
[0], [dnl
***decode error: OFPBRC_BAD_TYPE***
00000000 00 bb 00 08 ee ff 00 11- |........ |
-], [ofp_util|WARN|received OpenFlow message of unknown type 187
+], [ofp_msgs|WARN|unknown OpenFlow message (version 0, type 187)
])
AT_CLEANUP
AT_SETUP([truncated message])
AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print 01bbccddeeff0011], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 0110ccddeeff0011], [0], [dnl
(***truncated to 8 bytes from 52445***)
-00000000 01 bb cc dd ee ff 00 11- |........ |
+00000000 01 10 cc dd ee ff 00 11- |........ |
])
AT_CLEANUP
AT_SETUP([message only uses part of buffer])
AT_KEYWORDS([ofp-print])
-AT_CHECK([ovs-ofctl ofp-print 01bb0009eeff00112233], [0], [dnl
+AT_CHECK([ovs-ofctl ofp-print 01100009eeff00112233], [0], [dnl
(***only uses 9 bytes out of 10***)
-00000000 01 bb 00 09 ee ff 00 11-22 33 |........"3 |
+00000000 01 10 00 09 ee ff 00 11-22 33 |........"3 |
])
# "
AT_CLEANUP
OFPT_FEATURES_REPLY (OF1.1) (xid=0x1): dpid:0000505400000001
n_tables:2, n_buffers:256
capabilities: FLOW_STATS TABLE_STATS PORT_STATS ARP_MATCH_IP
-actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP SET_DL_SRC SET_DL_DST SET_NW_SRC SET_NW_DST SET_NW_ECN SET_NW_TOS SET_TP_SRC SET_TP_DST COPY_TTL_OUT COPY_TTL_IN SET_MPLS_LABEL SET_MPLS_TC SET_MPLS_TTL
3(eth0): addr:50:54:00:00:00:01
config: 0
state: 0
])
AT_CLEANUP
+AT_SETUP([OFPT_FEATURES_REPLY - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \
+00 00 01 00 ff 00 00 00 00 00 01 77 00 00 06 ff \
+ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
+62 72 30 0a 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 01 86 a0 00 01 86 a0 \
+00 00 00 03 00 00 00 00 50 54 00 00 00 01 00 00 \
+65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 20 08 00 00 28 0f \
+00 00 28 0f 00 00 00 00 00 01 86 a0 00 01 86 a0 \
+"], [0], [dnl
+OFPT_FEATURES_REPLY (OF1.2) (xid=0x1): dpid:0000505400000001
+n_tables:255, n_buffers:256
+capabilities: FLOW_STATS TABLE_STATS PORT_STATS IP_REASM QUEUE_STATS PORT_BLOCKED
+ 3(eth0): addr:50:54:00:00:00:01
+ config: 0
+ state: 0
+ current: 100MB-FD AUTO_NEG
+ advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+ supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+ speed: 100 Mbps now, 100 Mbps max
+ LOCAL(br0): addr:50:54:00:00:00:01
+ config: PORT_DOWN
+ state: LINK_DOWN
+ speed: 100 Mbps now, 100 Mbps max
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_FEATURES_REPLY cut off mid-port - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 06 00 a0 00 00 00 01 00 00 50 54 00 00 00 01 \
+00 00 01 00 ff 00 00 00 00 00 01 77 00 00 06 ff \
+ff ff ff fe 00 00 00 00 50 54 00 00 00 01 00 00 \
+62 72 30 0a 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 01 86 a0 00 01 86 a0 \
+00 00 00 03 00 00 00 00 50 54 00 00 00 01 00 00 \
+65 74 68 30 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 20 08 00 00 28 0f \
+"], [0], [dnl
+OFPT_FEATURES_REPLY (OF1.2) (xid=0x1):
+(***truncated to 144 bytes from 160***)
+00000000 03 06 00 a0 00 00 00 01-00 00 50 54 00 00 00 01 |..........PT....|
+00000010 00 00 01 00 ff 00 00 00-00 00 01 77 00 00 06 ff |...........w....|
+00000020 ff ff ff fe 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......|
+00000030 62 72 30 0a 00 00 00 00-00 00 00 00 00 00 00 00 |br0.............|
+00000040 00 00 00 01 00 00 00 01-00 00 00 00 00 00 00 00 |................|
+00000050 00 00 00 00 00 00 00 00-00 01 86 a0 00 01 86 a0 |................|
+00000060 00 00 00 03 00 00 00 00-50 54 00 00 00 01 00 00 |........PT......|
+00000070 65 74 68 30 00 00 00 00-00 00 00 00 00 00 00 00 |eth0............|
+00000080 00 00 00 00 00 00 00 00-00 00 20 08 00 00 28 0f |.......... ...@{:@.|
+], [stderr])
+AT_CHECK([sed 's/.*|//' stderr], [0], [dnl
+])
+AT_CLEANUP
+
AT_SETUP([OFPT_GET_CONFIG_REQUEST])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print '0107000800000001'], [0], [dnl
])
AT_CLEANUP
-AT_SETUP([OFPT_PACKET_IN])
+AT_SETUP([OFPT_PACKET_IN - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
01 0a 00 4e 00 00 00 00 00 00 01 11 00 3c 00 03 \
50 00 02 00 26 e8 00 00 00 00 00 00 00 00 \
"], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:06) type:0800 proto:6 tos:0 ttl:64 ip(192.168.0.1->192.168.0.2) port(10031->0) tcp_csum:26e8
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:06) type:0800 proto:6 tos:0 ttl:64 ip(192.168.0.1->192.168.0.2) port(10031->0) tcp_csum:26e8
])
AT_CLEANUP
-AT_SETUP([OFPT_FLOW_REMOVED])
+AT_SETUP([OFPT_PACKET_IN - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 0a 00 4c 00 00 00 00 ff ff ff 00 00 2a 00 00 \
+00 01 00 0c 80 00 00 04 ff ff ff fe 00 00 00 00 \
+00 00 ff ff ff ff ff ff 00 23 20 83 c1 5f 80 35 \
+00 01 08 00 06 04 00 03 00 23 20 83 c1 5f 00 00 \
+00 00 00 23 20 83 c1 5f 00 00 00 00 \
+"], [0], [dnl
+OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:23:20:83:c1:5f->ff:ff:ff:ff:ff:ff) type:8035 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_FLOW_REMOVED - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
01 0b 00 58 00 00 00 00 00 00 00 00 00 03 50 54 \
])
AT_CLEANUP
+AT_SETUP([OFPT_FLOW_REMOVED - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 0b 00 40 00 00 00 00 fe dc ba 98 76 54 32 10 \
+80 00 01 05 00 00 00 01 00 98 96 80 00 3c 00 78 \
+00 00 00 00 00 12 d6 87 00 00 00 00 6f 68 ba 66 \
+00 01 00 0a 80 00 0c 02 10 09 00 00 00 00 00 00"], [0], [dnl
+OFPT_FLOW_REMOVED (OF1.2) (xid=0x0): dl_vlan=9 reason=hard cookie:0xfedcba9876543210 duration1.01s idle60 hard120 pkts1234567 bytes1869134438
+])
+AT_CLEANUP
+
AT_SETUP([OFPT_PORT_STATUS - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
])
AT_CLEANUP
-AT_SETUP([OFPT_PACKET_OUT])
+AT_SETUP([OFPT_PACKET_OUT - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
01 0d 00 54 00 00 00 00 00 00 01 14 00 01 00 08 \
])
AT_CLEANUP
+AT_SETUP([OFPT_PACKET_OUT - OF1.1])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 0d 00 28 88 58 df c5 ff ff ff 00 ff ff ff fe \
+00 10 00 00 00 00 00 00 00 00 00 10 ff ff ff fb \
+05 dc 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPT_PACKET_OUT (OF1.2) (xid=0x8858dfc5): in_port=LOCAL actions=FLOOD buffer=0xffffff00
+])
+AT_CLEANUP
+
# The flow is formatted with cls_rule_format() for the low-verbosity case.
-AT_SETUP([OFPT_FLOW_MOD - low verbosity])
+AT_SETUP([OFPT_FLOW_MOD - OF1.0 - low verbosity])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
01 0e 00 50 00 00 00 00 00 00 00 00 00 01 50 54 \
00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 \
00 00 01 0e 00 00 00 00 00 00 00 08 00 03 00 00 \
" 2], [0], [dnl
-OFPT_FLOW_MOD (xid=0x0): ADD priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2 idle:5 buf:0x10e actions=output:3
+OFPT_FLOW_MOD (xid=0x0): ADD priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2 idle:5 buf:0x10e out_port:0 actions=output:3
], [dnl
ofp_util|INFO|normalization changed ofp_match, details:
ofp_util|INFO| pre: priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0
])
AT_CLEANUP
-# The flow is formatted with ofp_match_to_string() for the high-verbosity case.
-AT_SETUP([OFPT_FLOW_MOD - high verbosity])
+# The flow is formatted with cls_rule_format() for the low-verbosity case.
+AT_SETUP([OFPT_FLOW_MOD - OF1.2 - low verbosity])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 ff 00 00 00 00 00 ff ff \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \
+50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \
+80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \
+00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \
+00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:255 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2 actions=output:3
+], [dnl
+])
+AT_CLEANUP
+
+# The flow is formatted with ofp10_match_to_string() for the
+# high-verbosity case.
+AT_SETUP([OFPT_FLOW_MOD - OF1.0 - high verbosity])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
01 0e 00 50 00 00 00 00 00 00 00 00 00 01 50 54 \
00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 \
00 00 01 0e 00 00 00 00 00 00 00 08 00 03 00 00 \
" 3], [0], [dnl
-OFPT_FLOW_MOD (xid=0x0): ADD arp,in_port=1,dl_vlan=65535,dl_vlan_pcp=0,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 idle:5 pri:65535 buf:0x10e actions=output:3
+OFPT_FLOW_MOD (xid=0x0): ADD arp,in_port=1,dl_vlan=65535,dl_vlan_pcp=0,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 idle:5 pri:65535 buf:0x10e out_port:0 actions=output:3
], [dnl
ofp_util|INFO|normalization changed ofp_match, details:
ofp_util|INFO| pre: priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0
])
AT_CLEANUP
+# The flow is formatted with cls_rule_format() for the low-verbosity case.
+AT_SETUP([OFPT_FLOW_MOD - OF1.2 - low verbosity])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 ff 00 00 00 00 00 ff ff \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \
+50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \
+80 00 0a 02 08 06 80 00 0c 02 00 00 80 00 2a 02 \
+00 02 80 00 2c 04 c0 a8 00 02 80 00 2e 04 c0 a8 \
+00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:255 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=2 actions=output:3
+], [dnl
+])
+AT_CLEANUP
+
AT_SETUP([OFPT_PORT_MOD - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
])
AT_CLEANUP
+AT_SETUP([OFPT_PORT_MOD - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 10 00 28 00 00 00 03 00 00 00 03 00 00 00 00 \
+50 54 00 00 00 01 00 00 00 00 00 01 00 00 00 01 \
+00 00 00 00 00 00 00 00 \
+" 3], [0], [dnl
+OFPT_PORT_MOD (OF1.2) (xid=0x3):port: 3: addr:50:54:00:00:00:01
+ config: PORT_DOWN
+ mask: PORT_DOWN
+ advertise: UNCHANGED
+])
+AT_CLEANUP
+
AT_SETUP([OFPST_DESC request])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100000000"], [0], [dnl
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
01 11 04 2c 00 00 00 01 00 00 00 00 4e 69 63 69 \
-72 61 20 4e 65 74 77 6f 72 6b 73 2c 20 49 6e 63 \
-2e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+72 61 2c 20 49 6e 63 2e 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
00 00 00 00 00 00 00 00 00 00 00 00 \
"], [0], [dnl
OFPST_DESC reply (xid=0x1):
-Manufacturer: Nicira Networks, Inc.
+Manufacturer: Nicira, Inc.
Hardware: Open vSwitch
Software: 1.1.0pre2
Serial Num: None
])
AT_CLEANUP
-AT_SETUP([OFPST_FLOW request])
+AT_SETUP([OFPST_FLOW request - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "\
01 10 00 38 00 00 00 04 00 01 00 00 00 38 20 ff \
])
AT_CLEANUP
-AT_SETUP([OFPST_FLOW reply])
+AT_SETUP([OFPST_FLOW request - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 12 00 38 00 00 00 02 00 01 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW request (OF1.2) (xid=0x2): @&t@
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_FLOW reply - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
01 11 01 e4 00 00 00 04 00 01 00 00 00 60 00 00 \
00 00 00 00 00 00 00 00 \
"], [0], [dnl
OFPST_FLOW reply (xid=0x4):
- cookie=0x0, duration=4.2s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:1
- cookie=0x0, duration=8.9s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5,priority=65535,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:3
- cookie=0x0, duration=4.28s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:3
- cookie=0x0, duration=9.096s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5,icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,icmp_type=8,icmp_code=0 actions=output:1
+ cookie=0x0, duration=4.2s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,arp_op=2,nw_tos=0,tp_src=0,tp_dst=0 actions=output:1
+ cookie=0x0, duration=8.9s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5, priority=65535,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:3
+ cookie=0x0, duration=4.28s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,arp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,arp_op=1,nw_tos=0,icmp_type=0,icmp_code=0 actions=output:3
+ cookie=0x0, duration=9.096s, table=0, n_packets=13, n_bytes=1274, idle_timeout=5, icmp,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,icmp_type=8,icmp_code=0 actions=output:1
cookie=0x0, duration=0s, table=2, n_packets=0, n_bytes=0, actions=drop
])
AT_CLEANUP
-AT_SETUP([OFPST_AGGREGATE request])
+AT_SETUP([OFPST_FLOW reply - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 13 01 78 00 00 00 02 00 01 00 00 00 00 00 00 \
+00 78 00 00 00 00 00 03 01 5e f3 c0 80 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 62 \
+00 01 00 2d 80 00 00 04 00 00 00 02 80 00 06 06 \
+ca da ad d6 0d 37 80 00 0a 02 08 00 80 00 10 01 \
+00 80 00 04 08 00 00 00 00 00 00 00 00 00 00 00 \
+00 04 00 18 00 00 00 00 00 00 00 10 00 00 00 02 \
+05 dc 00 00 00 00 00 00 00 78 00 00 00 00 00 04 \
+20 7c 0a 40 80 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 \
+00 00 00 00 00 00 00 8c 00 01 00 2d 80 00 00 04 \
+00 00 00 02 80 00 06 06 52 54 00 c3 00 89 80 00 \
+0a 02 08 00 80 00 10 01 00 80 00 04 08 00 00 00 \
+00 00 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
+00 00 00 10 00 00 00 02 05 dc 00 00 00 00 00 00 \
+00 78 00 00 00 00 00 04 20 a9 d1 00 80 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 2a \
+00 01 00 2d 80 00 00 04 00 00 00 02 80 00 06 06 \
+52 54 00 97 00 69 80 00 0a 02 08 00 80 00 10 01 \
+00 80 00 04 08 00 00 00 00 00 00 00 00 00 00 00 \
+00 04 00 18 00 00 00 00 00 00 00 10 00 00 00 02 \
+05 dc 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPST_FLOW reply (OF1.2) (xid=0x2):
+ cookie=0x0, duration=3.023s, table=0, n_packets=1, n_bytes=98, ip,metadata=0,in_port=2,dl_dst=ca:da:ad:d6:0d:37,nw_tos=0 actions=output:2
+ cookie=0x0, duration=4.545s, table=0, n_packets=2, n_bytes=140, ip,metadata=0,in_port=2,dl_dst=52:54:00:c3:00:89,nw_tos=0 actions=output:2
+ cookie=0x0, duration=4.548s, table=0, n_packets=1, n_bytes=42, ip,metadata=0,in_port=2,dl_dst=52:54:00:97:00:69,nw_tos=0 actions=output:2
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_AGGREGATE request - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "\
01 10 00 38 00 00 00 04 00 02 00 00 00 38 20 ff \
])
AT_CLEANUP
-AT_SETUP([OFPST_AGGREGATE reply])
+AT_SETUP([OFPST_AGGREGATE request - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 12 00 38 00 00 00 02 00 02 00 00 00 00 00 00 \
+ff 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 04 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE request (OF1.2) (xid=0x2): @&t@
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_AGGREGATE reply - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
01 11 00 24 00 00 00 04 00 02 00 00 00 00 00 00 \
])
AT_CLEANUP
+AT_SETUP([OFPST_AGGREGATE reply - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 13 00 28 00 00 00 02 00 02 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 79 00 00 00 00 00 00 4b 4f \
+00 00 00 03 00 00 00 00 \
+"], [0], [dnl
+OFPST_AGGREGATE reply (OF1.2) (xid=0x2): packet_count=121 byte_count=19279 flow_count=3
+])
+AT_CLEANUP
+
AT_SETUP([OFPST_TABLE request])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "0110000c0000000100030000"], [0], [dnl
])
AT_CLEANUP
-AT_SETUP([OFPST_TABLE reply])
+AT_SETUP([OFPST_TABLE reply - OF1.0])
AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
AT_CHECK([ovs-ofctl ofp-print "\
01 11 00 4c 00 00 00 01 00 03 00 00 00 00 00 00 \
])
AT_CLEANUP
+AT_SETUP([OFPST_TABLE reply - OF1.2])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+(mid="wild=0xfffffffff, max=1000000,"
+ tail="
+ match=0xfffffffff, instructions=0x00000007, config=0x00000000
+ write_actions=0x00000000, apply_actions=0x00000000
+ write_setfields=0x0000000fffffffff
+ apply_setfields=0x0000000fffffffff
+ metadata_match=0x0000000000000000
+ metadata_write=0x0000000000000000"
+ echo "OFPST_TABLE reply (OF1.2) (xid=0x2): 255 tables
+ 0: classifier: $mid active=1
+ lookup=74614, matched=106024$tail"
+ x=1
+ while test $x -lt 254; do
+ printf " %d: %-8s: $mid active=0
+ lookup=0, matched=0$tail
+" $x table$x
+ x=`expr $x + 1`
+ done
+ echo " 254: table254: $mid active=2
+ lookup=0, matched=0$tail") > expout
+
+(pad32="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ pad7="00 00 00 00 00 00 00 "
+ mid="00 00 00 0f ff ff ff ff \
+00 00 00 0f ff ff ff ff 00 00 00 00 00 00 00 00 \
+00 00 00 0f ff ff ff ff 00 00 00 0f ff ff ff ff \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 07 00 00 00 00 00 0f 42 40 "
+ tail="00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
+
+ echo -n "03 13 7f 90 00 00 00 02 00 03 00 00 00 00 00 00 "
+
+ x=0
+ printf "%02x $pad7" $x
+ printf "%s$pad32" "classifier" | od -A n -t x1 -v -N 32 | tr '\n' ' '
+ echo -n "$mid 00 00 00 01 "
+ echo -n "00 00 00 00 00 01 23 76 00 00 00 00 00 01 9e 28 "
+
+ x=1
+ while test $x -lt 254; do
+ printf "%02x $pad7" $x
+ printf "%s$pad32" "table$x" | od -A n -t x1 -v -N 32 | tr '\n' ' '
+ echo -n "$mid 00 00 00 00 $tail "
+ x=`expr $x + 1`
+ done
+
+ x=254
+ printf "%02x $pad7" $x
+ printf "%s$pad32" "table$x" | od -A n -t x1 -v -N 32 | tr '\n' ' '
+ echo -n "$mid 00 00 00 02 $tail") > in
+AT_CHECK([ovs-ofctl ofp-print "$(cat in)"], [0], [expout])
+AT_CLEANUP
+
AT_SETUP([OFPST_PORT request])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "\
])
AT_CLEANUP
-AT_SETUP([OFPT_BARRIER_REQUEST])
+AT_SETUP([OFPST_PORT_DESC request - OF1.0])
+AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
+AT_CHECK([ovs-ofctl ofp-print "0110000c00000001000d0000"], [0], [dnl
+OFPST_PORT_DESC request (xid=0x1):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPST_PORT_DESC reply - OF1.0])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 11 00 3c 00 00 00 00 00 0d 00 00 00 03 50 54 \
+00 00 00 01 65 74 68 30 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 01 00 00 00 01 00 00 02 08 \
+00 00 02 8f 00 00 02 8f 00 00 00 00 \
+"], [0], [dnl
+OFPST_PORT_DESC reply (xid=0x0):
+ 3(eth0): addr:50:54:00:00:00:01
+ config: PORT_DOWN
+ state: LINK_DOWN
+ current: 100MB-FD AUTO_NEG
+ advertised: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+ supported: 10MB-HD 10MB-FD 100MB-HD 100MB-FD COPPER AUTO_NEG
+ speed: 100 Mbps now, 100 Mbps max
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BARRIER_REQUEST - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print '01 12 00 08 00 00 00 01'], [0], [dnl
OFPT_BARRIER_REQUEST (xid=0x1):
])
AT_CLEANUP
-AT_SETUP([OFPT_BARRIER_REPLY])
+AT_SETUP([OFPT_BARRIER_REQUEST - OF1.1])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '02 14 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REQUEST (OF1.1) (xid=0x1):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BARRIER_REQUEST - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '03 14 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REQUEST (OF1.2) (xid=0x1):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BARRIER_REPLY - OF1.0])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print '01 13 00 08 00 00 00 01'], [0], [dnl
OFPT_BARRIER_REPLY (xid=0x1):
])
AT_CLEANUP
+AT_SETUP([OFPT_BARRIER_REPLY] - OF1.1)
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '02 15 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REPLY (OF1.1) (xid=0x1):
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_BARRIER_REPLY] - OF1.2)
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print '03 15 00 08 00 00 00 01'], [0], [dnl
+OFPT_BARRIER_REPLY (OF1.2) (xid=0x1):
+])
+AT_CLEANUP
+
AT_SETUP([NXT_ROLE_REQUEST])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
AT_SETUP([NXT_PACKET_IN])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
-01 04 00 aa 00 00 00 00 00 00 23 20 00 00 00 11 \
+01 04 00 ba 00 00 00 00 00 00 23 20 00 00 00 11 \
ff ff ff ff 00 40 01 07 00 00 00 00 00 00 00 09 \
-00 3a 00 00 00 00 00 00 00 00 00 02 00 01 00 01 \
+00 4e 00 00 00 00 00 00 00 00 00 02 00 01 00 01 \
20 08 00 00 00 00 00 00 00 06 00 01 00 04 00 00 \
00 01 00 01 02 04 00 00 00 02 00 01 04 04 00 00 \
00 03 00 01 06 04 00 00 00 04 00 01 08 04 00 00 \
-00 05 00 00 00 00 00 00 00 00 82 82 82 82 82 82 \
+00 05 80 00 05 10 5a 5a 5a 5a 5a 5a 5a 5a ff ff \
+ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \
80 81 81 81 81 81 81 00 00 50 08 00 45 00 00 28 \
00 00 00 00 00 06 32 05 53 53 53 53 54 54 54 54 \
00 55 00 56 00 00 00 00 00 00 00 00 50 00 00 00 \
31 6d 00 00 00 00 00 00 00 00 \
"], [0], [dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 metadata=0x5a5a5a5a5a5a5a5a reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
])
AT_CLEANUP
])
AT_CLEANUP
+AT_SETUP([NXT_FLOW_MONITOR_CANCEL])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 14 00 00 00 03 00 00 23 20 00 00 00 15 \
+01 02 30 40 \
+"], [0], [dnl
+NXT_FLOW_MONITOR_CANCEL (xid=0x3): id=16920640
+])
+AT_CLEANUP
+
+AT_SETUP([NXT_FLOW_MONITOR_PAUSED])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 16 \
+"], [0], [dnl
+NXT_FLOW_MONITOR_PAUSED (xid=0x3):
+])
+AT_CLEANUP
+
+AT_SETUP([NXT_FLOW_MONITOR_RESUMED])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 04 00 10 00 00 00 03 00 00 23 20 00 00 00 17 \
+"], [0], [dnl
+NXT_FLOW_MONITOR_RESUMED (xid=0x3):
+])
+AT_CLEANUP
+
AT_SETUP([NXT_SET_FLOW_FORMAT])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
AT_CHECK([ovs-ofctl ofp-print "\
01 04 00 60 00 00 00 02 00 00 23 20 00 00 00 0d \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 \
-ff ff ff ff ff ff 00 00 00 14 00 00 00 00 00 00 \
+ff ff ff ff 00 10 00 00 00 14 00 00 00 00 00 00 \
00 01 20 08 00 00 00 00 00 00 01 c8 00 01 00 04 \
00 00 00 7b 00 00 00 00 ff ff 00 18 00 00 23 20 \
00 07 00 1f 00 01 00 04 00 00 00 00 00 00 00 05 \
" 2], [0], [dnl
-NXT_FLOW_MOD (xid=0x2): ADD reg0=0x7b,tun_id=0x1c8 actions=load:0x5->NXM_NX_REG0[[]]
+NXT_FLOW_MOD (xid=0x2): ADD reg0=0x7b,tun_id=0x1c8 out_port:16 actions=load:0x5->NXM_NX_REG0[[]]
])
AT_CLEANUP
-# The flow is formatted with ofp_match_to_string() for the low-verbosity case.
+# The flow is formatted with ofp10_match_to_string() for the
+# low-verbosity case.
AT_SETUP([NXT_FLOW_MOD, high verbosity])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
01 04 00 60 00 00 00 02 00 00 23 20 00 00 00 0d \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 \
-ff ff ff ff ff ff 00 00 00 14 00 00 00 00 00 00 \
+ff ff ff ff 01 00 00 00 00 14 00 00 00 00 00 00 \
00 01 20 08 00 00 00 00 00 00 01 c8 00 01 00 04 \
00 00 00 7b 00 00 00 00 ff ff 00 18 00 00 23 20 \
00 07 00 1f 00 01 00 04 00 00 00 00 00 00 00 05 \
" 3], [0], [dnl
-NXT_FLOW_MOD (xid=0x2): ADD NXM_NX_TUN_ID(00000000000001c8), NXM_NX_REG0(0000007b) actions=load:0x5->NXM_NX_REG0[[]]
+NXT_FLOW_MOD (xid=0x2): ADD NXM_NX_TUN_ID(00000000000001c8), NXM_NX_REG0(0000007b) out_port:256 actions=load:0x5->NXM_NX_REG0[[]]
])
AT_CLEANUP
00 00 00 00 00 00 00 64 00 00 00 00 00 00 19 00 \
"], [0],
[[NXST_FLOW reply (xid=0x4):
- cookie=0x0, duration=1.048s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2535,tp_dst=0 actions=output:1
- cookie=0x0, duration=3.84s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,idle_age=2,priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2532,tp_dst=0 actions=output:1
- cookie=0x0, duration=2.872s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,idle_age=4,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2533 actions=output:3
- cookie=0x0, duration=4.756s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,idle_age=0,priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2531,tp_dst=0 actions=output:1
- cookie=0x0, duration=2.88s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,hard_timeout=10,idle_age=2,priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2533,tp_dst=0 actions=output:1
- cookie=0x0, duration=5.672s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2530,tp_dst=0 actions=output:1
- cookie=0x0, duration=1.04s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2535 actions=output:3
- cookie=0x0, duration=1.952s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2534 actions=output:3
- cookie=0x0, duration=4.668s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2531 actions=output:3
- cookie=0x0, duration=3.752s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2532 actions=output:3
- cookie=0x0, duration=0.172s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2536,tp_dst=0 actions=output:1
- cookie=0x0, duration=5.624s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2530 actions=output:3
- cookie=0x0, duration=0.08s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2536 actions=output:3
- cookie=0x0, duration=1.96s, table=0, n_packets=1, n_bytes=60, idle_timeout=5,priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2534,tp_dst=0 actions=output:1
+ cookie=0x0, duration=1.048s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2535,tp_dst=0 actions=output:1
+ cookie=0x0, duration=3.84s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=2, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2532,tp_dst=0 actions=output:1
+ cookie=0x0, duration=2.872s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=4, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2533 actions=output:3
+ cookie=0x0, duration=4.756s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, idle_age=0, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2531,tp_dst=0 actions=output:1
+ cookie=0x0, duration=2.88s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, hard_timeout=10, idle_age=2, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2533,tp_dst=0 actions=output:1
+ cookie=0x0, duration=5.672s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2530,tp_dst=0 actions=output:1
+ cookie=0x0, duration=1.04s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2535 actions=output:3
+ cookie=0x0, duration=1.952s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2534 actions=output:3
+ cookie=0x0, duration=4.668s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2531 actions=output:3
+ cookie=0x0, duration=3.752s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2532 actions=output:3
+ cookie=0x0, duration=0.172s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2536,tp_dst=0 actions=output:1
+ cookie=0x0, duration=5.624s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2530 actions=output:3
+ cookie=0x0, duration=0.08s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,tp_src=0,tp_dst=2536 actions=output:3
+ cookie=0x0, duration=1.96s, table=0, n_packets=1, n_bytes=60, idle_timeout=5, priority=65535,tcp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,tp_src=2534,tp_dst=0 actions=output:1
cookie=0x0, duration=228.78s, table=0, n_packets=0, n_bytes=0, reg0=0x7b,tun_id=0x1c8 actions=load:0x5->NXM_NX_REG0[]
cookie=0x0, duration=3600.0005s, table=1, n_packets=100, n_bytes=6400, actions=drop
]])
NXST_AGGREGATE reply (xid=0x4): packet_count=7 byte_count=420 flow_count=7
])
AT_CLEANUP
+
+AT_SETUP([NXST_FLOW_MONITOR request])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 10 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
+00 00 40 00 00 3f ff fe 00 00 01 00 00 00 00 00 \
+00 00 20 00 00 04 ff ff 00 06 02 00 00 00 00 00 00 00 00 02 00 01 00 00 \
+"], [0], [dnl
+NXST_FLOW_MONITOR request (xid=0x4):
+ id=16384 flags=initial,add,delete,modify,actions,own out_port=LOCAL table=1
+ id=8192 flags=delete table=2 in_port=1
+])
+AT_CLEANUP
+
+AT_SETUP([NXST_FLOW_MONITOR reply])
+AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
+AT_CHECK([ovs-ofctl ofp-print "\
+01 11 00 40 00 00 00 04 ff ff 00 00 00 00 23 20 00 00 00 02 00 00 00 00 \
+00 20 00 01 00 04 80 00 00 05 00 10 00 06 01 00 12 34 56 78 9a bc de f0 \
+00 00 00 02 00 01 00 00 \
+00 08 00 03 00 01 86 a0 \
+"], [0], [dnl
+NXST_FLOW_MONITOR reply (xid=0x4):
+ event=DELETED reason=eviction table=1 idle_timeout=5 hard_timeout=16 cookie=0x123456789abcdef0 in_port=1
+ event=ABBREV xid=0x186a0
+])
+AT_CLEANUP
table=1 in_port=3 priority=1500 icmp actions=output(14),resubmit(,2)
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 10,11,12,13,14,15,16,17,18,19,20,21
])
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=2,frag=no)' -generate], [0], [stdout])
-AT_CHECK([tail -2 stdout], [0],
+AT_CHECK([tail -3 stdout], [0],
[Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=1,frag=no)),2,4
-This flow is not cachable.
+This flow is handled by the userspace slow path because it:
+ - Sends "packet-in" messages to the OpenFlow controller.
])
AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=3,frag=no)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=127,frag=no)),2,set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=126,frag=no)),3,4
])
-AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --pidfile 2> ofctl_monitor.log])
+AT_CAPTURE_FILE([ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log])
AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=2,frag=no)' -generate], [0], [stdout])
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
-NXT_PACKET_IN (xid=0x0): table_id=1 total_len=42 in_port=1 tun_id=0x0 reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via invalid_ttl) data_len=42 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:1 tos:0 ttl:1 ip(192.168.0.1->192.168.0.2)
+NXT_PACKET_IN (xid=0x0): table_id=1 total_len=42 in_port=1 (via invalid_ttl) data_len=42 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:1 tos:0 ttl:1 ip(192.168.0.1->192.168.0.2)
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
dnl Flow miss.
-AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
for i in 1 2 3 ; do
ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
])
dnl Singleton controller action.
-AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
for i in 1 2 3 ; do
ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10)'
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
])
dnl Modified controller action.
-AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
for i in 1 2 3 ; do
ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=30:33:33:33:33:33,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10)'
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
])
dnl Checksum TCP.
-AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --no-chdir --pidfile 2> ofctl_monitor.log])
for i in 1 ; do
ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=20:22:22:22:22:22,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=11)'
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
-NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 tun_id=0x0 reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
dnl
-NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 reg0=0x1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
dnl
-NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
dnl
-NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
dnl
-NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) tcp_csum:1a03
+NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) tcp_csum:1a03
dnl
-NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) tcp_csum:3205
+NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) tcp_csum:3205
dnl
-NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) tcp_csum:31b8
+NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) tcp_csum:31b8
dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
])
dnl Checksum UDP.
-AT_CHECK([ovs-ofctl monitor br0 65534 --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
for i in 1 ; do
ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 20 22 22 22 22 22 08 00 45 00 00 1C 00 00 00 00 00 11 00 00 C0 A8 00 01 C0 A8 00 02 00 08 00 0B 00 00 12 34 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00'
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
-NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 tun_id=0x0 reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
dnl
-NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 reg0=0x1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
dnl
-NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
dnl
-NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
dnl
-NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 tun_id=0x0 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) udp_csum:2c37
+NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) udp_csum:2c37
dnl
-NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) udp_csum:4439
+NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) udp_csum:4439
dnl
-NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) udp_csum:43ec
+NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) udp_csum:43ec
dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
dnl
-NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 reg5=0x0 reg6=0x0 reg7=0x0 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
add-id-to-interface=false], [<0>
])
-AT_CHECK([test-netflow --detach --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])AT_CAPTURE_FILE([netflow.log])
+ON_EXIT([kill `cat test-netflow.pid`])
+AT_CHECK([test-netflow --detach --no-chdir --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])
+AT_CAPTURE_FILE([netflow.log])
for delay in 1000 30000; do
ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
ovs-appctl time/warp $delay
done
+sleep 1
OVS_VSWITCHD_STOP
ovs-appctl -t test-netflow exit
AT_CHECK([[sed -e 's/, uptime [0-9]*//
s/, now [0-9.]*//
-s/time \([0-9]*\)\.\.\.\1\b/time <moment>/
+s/time \([0-9]*\)\.\.\.\1$/time <moment>/
s/time [0-9]*\.\.\.[0-9]*/time <range>/
' netflow.log]], [0],
[header: v5, seq 0, engine 2,1
add-id-to-interface=false], [<0>
])
-AT_CHECK([test-netflow --detach --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])AT_CAPTURE_FILE([netflow.log])
+ON_EXIT([kill `test-netflow.pid`])
+AT_CHECK([test-netflow --detach --no-chdir --pidfile $NETFLOW_PORT:127.0.0.1 > netflow.log])AT_CAPTURE_FILE([netflow.log])
AT_CHECK([ovs-appctl time/stop])
n=1
ovs-appctl time/warp 10000
+sleep 1
OVS_VSWITCHD_STOP
ovs-appctl -t test-netflow exit
s/ table=0,//
s/ n_packets=0,//
s/ n_bytes=0,//
-s/idle_age=[0-9]*,//
-s/hard_age=[0-9]*,//
+s/ idle_age=[0-9]*,//
+s/ hard_age=[0-9]*,//
'
}]
m4_divert_pop([PREPARE_TESTS])
m4_define([OVS_VSWITCHD_START],
[OVS_RUNDIR=`pwd`; export OVS_RUNDIR
OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+ OVS_DBDIR=`pwd`; export OVS_DBDIR
OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
- trap 'kill `cat ovsdb-server.pid ovs-vswitchd.pid`' 0
+ ON_EXIT([kill `cat ovsdb-server.pid ovs-vswitchd.pid`])
dnl Create database.
- mkdir openvswitch
- touch openvswitch/.conf.db.~lock~
- AT_CHECK([ovsdb-tool create openvswitch/conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema])
+ touch .conf.db.~lock~
+ AT_CHECK([ovsdb-tool create conf.db $abs_top_srcdir/vswitchd/vswitch.ovsschema])
dnl Start ovsdb-server.
- AT_CHECK([ovsdb-server --detach --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock], [0], [], [stderr])
- AT_CHECK([[sed < stderr '/vlog|INFO|opened log file/d']])
+ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock], [0], [], [stderr])
+ AT_CHECK([[sed < stderr '
+/vlog|INFO|opened log file/d
+/ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']])
AT_CAPTURE_FILE([ovsdb-server.log])
dnl Initialize database.
AT_CHECK([ovs-vsctl --no-wait init])
dnl Start ovs-vswitchd.
- AT_CHECK([ovs-vswitchd --detach --pidfile --enable-dummy --disable-system --log-file], [0], [], [stderr])
+ AT_CHECK([ovs-vswitchd --detach --no-chdir --pidfile --enable-dummy --disable-system --log-file -vvconn -vofproto_dpif], [0], [], [stderr])
AT_CAPTURE_FILE([ovs-vswitchd.log])
AT_CHECK([[sed < stderr '
/vlog|INFO|opened log file/d
+/vswitchd|INFO|ovs-vswitchd (Open vSwitch)/d
/reconnect|INFO|/d
/ofproto|INFO|using datapath ID/d
/ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
m4_define([OVS_VSWITCHD_STOP],
[AT_CHECK([ovs-appctl -t ovs-vswitchd exit])
- AT_CHECK([ovs-appctl -t ovsdb-server exit])
- trap '' 0])
+ AT_CHECK([ovs-appctl -t ovsdb-server exit])])
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
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
OVS_VSWITCHD_STOP
AT_CLEANUP
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - port-desc stats])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -vwarn dump-ports-desc br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_PORT_DESC reply:
+ LOCAL(br0): addr:aa:55:aa:55:00:00
+ config: PORT_DOWN
+ state: LINK_DOWN
+ speed: 100 Mbps now, 100 Mbps max
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
dnl This is really bare-bones.
dnl It at least checks request and reply serialization and deserialization.
AT_SETUP([ofproto - queue stats])
OVS_VSWITCHD_START
-AT_CHECK([ovs-ofctl -vANY:ANY:WARN queue-stats br0], [0], [stdout])
+AT_CHECK([ovs-ofctl -vwarn queue-stats br0], [0], [stdout])
AT_CHECK([STRIP_XIDS stdout], [0], [dnl
OFPST_QUEUE reply: 0 queues
])
+AT_CHECK([ovs-ofctl -vwarn queue-stats br0 ALL 5], [0],
+ [OFPT_ERROR (xid=0x2): OFPQOFC_BAD_QUEUE
+OFPST_QUEUE request (xid=0x2):port=ALL queue=5
+])
+AT_CHECK([ovs-ofctl -vwarn queue-stats br0 10], [0],
+ [OFPT_ERROR (xid=0x2): OFPQOFC_BAD_PORT
+OFPST_QUEUE request (xid=0x2):port=10 queue=ALL
+])
OVS_VSWITCHD_STOP
AT_CLEANUP
'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
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
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])
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
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])
AT_SETUP([ofproto - flow table configuration])
OVS_VSWITCHD_START
# Check the default configuration.
-(echo "OFPST_TABLE reply (xid=0x1): 255 tables
+(echo "OFPST_TABLE reply (xid=0x2): 255 tables
0: classifier: wild=0x3fffff, max=1000000, active=0
lookup=0, matched=0"
x=1
- while test $x -lt 255; do
+ while test $x -lt 254; do
printf " %d: %-8s: wild=0x3fffff, max=1000000, active=0
lookup=0, matched=0
" $x table$x
x=`expr $x + 1`
- done) > expout
+ done
+ echo " 254: table254: wild=0x3fffff, max=1000000, active=2
+ lookup=0, matched=0") > expout
AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout])
# Change the configuration.
AT_CHECK(
])
# Check that the configuration was updated.
mv expout orig-expout
-(echo "OFPST_TABLE reply (xid=0x1): 255 tables
+(echo "OFPST_TABLE reply (xid=0x2): 255 tables
0: main : wild=0x3fffff, max=1000000, active=0
lookup=0, matched=0
1: table1 : wild=0x3fffff, max= 1024, active=0
ovs-ofctl add-flow br0 idle_timeout=${in_port}0,in_port=$in_port,actions=drop
done
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=10,in_port=1 actions=drop
- idle_timeout=20,in_port=2 actions=drop
- idle_timeout=30,in_port=3 actions=drop
- idle_timeout=40,in_port=4 actions=drop
+ idle_timeout=10, in_port=1 actions=drop
+ idle_timeout=20, in_port=2 actions=drop
+ idle_timeout=30, in_port=3 actions=drop
+ idle_timeout=40, in_port=4 actions=drop
NXST_FLOW reply:
])
# Adding another flow will cause the one that expires soonest to be evicted.
AT_CHECK([ovs-ofctl add-flow br0 in_port=5,actions=drop])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=20,in_port=2 actions=drop
- idle_timeout=30,in_port=3 actions=drop
- idle_timeout=40,in_port=4 actions=drop
+ idle_timeout=20, in_port=2 actions=drop
+ idle_timeout=30, in_port=3 actions=drop
+ idle_timeout=40, in_port=4 actions=drop
in_port=5 actions=drop
NXST_FLOW reply:
])
AT_CHECK([ovs-ofctl add-flow br0 in_port=4,actions=normal])
AT_CHECK([ovs-ofctl mod-flows br0 in_port=3,actions=output:1])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=30,in_port=3 actions=output:1
+ idle_timeout=30, in_port=3 actions=output:1
in_port=4 actions=NORMAL
in_port=5 actions=drop
in_port=6 actions=drop
idle_timeout=40 in_port=1 dl_src=00:33:44:55:66:77 actions=drop
EOF
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=10,in_port=2,dl_src=00:44:55:66:77:88 actions=drop
- idle_timeout=20,in_port=1,dl_src=00:11:22:33:44:55 actions=drop
- idle_timeout=30,in_port=1,dl_src=00:22:33:44:55:66 actions=drop
- idle_timeout=40,in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=10, in_port=2,dl_src=00:44:55:66:77:88 actions=drop
+ idle_timeout=20, in_port=1,dl_src=00:11:22:33:44:55 actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
NXST_FLOW reply:
])
# Adding another flow will cause the one that expires soonest within
# (which is what makes the test interesting):
AT_CHECK([ovs-ofctl add-flow br0 in_port=2,dl_src=00:55:66:77:88:99,actions=drop])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=10,in_port=2,dl_src=00:44:55:66:77:88 actions=drop
- idle_timeout=30,in_port=1,dl_src=00:22:33:44:55:66 actions=drop
- idle_timeout=40,in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=10, in_port=2,dl_src=00:44:55:66:77:88 actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
in_port=2,dl_src=00:55:66:77:88:99 actions=drop
NXST_FLOW reply:
])
idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=drop
EOF
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=10,in_port=2,dl_src=00:44:55:66:77:88 actions=drop
- idle_timeout=30,in_port=1,dl_src=00:22:33:44:55:66 actions=drop
- idle_timeout=40,in_port=1,dl_src=00:33:44:55:66:77 actions=drop
- idle_timeout=50,in_port=2,dl_src=00:66:77:88:99:aa actions=drop
- idle_timeout=60,in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
- idle_timeout=70,in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
+ idle_timeout=10, in_port=2,dl_src=00:44:55:66:77:88 actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
+ idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
+ idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
in_port=2,dl_src=00:55:66:77:88:99 actions=drop
NXST_FLOW reply:
])
# to be evicted.
AT_CHECK([ovs-ofctl add-flow br0 'idle_timeout=80 in_port=2 dl_src=00:99:aa:bb:cc:dd actions=drop'])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=30,in_port=1,dl_src=00:22:33:44:55:66 actions=drop
- idle_timeout=40,in_port=1,dl_src=00:33:44:55:66:77 actions=drop
- idle_timeout=50,in_port=2,dl_src=00:66:77:88:99:aa actions=drop
- idle_timeout=60,in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
- idle_timeout=70,in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
- idle_timeout=80,in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
+ idle_timeout=30, in_port=1,dl_src=00:22:33:44:55:66 actions=drop
+ idle_timeout=40, in_port=1,dl_src=00:33:44:55:66:77 actions=drop
+ idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
+ idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
+ idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
+ idle_timeout=80, in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
in_port=2,dl_src=00:55:66:77:88:99 actions=drop
NXST_FLOW reply:
])
# overall to be evicted.
AT_CHECK([ovs-vsctl set Flow_Table evict flow-limit=4])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- idle_timeout=60,in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
- idle_timeout=70,in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
- idle_timeout=80,in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
+ idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=drop
+ idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
+ idle_timeout=80, in_port=2,dl_src=00:99:aa:bb:cc:dd actions=drop
in_port=2,dl_src=00:55:66:77:88:99 actions=drop
NXST_FLOW reply:
])
ovs-ofctl -v packet-out br0 none controller '0001020304050010203040501234'
if test X"$1" = X"OFPR_ACTION"; then shift;
echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via action) data_len=14 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)"
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)"
fi
# OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123)
ovs-ofctl -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234'
if test X"$1" = X"OFPR_NO_MATCH"; then shift;
echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via no_match) data_len=14 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)"
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)"
fi
# OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0)
ovs-ofctl packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003fb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00'
if test X"$1" = X"OFPR_INVALID_TTL"; then shift;
echo >>expout "OFPT_PACKET_IN: total_len=76 in_port=NONE (via invalid_ttl) data_len=76 (unbuffered)
-priority:0,tunnel:0,in_port:0000,tci(0) mac(00:26:b9:8c:b0:f9->00:25:83:df:b4:00) type:0800 proto:17 tos:0 ttl:0 ip(172.17.55.13->172.16.0.2) port(55155->53) udp_csum:8f6d"
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:26:b9:8c:b0:f9->00:25:83:df:b4:00) type:0800 proto:17 tos:0 ttl:0 ip(172.17.55.13->172.16.0.2) port(55155->53) udp_csum:8f6d"
fi
# OFPT_PORT_STATUS, OFPPR_ADD
# It's a service connection so initially there should be no async messages.
check_async 1
-# Set miss_send_len to 128, turning on packet-outs for our service connection.
+# Set miss_send_len to 128, turning on packet-ins for our service connection.
ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
check_async 2 OFPR_ACTION OFPPR_ADD OFPPR_DELETE OFPRR_DELETE
ovs-appctl -t ovs-ofctl exit
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+dnl This test checks that OFPT_PACKET_OUT accepts both OFPP_NONE (as
+dnl specified by OpenFlow 1.0) and OFPP_CONTROLLER (used by some
+dnl controllers despite the spec) as meaning a packet that was generated
+dnl by the controller.
+AT_SETUP([ofproto - packet-out from controller])
+OVS_VSWITCHD_START
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P openflow10 monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send some packet-outs with OFPP_NONE and OFPP_CONTROLLER (65533) as in_port.
+AT_CHECK([ovs-ofctl packet-out br0 none controller '0001020304050010203040501234'])
+AT_CHECK([ovs-ofctl packet-out br0 65533 controller '0001020304050010203040505678'])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+OFPT_PACKET_IN: total_len=14 in_port=NONE (via action) data_len=14 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)
+OFPT_PACKET_IN: total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:5678 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)
+OFPT_BARRIER_REPLY:
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This test checks that metadata is encoded in packet_in structures,
+dnl supported by NXAST.
+AT_SETUP([ofproto - packet-out with metadata (NXM)])
+OVS_VSWITCHD_START
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P nxm monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0109000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send a packet-out with a load action to set some metadata, and forward to controller
+AT_CHECK([ovs-ofctl packet-out br0 none 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), controller' '0001020304050010203040501234'])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+NXT_PACKET_IN: total_len=14 in_port=NONE metadata=0xfafafafa5a5a5a5a (via action) data_len=14 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234 proto:0 tos:0 ttl:0 ip(0.0.0.0->0.0.0.0)
+OFPT_BARRIER_REPLY:
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - flow monitoring])
+AT_KEYWORDS([monitor])
+OVS_VSWITCHD_START
+
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:1
+
+# Start a monitor watching the flow table and check the initial reply.
+ovs-ofctl monitor br0 watch: --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+ovs-appctl -t ovs-ofctl ofctl/barrier
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
+ [NXST_FLOW_MONITOR reply:
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:1
+OFPT_BARRIER_REPLY:
+])
+
+# Add, delete, and modify some flows and check the updates.
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=124,actions=output:2
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:5
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,dl_vlan_pcp=0,actions=output:6
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,dl_vlan_pcp=1,actions=output:7
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=123,actions=output:8
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=0,actions=output:9
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,dl_vlan_pcp=1,actions=output:10
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=65535,actions=output:11
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,dl_vlan_pcp=0,actions=output:12
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,dl_vlan_pcp=1,actions=output:13
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=8191,actions=output:14
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=0,actions=output:15
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=1,actions=output:16
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,actions=output:17
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=0,actions=output:18
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,dl_vlan_pcp=1,actions=output:19
+ovs-ofctl add-flow br0 in_port=0,dl_vlan=0,actions=output:20
+ovs-ofctl add-flow br0 in_port=0,dl_vlan_pcp=0,actions=output:21
+ovs-ofctl add-flow br0 in_port=0,dl_vlan_pcp=1,actions=output:22
+ovs-ofctl add-flow br0 in_port=0,actions=output:23
+ovs-ofctl mod-flows br0 cookie=5,dl_vlan=123,actions=output:3
+ovs-ofctl del-flows br0 dl_vlan=123
+ovs-ofctl del-flows br0
+ovs-appctl -t ovs-ofctl ofctl/barrier
+sort='
+ # Sorts groups of lines that start with a space, without moving them
+ # past the nearest line that does not start with a space.
+ use warnings;
+ use strict;
+ my @buffer = ();
+ while (<STDIN>) {
+ if (/^ /) {
+ push(@buffer, $_);
+ } else {
+ print $_ foreach sort(@buffer);
+ print $_;
+ @buffer = ();
+ }
+ }
+ print $_ foreach sort(@buffer);
+'
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log | perl -e "$sort"], [0],
+[NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:5
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:6
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:7
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=123 actions=output:8
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:9
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:10
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,vlan_tci=0x0000 actions=output:11
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:15
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:16
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:17
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=0 actions=output:23
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3
+ event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
+ event=MODIFIED table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123 actions=output:3
+ event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=0 actions=output:3
+ event=DELETED reason=delete table=0 cookie=0x5 in_port=0,dl_vlan=123,dl_vlan_pcp=1 actions=output:3
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=DELETED reason=delete table=0 cookie=0 in_port=0 actions=output:23
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0 actions=output:20
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=0 actions=output:18
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=0,dl_vlan_pcp=1 actions=output:19
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=124 actions=output:2
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095 actions=output:14
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=0 actions=output:12
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan=4095,dl_vlan_pcp=1 actions=output:13
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=0 actions=output:21
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,dl_vlan_pcp=1 actions=output:22
+ event=DELETED reason=delete table=0 cookie=0 in_port=0,vlan_tci=0x0000 actions=output:11
+OFPT_BARRIER_REPLY:
+])
+
+# Check that our own changes are reported as abbreviations.
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+ovs-ofctl add-flow br0 in_port=1,actions=output:2
+ovs-ofctl add-flow br0 in_port=2,actions=output:1
+ovs-appctl -t ovs-ofctl ofctl/send 010e004812345678003fffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000ffffffffffff0000
+ovs-appctl -t ovs-ofctl ofctl/barrier
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0], [NXST_FLOW reply:
+])
+AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0],
+[NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=1 actions=output:2
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ADDED table=0 cookie=0 in_port=2 actions=output:1
+send: OFPT_FLOW_MOD: DEL priority=0 actions=drop
+NXST_FLOW_MONITOR reply (xid=0x0):
+ event=ABBREV xid=0x12345678
+OFPT_BARRIER_REPLY:
+])
+
+ovs-appctl -t ovs-ofctl exit
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - flow monitoring pause and resume])
+AT_KEYWORDS([monitor])
+
+# The maximum socket receive buffer size is important for this test, which
+# tests behavior when the receive buffer overflows.
+if test -e /proc/sys/net/core/rmem_max; then
+ # Linux
+ rmem_max=`cat /proc/sys/net/core/rmem_max`
+elif rmem_max=`sysctl -n net.inet.tcp.recvbuf_max 2>/dev/null`; then
+ : # FreeBSD
+else
+ # Don't know how to get maximum socket receive buffer on this OS
+ AT_SKIP_IF([:])
+fi
+# Calculate the total amount of queuing: rmem_max in the kernel, 128 kB
+# in ofproto sending userspace (see ofmonitor_flush() in connmgr.c).
+queue_size=`expr $rmem_max + 128 \* 1024`
+echo rmem_max=$rmem_max queue_size=$queue_size
+
+# Each flow update message takes up at least 48 bytes of space in queues
+# and in practice more than that.
+n_msgs=`expr $queue_size / 48`
+echo n_msgs=$n_msgs
+
+OVS_VSWITCHD_START
+
+# Start a monitor watching the flow table, then make it block.
+ON_EXIT([kill `cat ovs-ofctl.pid`])
+ovs-ofctl monitor br0 watch: --detach --no-chdir --pidfile >monitor.log 2>&1
+AT_CAPTURE_FILE([monitor.log])
+ovs-appctl -t ovs-ofctl ofctl/block
+
+# Add $n_msgs flows.
+(echo "in_port=2,actions=output:2"
+perl -e '
+ for ($i = 0; $i < '$n_msgs'; $i++) {
+ print "cookie=1,reg1=$i,actions=drop\n";
+ }
+') > flows.txt
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+# Check that multipart flow dumps work properly:
+AT_CHECK([ovs-ofctl diff-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl add-flow br0 in_port=1,cookie=3,actions=drop])
+AT_CHECK([ovs-ofctl mod-flows br0 in_port=2,cookie=2,actions=output:2])
+AT_CHECK([ovs-ofctl del-flows br0 cookie=1/-1])
+
+ovs-appctl -t ovs-ofctl ofctl/unblock
+ovs-appctl -t ovs-ofctl ofctl/barrier
+
+ovs-appctl -t ovs-ofctl exit
+
+# Check that the flow monitor reported the same number of flows
+# added and deleted, but fewer than we actually added and deleted.
+adds=`grep -c 'ADDED.*reg1=' monitor.log`
+deletes=`grep -c 'DELETED.*reg1=' monitor.log`
+echo adds=$adds deletes=$deletes
+AT_CHECK([test $adds -gt 100 && test $adds -lt $n_msgs])
+AT_CHECK([test $adds = $deletes])
+
+# Check that the flow monitor reported everything in the expected order:
+#
+# event=ADDED table=0 cookie=0x1 reg1=0x22
+# ...
+# NXT_FLOW_MONITOR_PAUSED:
+# ...
+# event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
+# ...
+# event=ADDED table=0 cookie=0x3 in_port=1
+# event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2
+# NXT_FLOW_MONITOR_RESUMED:
+#
+# except that, between the PAUSED and RESUMED, the order of the ADDED
+# and MODIFIED lines lines depends on hash order, that is, it varies
+# as we change the hash function or change architecture. Therefore,
+# we use a couple of tests below to accept both orders.
+AT_CHECK([ofctl_strip < monitor.log | sed -n -e '
+/reg1=0x22$/p
+/cookie=0x[[23]]/p
+/NXT_FLOW_MONITOR_PAUSED:/p
+/NXT_FLOW_MONITOR_RESUMED:/p
+' > monitor.log.subset])
+AT_CHECK([grep -v MODIFIED monitor.log.subset], [0], [dnl
+ event=ADDED table=0 cookie=0x1 reg1=0x22
+NXT_FLOW_MONITOR_PAUSED:
+ event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
+ event=ADDED table=0 cookie=0x3 in_port=1
+NXT_FLOW_MONITOR_RESUMED:
+])
+AT_CHECK([grep -v ADDED monitor.log.subset], [0], [dnl
+NXT_FLOW_MONITOR_PAUSED:
+ event=DELETED reason=delete table=0 cookie=0x1 reg1=0x22
+ event=MODIFIED table=0 cookie=0x2 in_port=2 actions=output:2
+NXT_FLOW_MONITOR_RESUMED:
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
AT_SKIP_IF([test $HAVE_PYTHON = no])
OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
OVS_PKGDATADIR=`pwd`; export OVS_PKGDATADIR
cp "$top_srcdir/vswitchd/vswitch.ovsschema" .
-trap 'kill `cat pid ovs-monitor-ipsec.pid`' 0
+ON_EXIT([kill `cat pid ovs-monitor-ipsec.pid`])
mkdir etc etc/init.d etc/racoon etc/racoon/certs
mkdir usr usr/sbin
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' "$@"
actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, NXM_NX_REG0[0..12])
table=1,actions=drop
tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop
+metadata=0x1234ffff5678ffff/0xffff0000ffff0000,actions=drop
actions=bundle(eth_src,50,active_backup,ofport,slaves:1)
actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3)
actions=bundle(symmetric_l4,60,hrw,ofport,slaves:)
NXT_FLOW_MOD: ADD table:255 actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
NXT_FLOW_MOD: ADD table:1 actions=drop
NXT_FLOW_MOD: ADD table:255 tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop
+NXT_FLOW_MOD: ADD table:255 metadata=0x1234000056780000/0xffff0000ffff0000 actions=drop
NXT_FLOW_MOD: ADD table:255 actions=bundle(eth_src,50,active_backup,ofport,slaves:1)
NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3)
NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:)
tun_id=0x1234,cookie=0x5678,actions=flood
actions=drop
reg0=123,actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG2[0..31],move:NXM_NX_REG0[0..31]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
+actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[]
actions=autopath(5,NXM_NX_REG0[])
vlan_tci=0x1123/0x1fff,actions=drop
]])
-AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout])
+AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout], [stderr])
AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
[[usable protocols: NXM
chosen protocol: NXM-table_id
NXT_FLOW_MOD: ADD NXM_NX_TUN_ID(0000000000001234) cookie:0x5678 actions=FLOOD
NXT_FLOW_MOD: ADD <any> actions=drop
NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
+NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[]
NXT_FLOW_MOD: ADD <any> actions=autopath(5,NXM_NX_REG0[])
NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop
]])
+AT_CHECK([[sed 's/^[^|]*|[^|]*|//' stderr]], [0], [dnl
+autopath|WARN|The autopath action is deprecated and may be removed in February 2013. Please email dev@openvswitch.org with concerns.
+])
AT_CLEANUP
AT_SETUP([ovs-ofctl parse-nx-match])
NXM_OF_ETH_DST_W(000000000000/010000000000)
NXM_OF_ETH_DST_W(ffffffffffff/010000000000)
NXM_OF_ETH_DST_W(0002e30f80a4/ffffffffffff)
+NXM_OF_ETH_DST_W(60175619848f/000000000000)
NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
+NXM_OF_ETH_DST_W(60175619848f/5a5a5a5a5a5a)
# eth src
NXM_OF_ETH_SRC(020898456ddb)
+NXM_OF_ETH_SRC_W(012345abcdef/ffffff555555)
+NXM_OF_ETH_SRC_W(020898456ddb/ffffffffffff)
+NXM_OF_ETH_SRC_W(020898456ddb/000000000000)
# eth type
NXM_OF_ETH_TYPE(0800)
NXM_OF_VLAN_TCI(3123) # Packets with VID=123, PCP=1.
NXM_OF_VLAN_TCI(0123) # Does not make sense (but supported anyway)
NXM_OF_VLAN_TCI_W(1123/1fff) # Packets with VID=123, any PCP.
+NXM_OF_VLAN_TCI_W(1123/ffff) # Packets with VID=123, PCP=0
+NXM_OF_VLAN_TCI_W(1123/0000) # Packets with or without 802.1Q header
NXM_OF_VLAN_TCI_W(f000/f000) # Packets with any VID, PCP=7.
NXM_OF_VLAN_TCI_W(0000/e000) # No 802.1Q or with VID=0
# IP source
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC(ac100014)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/FFFF0000)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/5a5a5a5a)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/ffffffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/00000000)
NXM_OF_ETH_TYPE(0806) NXM_OF_IP_SRC(ac100014)
NXM_OF_IP_SRC_W(C0D80000/FFFF0000)
# IP destination
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST(ac100014)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a88012/FFFF0000)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a80000/5a5a5a5a)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a80000/ffffffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a80000/00000000)
NXM_OF_IP_DST(ac100014)
NXM_OF_ETH_TYPE(0806) NXM_OF_IP_DST_W(C0D80000/FFFF0000)
# TCP source port
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC(4231)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC_W(5050/F0F0)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC_W(5050/ffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC_W(5050/0000)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_SRC(4231)
# TCP destination port
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST(4231)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/FFF0)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/ffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/0000)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_DST(4231)
# UDP source port
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC(8732)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/01FF)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/ffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/0000)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_UDP_SRC(7823)
# UDP destination port
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST(1782)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST_W(5005/F00F)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST_W(5005/FFFF)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST_W(5005/0000)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_OF_UDP_DST(1293)
# ICMP type
# ARP source protocol address
NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA(ac100014)
NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/FFFFFF00)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/aaaaaa00)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/ffffffff)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/00000000)
NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_SPA(ac100014)
NXM_OF_ARP_SPA_W(C0D8fedc/FFFF0000)
# ARP destination protocol address
NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA(ac100014)
NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a812fe/FFFFFF00)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a81234/77777777)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a81234/ffffffff)
+NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a81234/00000000)
NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_TPA(ac100014)
NXM_OF_ARP_TPA_W(C0D80000/FFFF0000)
NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/5a5a5a5a5a5a5a5a0000000000000000)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/00000000000000000000000000000000)
NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
# IPv6 destination
NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
-NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/77777777777777777777777777777777)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/00000000000000000000000000000000)
NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+# IPv6 Flow Label
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_LABEL(1000000f)
+NXM_NX_IPV6_LABEL(0000000f)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_LABEL(0000000f)
+
+# ND target address
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/0123456789abcdeffedcba9876543210)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/ffffffffffffffffffffffffffffffff)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/00000000000000000000000000000000)
+NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET_W(20010db83c4d00010002000300040005/fedcba98765432100123456789abcdef)
+
# ND source hardware address
NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4)
NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4)
NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(01/01)
NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(02/02)
NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(03/03)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(03/ff)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG_W(03/00)
NXM_OF_ETH_TYPE(0800) NXM_NX_IP_FRAG(f3)
# IPv6 fragments.
NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(01/01)
NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(02/02)
NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/03)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/00)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG_W(03/ff)
NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG(f3)
# Flow cookie.
NXM_NX_COOKIE(00000000abcdef01)
NXM_NX_COOKIE_W(84200000abcdef01/84200000FFFFFFFF)
+NXM_NX_COOKIE_W(84200000abcdef01/ffffffffffffffff)
+NXM_NX_COOKIE_W(84200000abcdef01/0000000000000000)
# Tunnel ID.
NXM_NX_TUN_ID(00000000abcdef01)
NXM_NX_TUN_ID_W(84200000abcdef01/84200000FFFFFFFF)
+NXM_NX_TUN_ID_W(84200000abcdef01/FFFFFFFFFFFFFFFF)
+NXM_NX_TUN_ID_W(84200000abcdef01/0000000000000000)
# Register 0.
NXM_NX_REG0(acebdf56)
NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
+NXM_NX_REG0_W(a0e0d050/ffffffff)
+NXM_NX_REG0_W(a0e0d050/00000000)
# Invalid field number.
01020304(1111/2222)
NXM_OF_ETH_DST_W(000000000000/010000000000)
NXM_OF_ETH_DST_W(010000000000/010000000000)
NXM_OF_ETH_DST(0002e30f80a4)
+<any>
NXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
+NXM_OF_ETH_DST_W(40125218000a/5a5a5a5a5a5a)
# eth src
NXM_OF_ETH_SRC(020898456ddb)
+NXM_OF_ETH_SRC_W(012345014545/ffffff555555)
+NXM_OF_ETH_SRC(020898456ddb)
+<any>
# eth type
NXM_OF_ETH_TYPE(0800)
NXM_OF_VLAN_TCI(3123)
NXM_OF_VLAN_TCI(0123)
NXM_OF_VLAN_TCI_W(1123/1fff)
+NXM_OF_VLAN_TCI(1123)
+<any>
NXM_OF_VLAN_TCI_W(f000/f000)
NXM_OF_VLAN_TCI_W(0000/e000)
# IP source
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(ac100014)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC_W(c0a80000/ffff0000)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC_W(40080000/5a5a5a5a)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80000)
+NXM_OF_ETH_TYPE(0800)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
nx_pull_match() returned error OFPBMC_BAD_PREREQ
# IP destination
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST(ac100014)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST_W(c0a80000/ffff0000)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST_W(40080000/5a5a5a5a)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST(c0a80000)
+NXM_OF_ETH_TYPE(0800)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
nx_pull_match() returned error OFPBMC_BAD_PREREQ
# TCP source port
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(4231)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC_W(5050/f0f0)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(5050)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
# TCP destination port
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(4231)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST_W(fde0/fff0)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(fde0)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
# UDP source port
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(8732)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC_W(0132/01ff)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(0132)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
# UDP destination port
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(1782)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST_W(5005/f00f)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(5005)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
# ICMP type
# ARP source protocol address
NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(ac100014)
NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA_W(c0a81200/ffffff00)
+NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA_W(80a80200/aaaaaa00)
+NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(c0a81234)
+NXM_OF_ETH_TYPE(0806)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
nx_pull_match() returned error OFPBMC_BAD_PREREQ
# ARP destination protocol address
NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA(ac100014)
NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA_W(c0a81200/ffffff00)
+NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA_W(40201234/77777777)
+NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA(c0a81234)
+NXM_OF_ETH_TYPE(0806)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
nx_pull_match() returned error OFPBMC_BAD_PREREQ
NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(00000818184800000000000000000000/5a5a5a5a5a5a5a5a0000000000000000)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000000)
+NXM_OF_ETH_TYPE(86dd)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
# IPv6 destination
NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
-NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010530344500010000000000000000/77777777777777777777777777777777)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST(20010db83c4d00010000000000000000)
+NXM_OF_ETH_TYPE(86dd)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IPv6 Flow Label
+nx_pull_match() returned error OFPBMC_BAD_VALUE
nx_pull_match() returned error OFPBMC_BAD_PREREQ
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_LABEL(0000000f)
+
+# ND target address
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET_W(00010520080900010000000000040000/0123456789abcdeffedcba9876543210)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87)
+NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET_W(20000898344400000002000300000005/fedcba98765432100123456789abcdef)
# ND source hardware address
NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005), NXM_NX_ND_SLL(0002e30f80a4)
NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(01/01)
NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG_W(02/02)
NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(03)
+NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(03)
+NXM_OF_ETH_TYPE(0800), NXM_NX_IP_FRAG(00)
nx_pull_match() returned error OFPBMC_BAD_VALUE
# IPv6 fragments.
NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(01/01)
NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG_W(02/02)
NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(03)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(00)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_FRAG(03)
nx_pull_match() returned error OFPBMC_BAD_VALUE
# Flow cookie.
NXM_NX_COOKIE(00000000abcdef01)
NXM_NX_COOKIE_W(84200000abcdef01/84200000ffffffff)
+NXM_NX_COOKIE(84200000abcdef01)
+<any>
# Tunnel ID.
NXM_NX_TUN_ID(00000000abcdef01)
NXM_NX_TUN_ID_W(84200000abcdef01/84200000ffffffff)
+NXM_NX_TUN_ID(84200000abcdef01)
+<any>
# Register 0.
NXM_NX_REG0(acebdf56)
NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
+NXM_NX_REG0(a0e0d050)
+<any>
# Invalid field number.
nx_pull_match() returned error OFPBMC_BAD_FIELD
])
AT_CLEANUP
+AT_SETUP([ovs-ofctl parse-ofp10-match])
+AT_KEYWORDS([OF1.0])
+AT_DATA([test-data], [dnl
+# in_port=65534
+003820fe fffe xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_src=00:01:02:03:04:05
+003820fb xxxx 000102030405 xxxxxxxxxxxx xxxx xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_dst=10:20:30:40:50:60
+003820f7 xxxx xxxxxxxxxxxx 102030405060 xxxx xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_vlan=291
+003820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx 0123 xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_vlan_pcp=5
+002820ff xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx 05 xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_vlan=291,dl_vlan_pcp=4
+002820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx 0123 04 xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# vlan_tci=0x0000
+003820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx ffff xx xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl dl_vlan_pcp doesn't make sense when dl_vlan is "none", so
+dnl OVS ignores it and drops it on output.
+# vlan_tci=0x0000
+# 1: 28 -> 38
+# 20: 05 -> 00
+002820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx ffff 05 xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl Invalid VID and PCP discards out-of-range bits:
+# dl_vlan=256,dl_vlan_pcp=7
+# 18: f1 -> 01
+# 20: ff -> 07
+002820fd xxxx xxxxxxxxxxxx xxxxxxxxxxxx f100 ff xx xxxx xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# dl_type=0x1234
+003820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# ip,nw_proto=5
+003820cf xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 05 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl Ignore nw_proto if not IP or ARP:
+# dl_type=0x1234,nw_proto=5
+# normal: 3: cf -> ef
+# normal: 25: 05 -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: dl_type=0x1234,nw_proto=5
+& ofp_util|INFO|post: dl_type=0x1234
+003820cf xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx 05 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# ip,nw_tos=252
+001820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 fc xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl Ignore nw_tos if not IP:
+# arp,nw_tos=4
+# 24: 05 -> 04
+# normal: 1: 18 -> 38
+# normal: 24: 04 -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: arp,nw_tos=4
+& ofp_util|INFO|post: arp
+001820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 05 xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+dnl Low 2 bits of invalid TOS are forced to 0:
+# ip,nw_tos=48
+# 24: 31 -> 30
+001820ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 31 xx xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# arp,arp_op=2
+003820cf xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx 02 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx xxxx
+
+# ip,nw_src=192.168.128.85
+003800ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+# ip,nw_src=192.168.128.0/24
+# 31: 55 -> 00
+003808ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+# ip,nw_dst=192.168.128.85
+003020ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+# ip,nw_dst=192.168.128.0/24
+# 35: 55 -> 00
+003220ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+# arp,nw_src=192.168.128.85
+003800ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+# arp,nw_src=192.168.128.0/24
+# 31: 55 -> 00
+003808ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+# arp,nw_dst=192.168.128.85
+003020ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+# arp,nw_dst=192.168.128.0/24
+# 35: 55 -> 00
+003220ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0806 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+dnl Ignore nw_src if not IP or ARP:
+# dl_type=0x1234,nw_src=192.168.128.0/24
+# 31: 55 -> 00
+# normal: 2: 08 -> 20
+# normal: 28: c0 -> 00
+# normal: 29: a8 -> 00
+# normal: 30: 80 -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: dl_type=0x1234,nw_src=192.168.128.0/24
+& ofp_util|INFO|post: dl_type=0x1234
+003808ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx xx xxxx dnl
+c0a88055 xxxxxxxx xxxx xxxx
+
+dnl Ignore nw_dst if not IP or ARP:
+# dl_type=0x1234,nw_dst=192.168.128.0/24
+# 35: 55 -> 00
+# normal: 1: 32 -> 38
+# normal: 32: c0 -> 00
+# normal: 33: a8 -> 00
+# normal: 34: 80 -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: dl_type=0x1234,nw_dst=192.168.128.0/24
+& ofp_util|INFO|post: dl_type=0x1234
+003220ef xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 1234 xx xx xxxx dnl
+xxxxxxxx c0a88055 xxxx xxxx
+
+# tcp,tp_src=443
+0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 06 xxxx dnl
+xxxxxxxx xxxxxxxx 01bb xxxx
+
+# tcp,tp_dst=443
+0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 06 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx 01bb
+
+# udp,tp_src=443
+0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 11 xxxx dnl
+xxxxxxxx xxxxxxxx 01bb xxxx
+
+# udp,tp_dst=443
+0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 11 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx 01bb
+
+# icmp,icmp_type=5
+0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 01 xxxx dnl
+xxxxxxxx xxxxxxxx 0005 xxxx
+
+# icmp,icmp_code=8
+0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 01 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx 0008
+
+dnl Ignore tp_src if not TCP or UDP:
+# ip,nw_proto=21,tp_src=443
+# normal: 3: 8f -> cf
+# normal: 36: 01 -> 00
+# normal: 37: bb -> 00
+& ofp_util|INFO|normalization changed ofp_match, details:
+& ofp_util|INFO| pre: ip,nw_proto=21,tp_src=443
+& ofp_util|INFO|post: ip,nw_proto=21
+0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 15 xxxx dnl
+xxxxxxxx xxxxxxxx 01bb xxxx
+
+dnl Ignore tp_dst if not TCP or UDP:
+# ip,nw_proto=21,tp_dst=443
+# normal: 3: 4f -> cf
+# normal: 38: 01 -> 00
+# normal: 39: bb -> 00
+dnl The normalization details are suppressed here due to rate-limiting.
+0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 15 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx 01bb
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+ [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp10-match < input.txt],
+ [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([ovs-ofctl parse-ofp11-match])
+AT_KEYWORDS([OF1.1])
+AT_DATA([test-data], [dnl
+# in_port=65534
+0000 0058 fffffffe 000003fe dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# bad ofp11_match: OFPBMC_BAD_VALUE
+& ofp_util|WARN|port 305419896 is outside the supported range 0 through 65279 or 0xffffff00 through 0xffffffff
+0000 0058 12345678 000003fe dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_src=00:01:02:03:04:05
+0000 0058 00000000 000003ff dnl
+000102030405000000000000 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_src=55:55:55:55:55:55/55:55:55:55:55:55
+0000 0058 00000000 000003ff dnl
+555555555555aaaaaaaaaaaa 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_dst=00:01:02:03:04:05
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000102030405000000000000 dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_dst=01:00:00:00:00:00/01:00:00:00:00:00
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 010000000000feffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_dst=00:01:02:03:04:05/fe:ff:ff:ff:ff:ff
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000102030405010000000000 dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_dst=55:55:55:55:55:55/55:55:55:55:55:55
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 555555555555aaaaaaaaaaaa dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl dl_vlan_pcp is ignored if dl_vlan is wildcarded, which causes the
+dnl the wildcard bit and the dl_vlan_pcp to be dropped for output:
+# in_port=1
+# 11: fa -> fe
+# 38: 03 -> 00
+0000 0058 00000001 000003fa dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 03 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_vlan=291
+0000 0058 00000000 000003fd dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0123 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl OFPVID_NONE:
+# vlan_tci=0x0000
+0000 0058 00000000 000003fd dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+ffff 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl OFPVID_NONE ignores dl_vlan_pcp even if not wildcarded, which causes
+dnl the wildcard bit and the dl_vlan_pcp to be dropped for output:
+# vlan_tci=0x0000
+# 11: f9 -> fd
+# 38: 05 -> 00
+0000 0058 00000000 000003f9 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+ffff 05 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# vlan_tci=0x1000/0x1000
+0000 0058 00000000 000003fd dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+fffe 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl Try invalid VID:
+# bad ofp11_match: OFPBMC_BAD_VALUE
+0000 0058 00000000 000003fd dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+1234 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_vlan_pcp=4
+0000 0058 00000000 000003f9 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+fffe 04 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_vlan=10,dl_vlan_pcp=6
+0000 0058 00000000 000003f9 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+000a 06 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# dl_type=0x1234
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 1234 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_tos=252
+0000 0058 00000000 000003e7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 fc 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl Try invalid TOS:
+# bad ofp11_match: OFPBMC_BAD_VALUE
+0000 0058 00000000 000003e7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 01 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_proto=5
+0000 0058 00000000 000003d7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 05 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# arp,arp_op=2
+0000 0058 00000000 000003d7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0806 00 02 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_src=192.168.128.0/24
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 00 c0a88000000000ff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_src=128.160.128.0/165.165.165.165
+# 44: c0 -> 80
+# 45: a8 -> a0
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 00 c0a880005a5a5a5a 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_dst=192.168.128.0/24
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 00 00000000ffffffff c0a88000000000ff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# ip,nw_dst=128.160.128.0/165.165.165.165
+# 52: c0 -> 80
+# 53: a8 -> a0
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 00 00000000ffffffff c0a880005a5a5a5a 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# arp,nw_src=192.168.128.0/24
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0806 00 00 c0a88000000000ff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# arp,nw_dst=192.168.128.0/24
+0000 0058 00000000 000003f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0806 00 00 00000000ffffffff c0a88000000000ff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# tcp,tp_src=443
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 06 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# tcp,tp_dst=443
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 06 00000000ffffffff 00000000ffffffff 0000 01bb dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# udp,tp_src=443
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# icmp,icmp_type=5
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 01 00000000ffffffff 00000000ffffffff 0005 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# icmp,icmp_code=8
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 01 00000000ffffffff 00000000ffffffff 0000 0008 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# udp,tp_src=443
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+# udp,tp_dst=443
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 0000 01bb dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl SCTP, no ports.
+# ip,nw_proto=132
+0000 0058 00000000 000003d7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl SCTP tp_src matching not supported:
+# bad ofp11_match: OFPBMC_BAD_FIELD
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl SCTP tp_dst matching not supported:
+# bad ofp11_match: OFPBMC_BAD_FIELD
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 0000 01bb dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl Ignore tp_src if not TCP or UDP or SCTP:
+# ip,nw_proto=21
+# 11: 97 -> d7
+# 60: 01 -> 00
+# 61: bb -> 00
+0000 0058 00000000 00000397 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 15 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl Ignore tp_dst if not TCP or UDP or SCTP:
+# ip,nw_proto=22
+# 11: 57 -> d7
+# 62: 01 -> 00
+# 63: bb -> 00
+0000 0058 00000000 00000357 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0800 00 16 00000000ffffffff 00000000ffffffff 0000 01bb dnl
+00000000 00 000000 0000000000000000ffffffffffffffff
+
+dnl mpls_label not yet supported:
+# bad ofp11_match: OFPBMC_BAD_TAG
+0000 0058 00000000 000002f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 8847 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+12345678 00 000000 0000000000000000ffffffffffffffff
+
+dnl mpls_tc not yet supported:
+# bad ofp11_match: OFPBMC_BAD_TAG
+0000 0058 00000000 000001f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 8848 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 5a 000000 0000000000000000ffffffffffffffff
+
+dnl mpls_label and mpls_tc must be ignored if dl_type is not MPLS:
+# dl_type=0x1234
+# 10: 00 -> 03
+# 64: 12 -> 00
+# 65: 34 -> 00
+# 66: 56 -> 00
+# 67: 78 -> 00
+# 68: 5a -> 00
+0000 0058 00000000 000000f7 dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 1234 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+12345678 5a 000000 0000000000000000ffffffffffffffff
+
+dnl metadata match:
+# metadata=0x1234567890abcdef
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 1234567890abcdef0000000000000000
+
+dnl metadata match:
+# metadata=0x5555555555555555/0x5555555555555555
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 5555555555555555aaaaaaaaaaaaaaaa
+
+dnl metadata match:
+# metadata=0x1234000090ab0000/0xffff0000ffff0000
+# 74: 56 -> 00
+# 75: 78 -> 00
+# 78: cd -> 00
+# 79: ef -> 00
+0000 0058 00000000 000003ff dnl
+000000000000ffffffffffff 000000000000ffffffffffff dnl
+0000 00 00 0000 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
+00000000 00 000000 1234567890abcdef0000ffff0000ffff
+
+])
+sed '/^[[#&]]/d' < test-data > input.txt
+sed -n 's/^# //p; /^$/p' < test-data > expout
+sed -n 's/^& //p' < test-data > experr
+AT_CAPTURE_FILE([input.txt])
+AT_CAPTURE_FILE([expout])
+AT_CAPTURE_FILE([experr])
+AT_CHECK(
+ [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp11-match < input.txt],
+ [0], [expout], [experr])
+AT_CLEANUP
+
AT_SETUP([ovs-ofctl parse-nx-match loose])
AT_KEYWORDS([nx-match])
AT_DATA([nx-match.txt], [dnl
])
AT_CLEANUP
-dnl Check that "-F openflow10" rejects a flow_mod with a tun_id, since
-dnl OpenFlow 1.0 doesn't support tunnels.
-AT_SETUP([ovs-ofctl -F option and tun_id])
+AT_SETUP([ovs-ofctl parse-oxm])
+AT_KEYWORDS([oxm])
+AT_DATA([oxm.txt], [dnl
+<any>
+
+# in port
+OXM_OF_IN_PORT(00000000)
+OXM_OF_IN_PORT(fffffffe)
+
+# metadata
+OXM_OF_METADATA(5a5a5a5a5a5a5a5a)
+OXM_OF_METADATA_W(0000000000000000/00000000ffffffff)
+OXM_OF_METADATA_W(1234567890abcdef/ffff0000ffff0000)
+OXM_OF_METADATA_W(1234567890abcdef/ffffffffffffffff)
+OXM_OF_METADATA_W(1234567890abcdef/0000000000000000)
+
+# eth dst
+OXM_OF_ETH_DST(0002e30f80a4)
+OXM_OF_ETH_DST_W(010000000000/010000000000)
+OXM_OF_ETH_DST_W(000000000000/010000000000)
+OXM_OF_ETH_DST_W(ffffffffffff/010000000000)
+OXM_OF_ETH_DST_W(0002e30f80a4/ffffffffffff)
+OXM_OF_ETH_DST_W(0002e30f80a4/000000000000)
+OXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
+
+# eth src
+OXM_OF_ETH_SRC(020898456ddb)
+
+# eth type
+OXM_OF_ETH_TYPE(0800)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IN_PORT(00000012)
+
+# vlan
+OXM_OF_VLAN_VID(1009) OXM_OF_VLAN_VID(1009) # Duplicate Field
+OXM_OF_VLAN_VID(f009) # Bad Value
+OXM_OF_VLAN_PCP(00) # Bad Pre-Requisite
+OXM_OF_VLAN_VID(0000) # Packets without 802.1Q header or with VID=0
+OXM_OF_VLAN_VID(1123) # Packets with VID=123, any PCP
+OXM_OF_VLAN_VID(1123) OXM_OF_VLAN_PCP(01) # Packets with VID=123, PCP=1.
+OXM_OF_VLAN_VID(0123) # Does not make sense (but supported anyway)
+OXM_OF_VLAN_VID_W(0123/0123) # Does not make sense (but supported anyway)
+OXM_OF_VLAN_VID_W(1123/0123) # Does not make sense (but supported anyway)
+OXM_OF_VLAN_VID_W(0123/1123) # Does not make sense (but supported anyway)
+OXM_OF_VLAN_VID(0123) OXM_OF_VLAN_PCP(01) #Bad Pre-Requisite
+OXM_OF_VLAN_VID_W(1123/1fff) # Packets with VID=123, any PCP.
+OXM_OF_VLAN_VID_W(1123/ffff) # Packets with VID=123, any PCP.
+OXM_OF_VLAN_VID_W(1123/0000) # Packets with or without 802.1Q header
+OXM_OF_VLAN_VID_W(1123/1f0f), # Packets with # VID=123 (masked)
+OXM_OF_VLAN_VID_W(1123/1f0f), OXM_OF_VLAN_PCP(01) # Packets with VID=123 (masked), any PCP.
+OXM_OF_VLAN_VID_W(1000/1000) # Packets with any VID, any PCP
+OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01) # Packets with any VID, PCP=1.
+
+# IP ECN
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_ECN(03)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_ECN(06)
+OXM_OF_IP_ECN(03)
+
+# IP protocol
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(05)
+OXM_OF_IP_PROTO(05)
+
+# IP source
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC(ac100014)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC_W(C0a80000/FFFF0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC_W(C0a80000/FFFFFFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_SRC_W(C0a80000/00000000)
+OXM_OF_ETH_TYPE(0806) OXM_OF_IPV4_SRC(ac100014)
+OXM_OF_IPV4_SRC_W(C0D80000/FFFF0000)
+
+# IP destination
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST(ac100014)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST_W(C0a88012/FFFF0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST_W(C0a88012/FFFFFFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV4_DST_W(C0a88012/00000000)
+OXM_OF_IPV4_DST(ac100014)
+OXM_OF_ETH_TYPE(0806) OXM_OF_IPV4_DST_W(C0D80000/FFFF0000)
+
+# TCP source port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC(4231)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC_W(5050/F0F0)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC_W(5050/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_SRC_W(5050/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(07) OXM_OF_TCP_SRC(4231)
+
+# TCP destination port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST(4231)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST_W(FDE0/FFF0)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST_W(FDE0/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_TCP_DST_W(FDE0/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(07) OXM_OF_TCP_DST(4231)
+
+# UDP source port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC(8732)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC_W(0132/01FF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC_W(0132/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_SRC_W(0132/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_UDP_SRC(7823)
+
+# UDP destination port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST(1782)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/F00F)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(02) OXM_OF_UDP_DST(1293)
+
+# ICMP type
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01) OXM_OF_ICMPV4_TYPE(12)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(00) OXM_OF_ICMPV4_TYPE(10)
+
+# ICMP code
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01) OXM_OF_ICMPV4_CODE(12)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(00) OXM_OF_ICMPV4_CODE(10)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ICMPV4_CODE(10)
+OXM_OF_ICMPV4_CODE(00)
+
+# ARP opcode
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_OP(0001)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_OP(1111)
+OXM_OF_ETH_TYPE(0000) OXM_OF_ARP_OP(0001)
+OXM_OF_ARP_OP(0001)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_OP(0001) OXM_OF_ARP_OP(0001)
+
+# ARP source protocol address
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA(ac100014)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81234/FFFFFF00)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81234/FFFFFFFF)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81234/00000000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_SPA(ac100014)
+OXM_OF_ARP_SPA_W(C0D8fedc/FFFF0000)
+
+# ARP destination protocol address
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA(ac100014)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA_W(C0a812fe/FFFFFF00)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA_W(C0a812fe/FFFFFFFF)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA_W(C0a812fe/00000000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_TPA(ac100014)
+OXM_OF_ARP_TPA_W(C0D80000/FFFF0000)
+
+# ARP source hardware address
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_SHA(0002e30f80a4)
+OXM_OF_ARP_SHA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA_W(0002e30f80a4/ffffffffffff)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA_W(0002e30f80a4/000000000000)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SHA_W(0002e30f80a4/00000000000f)
+
+# ARP destination hardware address
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_THA(0002e30f80a4)
+OXM_OF_ARP_THA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA_W(0002e30f80a4/ffffffffffff)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA_W(0002e30f80a4/000000000000)
+OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_THA_W(0002e30f80a4/00000000000f)
+
+# IPv6 source
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC(20010db83c4d00010002000300040005)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_SRC(20010db83c4d00010002000300040005)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/00000000000000000000000000000000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+
+# IPv6 destination
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST(20010db83c4d00010002000300040005)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_DST(20010db83c4d00010002000300040005)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/00000000000000000000000000000000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+
+# IPv6 Flow Label
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL(1000000f)
+OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/0000000f)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/000fffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/000ffff0)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/100fffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/ffffffff)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IPV6_FLABEL_W(0000000f/00000000)
+
+# ND source hardware address
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3b) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+
+# ND destination hardware address
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(86dd) OXM_OF_IP_PROTO(3b) OXM_OF_ICMPV6_TYPE(87) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(3a) OXM_OF_ICMPV6_TYPE(88) OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005) OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+
+# Invalid field number.
+01020304(1111/2222)
+])
+AT_CHECK([ovs-ofctl --strict parse-oxm < oxm.txt], [0], [dnl
+<any>
+
+# in port
+OXM_OF_IN_PORT(00000000)
+OXM_OF_IN_PORT(fffffffe)
+
+# metadata
+OXM_OF_METADATA(5a5a5a5a5a5a5a5a)
+OXM_OF_METADATA_W(0000000000000000/00000000ffffffff)
+OXM_OF_METADATA_W(1234000090ab0000/ffff0000ffff0000)
+OXM_OF_METADATA(1234567890abcdef)
+<any>
+
+# eth dst
+OXM_OF_ETH_DST(0002e30f80a4)
+OXM_OF_ETH_DST_W(010000000000/010000000000)
+OXM_OF_ETH_DST_W(000000000000/010000000000)
+OXM_OF_ETH_DST_W(010000000000/010000000000)
+OXM_OF_ETH_DST(0002e30f80a4)
+<any>
+OXM_OF_ETH_DST_W(0002e30f80a4/feffffffffff)
+
+# eth src
+OXM_OF_ETH_SRC(020898456ddb)
+
+# eth type
+OXM_OF_ETH_TYPE(0800)
+OXM_OF_IN_PORT(00000012), OXM_OF_ETH_TYPE(0800)
+
+# vlan
+nx_pull_match() returned error OFPBMC_DUP_FIELD
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_VLAN_VID(0000)
+OXM_OF_VLAN_VID(1123)
+OXM_OF_VLAN_VID(1123), OXM_OF_VLAN_PCP(01)
+OXM_OF_VLAN_VID(0123)
+OXM_OF_VLAN_VID_W(0123/0123)
+OXM_OF_VLAN_VID_W(0123/0123)
+OXM_OF_VLAN_VID_W(0123/1123)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_VLAN_VID(1123)
+OXM_OF_VLAN_VID(1123)
+<any>
+OXM_OF_VLAN_VID_W(1103/1f0f)
+OXM_OF_VLAN_VID_W(1103/1f0f), OXM_OF_VLAN_PCP(01)
+OXM_OF_VLAN_VID_W(1000/1000)
+OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01)
+
+# IP ECN
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_ECN(03)
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IP protocol
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(05)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IP source
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_SRC(ac100014)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_SRC_W(c0a80000/ffff0000)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_SRC(c0a80000)
+OXM_OF_ETH_TYPE(0800)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IP destination
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_DST(ac100014)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_DST_W(c0a80000/ffff0000)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IPV4_DST(c0a88012)
+OXM_OF_ETH_TYPE(0800)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# TCP source port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_SRC(4231)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_SRC_W(5050/f0f0)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_SRC(5050)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# TCP destination port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_DST(4231)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_DST_W(fde0/fff0)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06), OXM_OF_TCP_DST(fde0)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(06)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# UDP source port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_SRC(8732)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_SRC_W(0132/01ff)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_SRC(0132)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# UDP destination port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST(1782)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST_W(5005/f00f)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST(5005)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ICMP type
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01), OXM_OF_ICMPV4_TYPE(12)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ICMP code
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01), OXM_OF_ICMPV4_CODE(12)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ARP opcode
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_OP(0001)
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_DUP_FIELD
+
+# ARP source protocol address
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SPA(ac100014)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SPA_W(c0a81200/ffffff00)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SPA(c0a81234)
+OXM_OF_ETH_TYPE(0806)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ARP destination protocol address
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_TPA(ac100014)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_TPA_W(c0a81200/ffffff00)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_TPA(c0a812fe)
+OXM_OF_ETH_TYPE(0806)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ARP source hardware address
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SHA(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SHA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0806)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_SHA_W(000000000004/00000000000f)
+
+# ARP destination hardware address
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_THA(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_THA(0002e30f80a4)
+OXM_OF_ETH_TYPE(0806)
+OXM_OF_ETH_TYPE(0806), OXM_OF_ARP_THA_W(000000000004/00000000000f)
+
+# IPv6 source
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_SRC(20010db83c4d00010002000300040005)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_SRC(20010db83c4d00010000000000000000)
+OXM_OF_ETH_TYPE(86dd)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IPv6 destination
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_DST(20010db83c4d00010002000300040005)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_DST(20010db83c4d00010000000000000000)
+OXM_OF_ETH_TYPE(86dd)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IPv6 Flow Label
+nx_pull_match() returned error OFPBMC_BAD_VALUE
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL_W(0000000f/0000000f)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL_W(00000000/000ffff0)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IPV6_FLABEL(0000000f)
+OXM_OF_ETH_TYPE(86dd)
+
+# ND source hardware address
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IP_PROTO(3a), OXM_OF_ICMPV6_TYPE(87), OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005), OXM_OF_IPV6_ND_SLL(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# ND destination hardware address
+OXM_OF_ETH_TYPE(86dd), OXM_OF_IP_PROTO(3a), OXM_OF_ICMPV6_TYPE(88), OXM_OF_IPV6_ND_TARGET(20010db83c4d00010002000300040005), OXM_OF_IPV6_ND_TLL(0002e30f80a4)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# Invalid field number.
+nx_pull_match() returned error OFPBMC_BAD_FIELD
+])
+AT_CLEANUP
+
+AT_SETUP([ovs-ofctl parse-oxm loose])
+AT_KEYWORDS([oxm])
+AT_DATA([oxm.txt], [dnl
+OXM_OF_IN_PORT(00000001), 01020304(1111/2222), OXM_OF_ETH_TYPE(0800)
+])
+
+AT_CHECK([ovs-ofctl --strict parse-oxm < oxm.txt], [0], [dnl
+nx_pull_match() returned error OFPBMC_BAD_FIELD
+])
+
+AT_CHECK([ovs-ofctl parse-oxm < oxm.txt], [0], [dnl
+OXM_OF_IN_PORT(00000001), OXM_OF_ETH_TYPE(0800)
+])
+AT_CLEANUP
+
+dnl Check all of the patterns mentioned in the "VLAN Matching" section
+dnl in the DESIGN file at top level.
+AT_SETUP([ovs-ofctl check-vlan])
+AT_KEYWORDS([VLAN])
+
+dnl [1]
+AT_CHECK([ovs-ofctl check-vlan 0000 0000], [0], [dnl
+ -> 0000/0000
+NXM: <any> -> 0000/0000
+OXM: <any> -> 0000/0000,--
+OF1.0: 0000/1,00/1 -> 0000/0000
+OF1.1: 0000/1,00/1 -> 0000/0000
+])
+
+dnl [2]
+AT_CHECK([ovs-ofctl check-vlan 0000 ffff], [0], [dnl
+vlan_tci=0x0000 -> 0000/ffff
+NXM: NXM_OF_VLAN_TCI(0000) -> 0000/ffff
+OXM: OXM_OF_VLAN_VID(0000) -> 0000/1fff,--
+OF1.0: ffff/0,00/1 -> 0000/ffff
+OF1.1: ffff/0,00/1 -> 0000/ffff
+])
+
+dnl [3]
+AT_CHECK([ovs-ofctl check-vlan 1abc 1fff], [0], [dnl
+dl_vlan=2748 -> 1abc/1fff
+NXM: NXM_OF_VLAN_TCI_W(1abc/1fff) -> 1abc/1fff
+OXM: OXM_OF_VLAN_VID(1abc) -> 1abc/1fff,--
+OF1.0: 0abc/0,00/1 -> 1abc/1fff
+OF1.1: 0abc/0,00/1 -> 1abc/1fff
+])
+
+dnl [4]
+AT_CHECK([ovs-ofctl check-vlan b000 f000], [0], [dnl
+dl_vlan_pcp=5 -> b000/f000
+NXM: NXM_OF_VLAN_TCI_W(b000/f000) -> b000/f000
+OXM: OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(05) -> 1000/1000,05
+OF1.0: 0000/1,05/0 -> b000/f000
+OF1.1: fffe/0,05/0 -> b000/f000
+])
+
+dnl [5]
+AT_CHECK([ovs-ofctl check-vlan babc ffff], [0], [dnl
+dl_vlan=2748,dl_vlan_pcp=5 -> babc/ffff
+NXM: NXM_OF_VLAN_TCI(babc) -> babc/ffff
+OXM: OXM_OF_VLAN_VID(1abc), OXM_OF_VLAN_PCP(05) -> 1abc/1fff,05
+OF1.0: 0abc/0,05/0 -> babc/ffff
+OF1.1: 0abc/0,05/0 -> babc/ffff
+])
+
+dnl [6]
+AT_CHECK([ovs-ofctl check-vlan 0000 0fff], [0], [dnl
+vlan_tci=0x0000/0x0fff -> 0000/0fff
+NXM: NXM_OF_VLAN_TCI_W(0000/0fff) -> 0000/0fff
+OXM: OXM_OF_VLAN_VID_W(0000/0fff) -> 0000/0fff,--
+OF1.0: 0000/0,00/1 -> 1000/1fff
+OF1.1: 0000/0,00/1 -> 1000/1fff
+])
+
+dnl [7]
+AT_CHECK([ovs-ofctl check-vlan 0000 f000], [0], [dnl
+vlan_tci=0x0000/0xf000 -> 0000/f000
+NXM: NXM_OF_VLAN_TCI_W(0000/f000) -> 0000/f000
+OXM: OXM_OF_VLAN_VID_W(0000/1000) -> 0000/1000,--
+OF1.0: ffff/0,00/1 -> 0000/ffff
+OF1.1: ffff/0,00/1 -> 0000/ffff
+])
+
+dnl [8]
+AT_CHECK([ovs-ofctl check-vlan 0000 efff], [0], [dnl
+vlan_tci=0x0000/0xefff -> 0000/efff
+NXM: NXM_OF_VLAN_TCI_W(0000/efff) -> 0000/efff
+OXM: OXM_OF_VLAN_VID_W(0000/0fff) -> 0000/0fff,--
+OF1.0: 0000/0,00/0 -> 1000/ffff
+OF1.1: 0000/0,00/0 -> 1000/ffff
+])
+
+dnl [9]
+AT_CHECK([ovs-ofctl check-vlan 1001 1001], [0], [dnl
+vlan_tci=0x1001/0x1001 -> 1001/1001
+NXM: NXM_OF_VLAN_TCI_W(1001/1001) -> 1001/1001
+OXM: OXM_OF_VLAN_VID_W(1001/1001) -> 1001/1001,--
+OF1.0: 0001/0,00/1 -> 1001/1fff
+OF1.1: 0001/0,00/1 -> 1001/1fff
+])
+
+dnl [10]
+AT_CHECK([ovs-ofctl check-vlan 3000 3000], [0], [dnl
+vlan_tci=0x3000/0x3000 -> 3000/3000
+NXM: NXM_OF_VLAN_TCI_W(3000/3000) -> 3000/3000
+OXM: OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01) -> 1000/1000,01
+OF1.0: 0000/1,01/0 -> 3000/f000
+OF1.1: fffe/0,01/0 -> 3000/f000
+])
+AT_CHECK
+AT_CLEANUP
+
+dnl Check that "-F openflow10" rejects a flow_mod with unsupported features,
+dnl such as tunnels and metadata.
+AT_SETUP([ovs-ofctl -F option and NXM features])
AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy tun_id=123,actions=drop],
[1], [], [ovs-ofctl: none of the usable flow formats (NXM) is among the allowed flow formats (OpenFlow10)
])
+AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy metadata=123,actions=drop],
+ [1], [], [ovs-ofctl: none of the usable flow formats (NXM) is among the allowed flow formats (OpenFlow10)
+])
AT_CLEANUP
dnl Check that "-F nxm" really forces add-flow to use the NXM flow format.
])
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+dnl Check that --sort and --rsort works with dump-flows
+dnl Default field is 'priority'. Flow entries are displayed based
+dnl on field to sort.
+AT_SETUP([ovs-ofctl dump-flows with sorting])
+OVS_VSWITCHD_START
+AT_KEYWORDS([sort])
+AT_DATA([allflows.txt], [[
+priority=4,in_port=23213 actions=output:42
+priority=5,in_port=1029 actions=output:43
+priority=7,in_port=1029 actions=output:43
+priority=3,in_port=1028 actions=output:44
+priority=1,in_port=1026 actions=output:45
+priority=6,in_port=1027 actions=output:64
+priority=2,in_port=1025 actions=output:47
+priority=8,tcp,tp_src=5 actions=drop
+priority=9,tcp,tp_src=6 actions=drop
+]])
+
+AT_CHECK([ovs-ofctl add-flows br0 allflows.txt
+], [0], [ignore])
+AT_CHECK([ovs-ofctl --sort dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=1,in_port=1026 actions=output:45
+ priority=2,in_port=1025 actions=output:47
+ priority=3,in_port=1028 actions=output:44
+ priority=4,in_port=23213 actions=output:42
+ priority=5,in_port=1029 actions=output:43
+ priority=6,in_port=1027 actions=output:64
+ priority=7,in_port=1029 actions=output:43
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=9,tcp,tp_src=6 actions=drop
+])
+AT_CHECK([ovs-ofctl --rsort dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=7,in_port=1029 actions=output:43
+ priority=6,in_port=1027 actions=output:64
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+ priority=3,in_port=1028 actions=output:44
+ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+])
+AT_CHECK([ovs-ofctl --sort=in_port dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+ priority=6,in_port=1027 actions=output:64
+ priority=3,in_port=1028 actions=output:44
+ priority=7,in_port=1029 actions=output:43
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=8,tcp,tp_src=5 actions=drop
+])
+AT_CHECK([ovs-ofctl --rsort=in_port dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=4,in_port=23213 actions=output:42
+ priority=7,in_port=1029 actions=output:43
+ priority=5,in_port=1029 actions=output:43
+ priority=3,in_port=1028 actions=output:44
+ priority=6,in_port=1027 actions=output:64
+ priority=1,in_port=1026 actions=output:45
+ priority=2,in_port=1025 actions=output:47
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=8,tcp,tp_src=5 actions=drop
+])
+AT_CHECK([ovs-ofctl --sort=tcp_src dump-flows br0 | ofctl_strip], [0], [dnl
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=9,tcp,tp_src=6 actions=drop
+ priority=7,in_port=1029 actions=output:43
+ priority=6,in_port=1027 actions=output:64
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+ priority=3,in_port=1028 actions=output:44
+ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+])
+AT_CHECK(
+ [ovs-ofctl --sort=in_port --sort=tcp_src dump-flows br0 | ofctl_strip], [0],
+ [ priority=2,in_port=1025 actions=output:47
+ priority=1,in_port=1026 actions=output:45
+ priority=6,in_port=1027 actions=output:64
+ priority=3,in_port=1028 actions=output:44
+ priority=7,in_port=1029 actions=output:43
+ priority=5,in_port=1029 actions=output:43
+ priority=4,in_port=23213 actions=output:42
+ priority=8,tcp,tp_src=5 actions=drop
+ priority=9,tcp,tp_src=6 actions=drop
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ovs-ofctl diff-flows])
+OVS_VSWITCHD_START
+
+# Add tons of flows to br0.
+for i in `seq 0 1023`; do echo "dl_vlan=$i,actions=drop"; done > add-flows.txt
+AT_CHECK([ovs-ofctl add-flows br0 add-flows.txt])
+
+# Dump them and compare against what we expect by hand, then with diff-flows.
+for i in `seq 0 1023`; do echo " dl_vlan=$i actions=drop"; done | sort > expout
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sed '/NXST_FLOW/d' | sort],
+ [0], [expout])
+AT_CHECK([ovs-ofctl diff-flows br0 add-flows.txt])
+
+# Remove even-numbered flows, compare again.
+for i in `seq 0 1023 2`; do echo "dl_vlan=$i"; done > del-flows.txt
+AT_CHECK([ovs-ofctl del-flows br0 - < del-flows.txt])
+for i in `seq 0 1023 2`; do echo "+dl_vlan=$i actions=drop"; done | sort > expout
+AT_CHECK([ovs-ofctl diff-flows br0 add-flows.txt | sort], [0], [expout])
+for i in `seq 0 1023 2`; do echo "-dl_vlan=$i actions=drop"; done | sort > expout
+AT_CHECK([ovs-ofctl diff-flows add-flows.txt br0 | sort], [0], [expout])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl ofpacts that differ bytewise don't necessarily differ when
+dnl converted to another representation, such as OpenFlow 1.0
+dnl or to a string. "resubmit(,1)" is an example of an action
+dnl of this type: "ofpact_resubmit"s can differ in their "compat"
+dnl values even though this doesn't affect the string format.
+dnl
+dnl This test checks that "ovs-ofctl diff-flows" doesn't report
+dnl false ofpacts differences.
+AT_SETUP([ovs-ofctl diff-flows - suppress false differences])
+OVS_VSWITCHD_START
+AT_DATA([flows.txt], [actions=resubmit(,1)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl diff-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl add-flow br0 idle_timeout=60,dl_vlan=9,actions=output:1])
+AT_CHECK([ovs-ofctl diff-flows br0 flows.txt], [2], [dnl
+-dl_vlan=9 idle_timeout=60 actions=output:1
+])
+AT_CHECK([ovs-ofctl add-flow br0 hard_timeout=120,cookie=1234,dl_vlan=9,actions=output:1])
+AT_CHECK([ovs-ofctl diff-flows flows.txt br0], [2], [dnl
++dl_vlan=9 cookie=0x4d2 hard_timeout=120 actions=output:1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
dnl an ovsdb-server on it for ovs-vsctl to connect to.
m4_define([OVS_VSCTL_SETUP],
[OVSDB_INIT([db])
- AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db >/dev/null 2>&1], [0], [ignore], [ignore])])
+ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db >/dev/null 2>&1], [0], [ignore], [ignore])])
dnl OVS_VSCTL_CLEANUP
dnl
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], ...)
AT_SETUP([database commands -- conditions])
AT_KEYWORDS([ovs-vsctl])
-trap 'kill `cat pid`' 0
+ON_EXIT([kill `cat pid`])
OVS_VSCTL_SETUP
AT_CHECK(
[RUN_OVS_VSCTL_TOGETHER(
])
m4_define([VSCTL_CHECK_FIND],
- [AT_CHECK([ovs-vsctl --bare --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket -- --columns=name find bridge '$1' | sort | xargs echo], [0], [$2
+ [AT_CHECK([echo `ovs-vsctl --bare --timeout=5 --no-wait -vreconnect:emer --db=unix:socket -- --columns=name find bridge '$1' | sort`], [0], [$2
])])
# Arithmetic relational operators without keys.
AT_SETUP([database commands -- wait-until must wait])
AT_KEYWORDS([ovs-vsctl])
-# Disable lcov for this test. All the programs running in parallel
-# race badly on access to profiling data.
-DISABLE_LCOV=true
-export DISABLE_LCOV
-
OVS_VSCTL_SETUP
# Start ovs-vsctls in background.
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 \
]], [ignore], [test ! -e pid || kill `cat pid`])
OVS_VSCTL_CLEANUP
AT_CLEANUP
+
+dnl This test will create a linux-htb QoS record that
+dnl points to a few queues and use it on a1 and a2 port.
+dnl It also destroys all records from Qos and Queue table.
+AT_SETUP([--all option on destroy command])
+AT_KEYWORDS([ovs-vsctl])
+OVS_VSCTL_SETUP
+AT_CHECK([RUN_OVS_VSCTL(
+ [add-br a],
+ [add-port a a1],
+ [add-port a a2])], [0], [], [], [OVS_VSCTL_CLEANUP])
+CHECK_BRIDGES([a, a, 0])
+CHECK_PORTS([a], [a1], [a2])
+CHECK_IFACES([a], [a1], [a2])
+AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
+ [set Port a1 qos=@newqos],
+ [set Port a2 qos=@newqos],
+ [--id=@newqos create QoS type=linux-htb other-config:max-rate=1000000000 queues=0=@q0,1=@q1],
+ [--id=@q0 create Queue other-config:min-rate=100000000 other-config:max-rate=100000000],
+ [--id=@q1 create Queue other-config:min-rate=500000000])], [0], [ignore], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+ [--columns=other_config,type list Qos])], [0],
+[other_config : {max-rate="1000000000"}
+type : linux-htb
+], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+ [--columns=other_config list Queue | sort | xargs echo])], [0],
+[other_config : {max-rate=100000000, min-rate=100000000} other_config : {min-rate=500000000}
+], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+ [clear Port a1 qos],
+ [clear Port a2 qos])], [0], [], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+ [--columns=qos list Port a1 a2])], [0],
+[[qos : []
+
+qos : []
+]], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+ [--all destroy Qos])], [0], [], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+ [-- list Qos])], [0], [], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+ [--all destroy Queue])], [0], [], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL(
+ [-- list Queue])], [0], [], [], [OVS_VSCTL_CLEANUP])
+OVS_VSCTL_CLEANUP
+AT_CLEANUP
export PYTHONPATH
OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
OVS_PKGDATADIR=`pwd`; export OVS_PKGDATADIR
cp "$top_srcdir/vswitchd/vswitch.ovsschema" .
cp "$top_srcdir/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync" \
ovs-xapi-sync
-trap 'kill `cat pid ovs-xapi-sync.pid`' 0
+ON_EXIT([kill `cat pid ovs-xapi-sync.pid`])
mkdir var var/run
touch var/run/xapi_init_complete.cookie
ovs_vsctl () {
- ovs-vsctl --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket "$@"
+ ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket "$@"
}
# Start ovsdb-server.
AT_KEYWORDS([ovsdb server idl positive $5])
AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
[0], [stdout], [ignore])
- AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+ AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
m4_if([$2], [], [],
[AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore], [kill `cat pid`])])
AT_CHECK([test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 idl unix:socket $3],
AT_KEYWORDS([ovsdb server idl positive Python $5])
AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
[0], [stdout], [ignore])
- AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+ AT_CHECK([ovsdb-server '-vPATTERN:console:ovsdb-server|%c|%m' --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
m4_if([$2], [], [],
[AT_CHECK([ovsdb-client transact unix:socket $2], [0], [ignore], [ignore], [kill `cat pid`])])
AT_CHECK([$PYTHON $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema unix:socket $3],
m4_foreach([txn], [$3],
[AT_CHECK([ovsdb-tool transact db 'txn'], [0], [ignore], [ignore])])
AT_CAPTURE_FILE([ovsdb-server-log])
- AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/server-pid --remote=punix:socket --unixctl="`pwd`"/unixctl --log-file="`pwd`"/ovsdb-server-log db >/dev/null 2>&1],
+ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/server-pid --remote=punix:socket --unixctl="`pwd`"/unixctl --log-file="`pwd`"/ovsdb-server-log db >/dev/null 2>&1],
[0], [], [])
- AT_CHECK([ovsdb-client -vjsonrpc --detach --pidfile="`pwd`"/client-pid -d json monitor --format=csv unix:socket $4 $5 $8 > output],
+ AT_CHECK([ovsdb-client -vjsonrpc --detach --no-chdir --pidfile="`pwd`"/client-pid -d json monitor --format=csv unix:socket $4 $5 $8 > output],
[0], [ignore], [ignore], [kill `cat server-pid`])
m4_foreach([txn], [$6],
[AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0],
AT_KEYWORDS([ovsdb server positive unix $5])
$2 > schema
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
- AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
m4_foreach([txn], [$3],
[AT_CHECK([ovsdb-client transact unix:socket 'txn'], [0], [stdout], [ignore],
[test ! -e pid || kill `cat pid`])
AT_KEYWORDS([ovsdb server positive])
ordinal_schema > schema
AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
AT_CHECK([ovsdb-client get-schema-version unix:socket ordinals], [0], [5.1.3
])
OVSDB_SERVER_SHUTDOWN
AT_KEYWORDS([ovsdb server positive])
ordinal_schema > schema
AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
AT_CHECK(
[[ovsdb-client list-dbs unix:socket]],
[0], [ordinals
{"op": "insert",
"table": "Manager",
"row": {"manager": "punix:socket"}}]']], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --remote=db:Manager,manager --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=db:Manager,manager --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
AT_CHECK(
[[ovsdb-client transact unix:socket \
'["mydb",
AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
SSL_PORT=`cat stdout`
AT_CHECK(
- [ovsdb-server --detach --pidfile="`pwd`"/pid \
+ [ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid \
--private-key=db:SSL,private_key \
--certificate=db:SSL,certificate \
--ca-cert=db:SSL,ca_cert \
AT_SETUP([compacting online])
AT_KEYWORDS([ovsdb server compact])
ordinal_schema > schema
-touch .db.~lock~
+dnl Make sure that "ovsdb-tool create" works with a dangling symlink for
+dnl the database and the lockfile, creating the target of each symlink rather
+dnl than replacing the symlinks with regular files.
+mkdir dir
+ln -s dir/db db
+ln -s dir/.db.~lock~ .db.~lock~
+AT_SKIP_IF([test ! -h db || test ! -h .db.~lock~])
AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket --log-file="`pwd`"/ovsdb-server.log db], [0], [ignore], [ignore])
+dnl Start ovsdb-server.
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket --log-file="`pwd`"/ovsdb-server.log db], [0], [ignore], [ignore])
AT_CAPTURE_FILE([ovsdb-server.log])
dnl Do a bunch of random transactions that put crap in the database log.
AT_CHECK(
dnl Now compact the database in-place.
AT_CHECK([[ovs-appctl -t "`pwd`"/unixctl ovsdb-server/compact]],
[0], [], [ignore], [test ! -e pid || kill `cat pid`])
+dnl Make sure that "db" is still a symlink to dir/db instead of getting
+dnl replaced by a regular file, ditto for .db.~lock~.
+AT_CHECK([test -h db])
+AT_CHECK([test -h .db.~lock~])
+AT_CHECK([test -f dir/db])
+AT_CHECK([test -f dir/.db.~lock~])
dnl We can't fully re-check the contents of the database log, because the
dnl order of the records is not predictable, but there should only be 4 lines
dnl in it now.
AT_CAPTURE_FILE([db])
-AT_CHECK([wc -l < db], [0], [4
-], [], [test ! -e pid || kill `cat pid`])
+AT_CHECK([test `wc -l < db` -eq 4], [0], [], [],
+ [test ! -e pid || kill `cat pid`])
dnl And check that the dumped data is the same too:
AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout], [ignore],
[test ! -e pid || kill `cat pid`])
[0], [[[{"count":3}]
]], [ignore], [test ! -e pid || kill `cat pid`])
dnl There should be 6 lines in the log now.
-AT_CHECK([wc -l < db], [0], [6
-], [], [test ! -e pid || kill `cat pid`])
+AT_CHECK([test `wc -l < db` -eq 6], [0], [], [],
+ [test ! -e pid || kill `cat pid`])
dnl Then check that the dumped data is correct.
AT_CHECK([ovsdb-client dump unix:socket ordinals], [0], [stdout], [ignore],
[test ! -e pid || kill `cat pid`])
SSL_PORT=`cat stdout`
PKIDIR=$abs_top_builddir/tests
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
- AT_CHECK([ovsdb-server --detach --pidfile="`pwd`"/pid --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:$SSL_PORT:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --private-key=$PKIDIR/testpki-privkey2.pem --certificate=$PKIDIR/testpki-cert2.pem --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:$SSL_PORT:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
m4_foreach([txn], [$3],
[AT_CHECK([ovsdb-client --private-key=$PKIDIR/testpki-privkey.pem --certificate=$PKIDIR/testpki-cert.pem --ca-cert=$PKIDIR/testpki-cacert.pem transact ssl:127.0.0.1:$SSL_PORT 'txn'], [0], [stdout], [ignore],
[test ! -e pid || kill `cat pid`])
AT_SETUP([ovsdb-tool compact])
AT_KEYWORDS([ovsdb file positive])
ordinal_schema > schema
-touch .db.~lock~
+dnl Make sure that "ovsdb-tool create" works with a dangling symlink,
+dnl creating the target of the symlink rather than replacing the symlink
+dnl with a regular file, and that the lockfile gets created relative to
+dnl the symlink's target.
+mkdir dir
+: > dir/.db.~lock~
+ln -s dir/db db
+AT_SKIP_IF([test ! -h db])
AT_CHECK([ovsdb-tool create db schema], [0], [], [ignore])
+AT_CHECK([test ! -e .db.~lock])
+AT_CHECK([test -h db])
+AT_CHECK([test -f dir/db])
dnl Do a bunch of random transactions that put crap in the database log.
AT_CHECK(
[[for pair in 'zero 0' 'one 1' 'two 2' 'three 3' 'four 4' 'five 5'; do
dnl Now compact the database in-place.
touch .db.tmp.~lock~
AT_CHECK([[ovsdb-tool compact db]], [0], [], [ignore])
+dnl Make sure that "db" is still a symlink to dir/db instead of getting
+dnl replaced by a regular file.
+AT_CHECK([test ! -e .db.~lock])
+AT_CHECK([test -h db])
+AT_CHECK([test -f dir/db])
dnl We can't fully re-check the contents of the database log, because the
dnl order of the records is not predictable, but there should only be 4 lines
dnl in it now.
AT_CAPTURE_FILE([db])
-AT_CHECK([wc -l < db], [0], [4
-])
+AT_CHECK([test `wc -l < db` -eq 4])
dnl And check that the dumped data is the same too:
AT_CHECK([[ovsdb-server --unixctl="`pwd`"/unixctl --remote=punix:socket --run "ovsdb-client dump unix:socket ordinals" db]],
[0], [stdout], [ignore])
dnl order of the records is not predictable, but there should only be 4 lines
dnl in it now.
AT_CAPTURE_FILE([db])
-AT_CHECK([wc -l < db], [0], [4
-])
+AT_CHECK([test `wc -l < db` -eq 4])
dnl And check that the dumped data is the same except for the removed column:
AT_CHECK([[ovsdb-server --unixctl="`pwd`"/unixctl --remote=punix:socket --run "ovsdb-client dump unix:socket ordinals" db]],
[0], [stdout], [ignore])
dnl order of the records is not predictable, but there should only be 4 lines
dnl in it now.
AT_CAPTURE_FILE([db])
-AT_CHECK([wc -l < db], [0], [4
-])
+AT_CHECK([test `wc -l < db` -eq 4])
dnl And check that the dumped data is the same except for the added column:
AT_CHECK([[ovsdb-server --unixctl="`pwd`"/unixctl --remote=punix:socket --run "ovsdb-client dump unix:socket ordinals" db]],
[0], [stdout], [ignore])
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2011 Nicira Networks.
+/* Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdlib.h>
#include "flow.h"
+#include "ofp-actions.h"
#include "ofpbuf.h"
#include "random.h"
return slave ? slave->enabled : false;
}
-static struct nx_action_bundle *
+static struct ofpact_bundle *
parse_bundle_actions(char *actions)
{
- struct nx_action_bundle *nab;
- struct ofpbuf b;
+ struct ofpact_bundle *bundle;
+ struct ofpbuf ofpacts;
+ struct ofpact *action;
- ofpbuf_init(&b, 0);
- bundle_parse_load(&b, actions);
- nab = ofpbuf_steal_data(&b);
- ofpbuf_uninit(&b);
+ ofpbuf_init(&ofpacts, 0);
+ bundle_parse_load(actions, &ofpacts);
+ action = ofpacts.data;
+ bundle = ofpact_get_BUNDLE(xmemdup(action, action->len));
+ ofpbuf_uninit(&ofpacts);
- if (ntohs(nab->n_slaves) > MAX_SLAVES) {
+ if (bundle->n_slaves > MAX_SLAVES) {
ovs_fatal(0, "At most %u slaves are supported", MAX_SLAVES);
}
- return nab;
+ return bundle;
}
static const char *
main(int argc, char *argv[])
{
bool ok = true;
- struct nx_action_bundle *nab;
+ struct ofpact_bundle *bundle;
struct flow *flows;
size_t i, n_permute, old_n_enabled;
struct slave_group sg;
ovs_fatal(0, "usage: %s bundle_action", program_name);
}
- nab = parse_bundle_actions(argv[1]);
+ bundle = parse_bundle_actions(argv[1]);
/* Generate 'slaves' array. */
sg.n_slaves = 0;
- for (i = 0; i < ntohs(nab->n_slaves); i++) {
- uint16_t slave_id = bundle_get_slave(nab, i);
+ for (i = 0; i < bundle->n_slaves; i++) {
+ uint16_t slave_id = bundle->slaves[i];
if (slave_lookup(&sg, slave_id)) {
ovs_fatal(0, "Redundant slaves are not supported. ");
flows[i].regs[0] = OFPP_NONE;
}
- if (bundle_check(nab, 1024, flows)) {
- ovs_fatal(0, "Bundle action fails to check.");
- }
-
/* Cycles through each possible liveness permutation for the given
* n_slaves. The initial state is equivalent to all slaves down, so we
* skip it by starting at i = 1. We do one extra iteration to cover
uint16_t old_slave_id, ofp_port;
old_slave_id = flow->regs[0];
- ofp_port = bundle_execute(nab, flow, slave_enabled_cb, &sg);
- bundle_execute_load(nab, flow, slave_enabled_cb, &sg);
- if (flow->regs[0] != ofp_port) {
- ovs_fatal(0, "bundle_execute_load() and bundle_execute() "
- "disagree");
- }
+ ofp_port = bundle_execute(bundle, flow, slave_enabled_cb, &sg);
+ flow->regs[0] = ofp_port;
- if (flow->regs[0] != OFPP_NONE) {
- slave_lookup(&sg, flow->regs[0])->flow_count++;
+ if (ofp_port != OFPP_NONE) {
+ slave_lookup(&sg, ofp_port)->flow_count++;
}
- if (old_slave_id != flow->regs[0]) {
+ if (old_slave_id != ofp_port) {
changed++;
}
}
- if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) {
+ if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) {
perfect = active == old_active ? 0.0 : 1.0;
} else {
if (old_n_enabled || n_enabled) {
if (slave->enabled) {
double perfect_fp;
- if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) {
+ if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) {
perfect_fp = j == active ? 1.0 : 0.0;
} else {
perfect_fp = 1.0 / n_enabled;
old_n_enabled = n_enabled;
}
- free(nab);
+ free(bundle);
free(flows);
return ok ? 0 : 1;
}
/*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* FWW_* bit(s) member name name */ \
/* -------------------------- ----------- -------- */ \
CLS_FIELD(0, tun_id, TUN_ID) \
+ CLS_FIELD(0, metadata, METADATA) \
CLS_FIELD(0, nw_src, NW_SRC) \
CLS_FIELD(0, nw_dst, NW_DST) \
CLS_FIELD(FWW_IN_PORT, in_port, IN_PORT) \
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)
eq = !((fixed->tp_src ^ wild->flow.tp_src) & wild->wc.tp_src_mask);
} else if (f_idx == CLS_F_IDX_TP_DST) {
eq = !((fixed->tp_dst ^ wild->flow.tp_dst) & wild->wc.tp_dst_mask);
+ } else if (f_idx == CLS_F_IDX_DL_SRC) {
+ eq = eth_addr_equal_except(fixed->dl_src, wild->flow.dl_src,
+ wild->wc.dl_src_mask);
+ } else if (f_idx == CLS_F_IDX_DL_DST) {
+ eq = eth_addr_equal_except(fixed->dl_dst, wild->flow.dl_dst,
+ wild->wc.dl_dst_mask);
} else if (f_idx == CLS_F_IDX_VLAN_TCI) {
eq = !((fixed->vlan_tci ^ wild->flow.vlan_tci)
& wild->wc.vlan_tci_mask);
} else if (f_idx == CLS_F_IDX_TUN_ID) {
eq = !((fixed->tun_id ^ wild->flow.tun_id) & wild->wc.tun_id_mask);
+ } else if (f_idx == CLS_F_IDX_METADATA) {
+ eq = !((fixed->metadata ^ wild->flow.metadata)
+ & wild->wc.metadata_mask);
} else if (f_idx == CLS_F_IDX_NW_DSCP) {
eq = !((fixed->nw_tos ^ wild->flow.nw_tos) & IP_DSCP_MASK);
} else {
static ovs_be64 tun_id_values[] = {
0,
CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) };
+static ovs_be64 metadata_values[] = {
+ 0,
+ CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) };
static uint16_t in_port_values[] = { 1, OFPP_LOCAL };
static ovs_be16 vlan_tci_values[] = { CONSTANT_HTONS(101), CONSTANT_HTONS(0) };
static ovs_be16 dl_type_values[]
values[CLS_F_IDX_TUN_ID][0] = &tun_id_values[0];
values[CLS_F_IDX_TUN_ID][1] = &tun_id_values[1];
+ values[CLS_F_IDX_METADATA][0] = &metadata_values[0];
+ values[CLS_F_IDX_METADATA][1] = &metadata_values[1];
+
values[CLS_F_IDX_IN_PORT][0] = &in_port_values[0];
values[CLS_F_IDX_IN_PORT][1] = &in_port_values[1];
#define N_NW_SRC_VALUES ARRAY_SIZE(nw_src_values)
#define N_NW_DST_VALUES ARRAY_SIZE(nw_dst_values)
#define N_TUN_ID_VALUES ARRAY_SIZE(tun_id_values)
+#define N_METADATA_VALUES ARRAY_SIZE(metadata_values)
#define N_IN_PORT_VALUES ARRAY_SIZE(in_port_values)
#define N_VLAN_TCI_VALUES ARRAY_SIZE(vlan_tci_values)
#define N_DL_TYPE_VALUES ARRAY_SIZE(dl_type_values)
flow.nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)];
flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)];
flow.tun_id = tun_id_values[get_value(&x, N_TUN_ID_VALUES)];
+ flow.metadata = metadata_values[get_value(&x, N_METADATA_VALUES)];
flow.in_port = in_port_values[get_value(&x, N_IN_PORT_VALUES)];
flow.vlan_tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)];
flow.dl_type = dl_type_values[get_value(&x, N_DL_TYPE_VALUES)];
rule->cls_rule.wc.tp_src_mask = htons(UINT16_MAX);
} else if (f_idx == CLS_F_IDX_TP_DST) {
rule->cls_rule.wc.tp_dst_mask = htons(UINT16_MAX);
+ } else if (f_idx == CLS_F_IDX_DL_SRC) {
+ memset(rule->cls_rule.wc.dl_src_mask, 0xff, ETH_ADDR_LEN);
+ } else if (f_idx == CLS_F_IDX_DL_DST) {
+ memset(rule->cls_rule.wc.dl_dst_mask, 0xff, ETH_ADDR_LEN);
} else if (f_idx == CLS_F_IDX_VLAN_TCI) {
rule->cls_rule.wc.vlan_tci_mask = htons(UINT16_MAX);
} else if (f_idx == CLS_F_IDX_TUN_ID) {
rule->cls_rule.wc.tun_id_mask = htonll(UINT64_MAX);
+ } else if (f_idx == CLS_F_IDX_METADATA) {
+ rule->cls_rule.wc.metadata_mask = htonll(UINT64_MAX);
} else {
NOT_REACHED();
}
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-# Copyright (c) 2010, 2011 Nicira Networks.
+# Copyright (c) 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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;
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;
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");
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2012 Nicira Networks.
+ * Copyright (c) 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010 Nicira Networks.
+ * Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-# Copyright (c) 2009, 2010 Nicira Networks.
+# Copyright (c) 2009, 2010 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-# Copyright (c) 2009, 2010, 2011 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <errno.h>
#include <stdlib.h>
+#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
{
struct lockfile *lockfile;
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
lockfile_unlock(lockfile);
}
{
struct lockfile *lockfile;
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
lockfile_unlock(lockfile);
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
lockfile_unlock(lockfile);
}
{
struct lockfile *lockfile;
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
- CHECK(lockfile_lock("file", 0, &lockfile), EDEADLK);
+ CHECK(lockfile_lock("file", &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), EDEADLK);
lockfile_unlock(lockfile);
}
{
struct lockfile *lockfile;
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
- CHECK(lockfile_lock("file", 0, &lockfile), EDEADLK);
- CHECK(lockfile_lock("file", 0, &lockfile), EDEADLK);
+ CHECK(lockfile_lock("file", &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), EDEADLK);
+ CHECK(lockfile_lock("file", &lockfile), EDEADLK);
lockfile_unlock(lockfile);
}
* this function that does the wait() call. */
static struct lockfile *lockfile;
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
if (do_fork() == CHILD) {
lockfile_unlock(lockfile);
- CHECK(lockfile_lock("file", 0, &lockfile), EAGAIN);
+ CHECK(lockfile_lock("file", &lockfile), EAGAIN);
exit(11);
}
}
{
struct lockfile *lockfile, *dummy;
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
- CHECK(lockfile_lock("file", 0, &dummy), EDEADLK);
+ CHECK(lockfile_lock("file", &lockfile), 0);
+ CHECK(lockfile_lock("file", &dummy), EDEADLK);
if (do_fork() == CHILD) {
- CHECK(lockfile_lock("file", 0, &dummy), EAGAIN);
+ CHECK(lockfile_lock("file", &dummy), EAGAIN);
exit(11);
}
}
{
struct lockfile *lockfile;
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
lockfile_unlock(lockfile);
if (do_fork() == CHILD) {
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
+ CHECK(lockfile_lock("file", &lockfile), 0);
exit(11);
}
}
static void
-run_lock_timeout_gets_the_lock(void)
+run_lock_multiple(void)
{
- struct lockfile *lockfile;
+ struct lockfile *a, *b, *c, *dummy;
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
+ CHECK(lockfile_lock("a", &a), 0);
+ CHECK(lockfile_lock("b", &b), 0);
+ CHECK(lockfile_lock("c", &c), 0);
- if (do_fork() == CHILD) {
- lockfile_unlock(lockfile);
- CHECK(lockfile_lock("file", TIME_UPDATE_INTERVAL * 3, &lockfile), 0);
- exit(11);
- } else {
- long long int now = time_msec();
- while (time_msec() < now + TIME_UPDATE_INTERVAL) {
- pause();
- }
- lockfile_unlock(lockfile);
- }
+ lockfile_unlock(a);
+ CHECK(lockfile_lock("a", &a), 0);
+ CHECK(lockfile_lock("a", &dummy), EDEADLK);
+ lockfile_unlock(a);
+
+ lockfile_unlock(b);
+ CHECK(lockfile_lock("a", &a), 0);
+
+ lockfile_unlock(c);
+ lockfile_unlock(a);
}
+/* Checks that locking a dangling symlink works OK. (It used to hang.) */
static void
-run_lock_timeout_runs_out(void)
+run_lock_symlink(void)
{
- struct lockfile *lockfile;
+ struct lockfile *a, *b, *dummy;
+ struct stat s;
+
+ /* Create a symlink .a.~lock~ pointing to .b.~lock~. */
+ CHECK(symlink(".b.~lock~", ".a.~lock~"), 0);
+ CHECK(lstat(".a.~lock~", &s), 0);
+ CHECK(S_ISLNK(s.st_mode) != 0, 1);
+ CHECK(stat(".a.~lock~", &s), -1);
+ CHECK(errno, ENOENT);
+ CHECK(stat(".b.~lock~", &s), -1);
+ CHECK(errno, ENOENT);
+
+ CHECK(lockfile_lock("a", &a), 0);
+ CHECK(lockfile_lock("a", &dummy), EDEADLK);
+ CHECK(lockfile_lock("b", &dummy), EDEADLK);
+ lockfile_unlock(a);
- CHECK(lockfile_lock("file", 0, &lockfile), 0);
+ CHECK(lockfile_lock("b", &b), 0);
+ CHECK(lockfile_lock("b", &dummy), EDEADLK);
+ CHECK(lockfile_lock("a", &dummy), EDEADLK);
+ lockfile_unlock(b);
- if (do_fork() == CHILD) {
- lockfile_unlock(lockfile);
- CHECK(lockfile_lock("file", TIME_UPDATE_INTERVAL, &lockfile),
- ETIMEDOUT);
- exit(11);
- } else {
- long long int now = time_msec();
- while (time_msec() < now + TIME_UPDATE_INTERVAL * 3) {
- pause();
- }
- lockfile_unlock(lockfile);
- }
+ CHECK(lstat(".a.~lock~", &s), 0);
+ CHECK(S_ISLNK(s.st_mode) != 0, 1);
+ CHECK(stat(".a.~lock~", &s), 0);
+ CHECK(S_ISREG(s.st_mode) != 0, 1);
+ CHECK(stat(".b.~lock~", &s), 0);
+ CHECK(S_ISREG(s.st_mode) != 0, 1);
}
+/* Checks that locking a file that is itself a symlink yields a lockfile in the
+ * directory that the symlink points to, named for the target of the
+ * symlink.
+ *
+ * (That is, if "a" is a symlink to "dir/b", then "a"'s lockfile is named
+ * "dir/.b.~lock".) */
static void
-run_lock_multiple(void)
+run_lock_symlink_to_dir(void)
{
- struct lockfile *a, *b, *c, *dummy;
-
- CHECK(lockfile_lock("a", 0, &a), 0);
- CHECK(lockfile_lock("b", 0, &b), 0);
- CHECK(lockfile_lock("c", 0, &c), 0);
-
- lockfile_unlock(a);
- CHECK(lockfile_lock("a", 0, &a), 0);
- CHECK(lockfile_lock("a", 0, &dummy), EDEADLK);
- lockfile_unlock(a);
-
- lockfile_unlock(b);
- CHECK(lockfile_lock("a", 0, &a), 0);
+ struct lockfile *a, *dummy;
+ struct stat s;
+
+ /* Create a symlink "a" pointing to "dir/b". */
+ CHECK(mkdir("dir", 0700), 0);
+ CHECK(symlink("dir/b", "a"), 0);
+ CHECK(lstat("a", &s), 0);
+ CHECK(S_ISLNK(s.st_mode) != 0, 1);
+
+ /* Lock 'a'. */
+ CHECK(lockfile_lock("a", &a), 0);
+ CHECK(lstat("dir/.b.~lock~", &s), 0);
+ CHECK(S_ISREG(s.st_mode) != 0, 1);
+ CHECK(lstat(".a.~lock~", &s), -1);
+ CHECK(errno, ENOENT);
+ CHECK(lockfile_lock("dir/b", &dummy), EDEADLK);
- lockfile_unlock(c);
lockfile_unlock(a);
}
TEST(lock_blocks_other_process),
TEST(lock_twice_blocks_other_process),
TEST(lock_and_unlock_allows_other_process),
- TEST(lock_timeout_gets_the_lock),
- TEST(lock_timeout_runs_out),
TEST(lock_multiple),
+ TEST(lock_symlink),
+ TEST(lock_symlink_to_dir),
TEST(help),
{ NULL, NULL }
#undef TEST
/*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdlib.h>
#include "flow.h"
+#include "ofp-actions.h"
#include "random.h"
#include "util.h"
main(int argc, char *argv[])
{
enum { MP_MAX_LINKS = 63 };
- struct nx_action_multipath mp;
+ struct ofpact_multipath mp;
bool ok = true;
int n;
random_bytes(&flow, sizeof flow);
- mp.max_link = htons(n - 1);
+ mp.max_link = n - 1;
multipath_execute(&mp, &flow);
old_link = flow.regs[0];
- mp.max_link = htons(n);
+ mp.max_link = n;
multipath_execute(&mp, &flow);
new_link = flow.regs[0];
"stddev/expected=%.4f\n",
n, n + 1, disruption, perfect, distribution);
- switch (ntohs(mp.algorithm)) {
+ switch (mp.algorithm) {
case NX_MP_ALG_MODULO_N:
if (disruption < (n < 2 ? .25 : .5)) {
fprintf(stderr, "%d -> %d: disruption=%.2f < .5\n",
/*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "flow.h"
#include "odp-util.h"
#include "ofpbuf.h"
+#include "util.h"
#include "vlog.h"
-int
-main(void)
+static int
+parse_keys(void)
{
+ int exit_code = 0;
struct ds in;
ds_init(&in);
vlog_set_levels_from_string("odp_util:console:dbg");
- while (!ds_get_line(&in, stdin)) {
+ while (!ds_get_test_line(&in, stdin)) {
enum odp_key_fitness fitness;
struct ofpbuf odp_key;
struct flow flow;
struct ds out;
int error;
- char *s;
-
- /* Delete comments, skip blank lines. */
- s = ds_cstr(&in);
- if (*s == '#') {
- puts(s);
- continue;
- }
- if (strchr(s, '#')) {
- *strchr(s, '#') = '\0';
- }
- if (s[strspn(s, " ")] == '\0') {
- putchar('\n');
- continue;
- }
/* Convert string to OVS DP key. */
ofpbuf_init(&odp_key, 0);
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);
}
ds_destroy(&in);
+ return exit_code;
+}
+
+static int
+parse_actions(void)
+{
+ struct ds in;
+
+ ds_init(&in);
+ vlog_set_levels_from_string("odp_util:console:dbg");
+ while (!ds_get_test_line(&in, stdin)) {
+ struct ofpbuf odp_actions;
+ struct ds out;
+ int error;
+
+ /* Convert string to OVS DP actions. */
+ ofpbuf_init(&odp_actions, 0);
+ error = odp_actions_from_string(ds_cstr(&in), NULL, &odp_actions);
+ if (error) {
+ printf("odp_actions_from_string: error\n");
+ goto next;
+ }
+
+ /* Convert odp_actions back to string. */
+ ds_init(&out);
+ format_odp_actions(&out, odp_actions.data, odp_actions.size);
+ puts(ds_cstr(&out));
+ ds_destroy(&out);
+
+ next:
+ ofpbuf_uninit(&odp_actions);
+ }
+ ds_destroy(&in);
+
return 0;
}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc == 2 &&!strcmp(argv[1], "parse-keys")) {
+ return parse_keys();
+ } else if (argc == 2 && !strcmp(argv[1], "parse-actions")) {
+ return parse_actions();
+ } else {
+ ovs_fatal(0, "usage: %s parse-keys | parse-actions", argv[0]);
+ }
+}
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-# Copyright (c) 2009, 2010 Nicira Networks.
+# Copyright (c) 2009, 2010 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct sha1_ctx sha1;
sha1_init(&sha1);
- sha1_update(&sha1, (const void *) vec->data, n0);
- sha1_update(&sha1, (const void *) (vec->data + n0), n1);
+ sha1_update(&sha1, vec->data, n0);
+ sha1_update(&sha1, vec->data + n0, n1);
sha1_final(&sha1, md);
assert(!memcmp(md, vec->output, SHA1_DIGEST_SIZE));
}
/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
for (i = 0; i < tc->n_lans; i++) {
struct lan *lan = tc->lans[i];
- free((char *) lan->name);
+ free(CONST_CAST(char *, lan->name));
free(lan);
}
for (i = 0; i < tc->n_bridges; i++) {
/*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
if (argc != 2) {
usage();
} else if (!strcmp(argv[1], "plain")) {
+ /* If we're not caching time there isn't much to test and SIGALRM won't
+ * be around to pull us out of the select() call, so just skip out */
+ if (!CACHE_TIME) {
+ exit (77);
+ }
+
do_test();
} else if (!strcmp(argv[1], "daemon")) {
/* Test that time still advances even in a daemon. This is an
char cwd[1024], *pidfile;
FILE *success;
+ if (!CACHE_TIME) {
+ exit (77);
+ }
+
assert(getcwd(cwd, sizeof cwd) == cwd);
unlink("test-timeval.success");
/*
- * Copyright (c) 2008, 2009, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
alarm(5);
/* Create a listening socket under name 'sockname1'. */
- sock1 = make_unix_socket(SOCK_STREAM, false, false, sockname1, NULL);
+ sock1 = make_unix_socket(SOCK_STREAM, false, sockname1, NULL);
if (sock1 < 0) {
ovs_fatal(-sock1, "%s: bind failed", sockname1);
}
/* Connect to 'sockname2' (which should be the same file, perhaps under a
* different name). */
- sock2 = make_unix_socket(SOCK_STREAM, false, false, NULL, sockname2);
+ sock2 = make_unix_socket(SOCK_STREAM, false, NULL, sockname2);
if (sock2 < 0) {
ovs_fatal(-sock2, "%s: connect failed", sockname2);
}
-# Copyright (c) 2012 Nicira Networks.
+# Copyright (c) 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
import ovs.daemon
import ovs.unixctl
+import ovs.unixctl.server
vlog = ovs.vlog.Vlog("test-unixctl")
exiting = False
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
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)
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)
/*
- * Copyright (c) 2011, 2012 Nicira Networks.
+ * Copyright (c) 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdlib.h>
#include "byte-order.h"
+#include "command-line.h"
#include "random.h"
#include "util.h"
}
}
+static void
+test_log_2_floor(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ int n;
+
+ for (n = 0; n < 32; n++) {
+ /* Check minimum x such that f(x) == n. */
+ check_log_2_floor(1 << n, n);
+
+ /* Check maximum x such that f(x) == n. */
+ check_log_2_floor((1 << n) | ((1 << n) - 1), n);
+
+ /* Check a random value in the middle. */
+ check_log_2_floor((random_uint32() & ((1 << n) - 1)) | (1 << n), n);
+ }
+
+ /* log_2_floor(0) is undefined, so don't check it. */
+}
+
static void
check_ctz(uint32_t x, int n)
{
}
}
+static void
+test_ctz(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ int n;
+
+ for (n = 0; n < 32; n++) {
+ /* Check minimum x such that f(x) == n. */
+ check_ctz(1 << n, n);
+
+ /* Check maximum x such that f(x) == n. */
+ check_ctz(UINT32_MAX << n, n);
+
+ /* Check a random value in the middle. */
+ check_ctz((random_uint32() | 1) << n, n);
+ }
+
+ /* Check ctz(0). */
+ check_ctz(0, 32);
+}
+
/* Returns the sum of the squares of the first 'n' positive integers. */
static unsigned int
sum_of_squares(int n)
}
static void
-check_bitwise_copy(void)
+test_bitwise_copy(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
unsigned int n_loops;
int src_ofs;
}
static void
-check_bitwise_zero(void)
+test_bitwise_zero(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
unsigned int n_loops;
int dst_ofs;
}
static void
-check_bitwise_one(void)
+test_bitwise_one(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
unsigned int n_loops;
int dst_ofs;
}
static void
-check_bitwise_is_all_zeros(void)
+test_bitwise_is_all_zeros(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
int n_loops;
- n_loops = 0;
for (n_loops = 0; n_loops < 100; n_loops++) {
ovs_be64 x = htonll(0);
int i;
}
}
-int
-main(void)
+static void
+test_follow_symlinks(int argc, char *argv[])
{
- int n;
-
- for (n = 0; n < 32; n++) {
- /* Check minimum x such that f(x) == n. */
- check_log_2_floor(1 << n, n);
- check_ctz(1 << n, n);
-
- /* Check maximum x such that f(x) == n. */
- check_log_2_floor((1 << n) | ((1 << n) - 1), n);
- check_ctz(UINT32_MAX << n, n);
+ int i;
- /* Check a random value in the middle. */
- check_log_2_floor((random_uint32() & ((1 << n) - 1)) | (1 << n), n);
- check_ctz((random_uint32() | 1) << n, n);
+ for (i = 1; i < argc; i++) {
+ char *target = follow_symlinks(argv[i]);
+ puts(target);
+ free(target);
}
+}
+\f
+static const struct command commands[] = {
+ {"ctz", 0, 0, test_ctz},
+ {"log_2_floor", 0, 0, test_log_2_floor},
+ {"bitwise_copy", 0, 0, test_bitwise_copy},
+ {"bitwise_zero", 0, 0, test_bitwise_zero},
+ {"bitwise_one", 0, 0, test_bitwise_one},
+ {"bitwise_is_all_zeros", 0, 0, test_bitwise_is_all_zeros},
+ {"follow-symlinks", 1, INT_MAX, test_follow_symlinks},
+ {NULL, 0, 0, NULL},
+};
- /* Check ctz(0).
- * (log_2_floor(0) is undefined.) */
- check_ctz(0, 32);
-
- check_bitwise_copy();
-
- check_bitwise_zero();
-
- check_bitwise_one();
-
- check_bitwise_is_all_zeros();
-
+int
+main(int argc, char *argv[])
+{
+ set_program_name(argv[0]);
+ run_command(argc - 1, argv + 1, commands);
return 0;
}
/*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdlib.h>
#include <unistd.h>
#include "command-line.h"
+#include "ofp-msgs.h"
+#include "ofp-util.h"
+#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "poll-loop.h"
#include "socket-util.h"
check_errno(int a, int b, const char *as, const char *file, int line)
{
if (a != b) {
+ char *str_b = strdup(strerror(abs(b)));
ovs_fatal(0, "%s:%d: %s is %d (%s) but should be %d (%s)",
- file, line, as, a, strerror(abs(a)), b, strerror(abs(b)));
+ file, line, as, a, strerror(abs(a)), b, str_b);
}
}
test_refuse_connection(int argc OVS_UNUSED, char *argv[])
{
const char *type = argv[1];
- int expected_error;
struct fake_pvconn fpv;
struct vconn *vconn;
-
- expected_error = (!strcmp(type, "unix") ? EPIPE
- : !strcmp(type, "tcp") ? ECONNRESET
- : EPROTO);
+ int error;
fpv_create(type, &fpv);
CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
DSCP_DEFAULT), 0);
fpv_close(&fpv);
vconn_run(vconn);
- CHECK_ERRNO(vconn_connect(vconn), expected_error);
+
+ error = vconn_connect_block(vconn);
+ if (!strcmp(type, "tcp")) {
+ if (error != ECONNRESET && error != EPIPE) {
+ ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
+ error, strerror(error));
+ }
+ } else {
+ CHECK_ERRNO(error, !strcmp(type, "unix") ? EPIPE : EPROTO);
+ }
+
vconn_close(vconn);
fpv_destroy(&fpv);
}
test_accept_then_close(int argc OVS_UNUSED, char *argv[])
{
const char *type = argv[1];
- int expected_error;
struct fake_pvconn fpv;
struct vconn *vconn;
-
- expected_error = (!strcmp(type, "unix") ? EPIPE
- : !strcmp(type, "tcp") ? ECONNRESET
- : EPROTO);
+ int error;
fpv_create(type, &fpv);
CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
vconn_run(vconn);
stream_close(fpv_accept(&fpv));
fpv_close(&fpv);
- CHECK_ERRNO(vconn_connect(vconn), expected_error);
+
+ error = vconn_connect_block(vconn);
+ if (!strcmp(type, "tcp") || !strcmp(type, "unix")) {
+ if (error != ECONNRESET && error != EPIPE) {
+ ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
+ error, strerror(error));
+ }
+ } else {
+ CHECK_ERRNO(error, EPROTO);
+ }
+
vconn_close(vconn);
fpv_destroy(&fpv);
}
struct fake_pvconn fpv;
struct vconn *vconn;
struct stream *stream;
+ int error;
fpv_create(type, &fpv);
CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP10_VERSION, &vconn,
retval = stream_recv(stream, &hello, sizeof hello);
if (retval == sizeof hello) {
+ enum ofpraw raw;
+
CHECK(hello.version, OFP10_VERSION);
- CHECK(hello.type, OFPT_HELLO);
+ CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0);
+ CHECK(raw, OFPRAW_OFPT_HELLO);
CHECK(ntohs(hello.length), sizeof hello);
break;
} else {
poll_block();
}
stream_close(stream);
- CHECK_ERRNO(vconn_connect(vconn), ECONNRESET);
+ error = vconn_connect_block(vconn);
+ if (error != ECONNRESET && error != EPIPE) {
+ ovs_fatal(0, "unexpected vconn_connect() return value %d (%s)",
+ error, strerror(error));
+ }
vconn_close(vconn);
}
struct ofp_header hello;
int retval = stream_recv(stream, &hello, sizeof hello);
if (retval == sizeof hello) {
+ enum ofpraw raw;
+
CHECK(hello.version, OFP10_VERSION);
- CHECK(hello.type, OFPT_HELLO);
+ CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0);
+ CHECK(raw, OFPRAW_OFPT_HELLO);
CHECK(ntohs(hello.length), sizeof hello);
read_hello = true;
} else {
poll_block();
}
stream_close(stream);
- CHECK_ERRNO(vconn_recv(vconn, &msg), EOF);
+ CHECK_ERRNO(vconn_recv_block(vconn, &msg), EOF);
vconn_close(vconn);
}
test_send_plain_hello(int argc OVS_UNUSED, char *argv[])
{
const char *type = argv[1];
- struct ofp_header hello;
+ struct ofpbuf *hello;
- hello.version = OFP10_VERSION;
- hello.type = OFPT_HELLO;
- hello.length = htons(sizeof hello);
- hello.xid = htonl(0x12345678);
- test_send_hello(type, &hello, sizeof hello, 0);
+ hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP10_VERSION,
+ htonl(0x12345678), 0);
+ test_send_hello(type, hello->data, hello->size, 0);
+ ofpbuf_delete(hello);
}
/* Try connecting and sending an extra-long hello, which should succeed (since
test_send_long_hello(int argc OVS_UNUSED, char *argv[])
{
const char *type = argv[1];
- struct ofp_header hello;
- char buffer[sizeof hello * 2];
-
- hello.version = OFP10_VERSION;
- hello.type = OFPT_HELLO;
- hello.length = htons(sizeof buffer);
- hello.xid = htonl(0x12345678);
- memset(buffer, 0, sizeof buffer);
- memcpy(buffer, &hello, sizeof hello);
- test_send_hello(type, buffer, sizeof buffer, 0);
+ struct ofpbuf *hello;
+ enum { EXTRA_BYTES = 8 };
+
+ hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP10_VERSION,
+ htonl(0x12345678), EXTRA_BYTES);
+ ofpbuf_put_zeros(hello, EXTRA_BYTES);
+ ofpmsg_update_length(hello);
+ test_send_hello(type, hello->data, hello->size, 0);
+ ofpbuf_delete(hello);
}
/* Try connecting and sending an echo request instead of a hello, which should
test_send_echo_hello(int argc OVS_UNUSED, char *argv[])
{
const char *type = argv[1];
- struct ofp_header echo;
+ struct ofpbuf *echo;
- echo.version = OFP10_VERSION;
- echo.type = OFPT_ECHO_REQUEST;
- echo.length = htons(sizeof echo);
- echo.xid = htonl(0x89abcdef);
- test_send_hello(type, &echo, sizeof echo, EPROTO);
+ echo = ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+ htonl(0x12345678), 0);
+ test_send_hello(type, echo->data, echo->size, EPROTO);
+ ofpbuf_delete(echo);
}
/* Try connecting and sending a hello packet that has its length field as 0,
test_send_invalid_version_hello(int argc OVS_UNUSED, char *argv[])
{
const char *type = argv[1];
- struct ofp_header hello;
+ struct ofpbuf *hello;
- hello.version = OFP10_VERSION - 1;
- hello.type = OFPT_HELLO;
- hello.length = htons(sizeof hello);
- hello.xid = htonl(0x12345678);
- test_send_hello(type, &hello, sizeof hello, EPROTO);
+ hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP10_VERSION,
+ htonl(0x12345678), 0);
+ ((struct ofp_header *) hello->data)->version = 0;
+ test_send_hello(type, hello->data, hello->size, EPROTO);
+ ofpbuf_delete(hello);
}
static const struct command commands[] = {
-# Copyright (c) 2011 Nicira Networks.
+# Copyright (c) 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
AT_INIT
-AT_COPYRIGHT([Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+AT_COPYRIGHT([Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
done
exit 1
}
+
+# Prints the integers from $1 to $2, increasing by $3 (default 1) on stdout.
+seq () {
+ while test $1 -le $2; do
+ echo $1
+ set `expr $1 + ${3-1}` $2 $3
+ done
+}
]
m4_divert_pop([PREPARE_TESTS])
m4_define([OVS_WAIT_WHILE],
[OVS_WAIT([if $1; then return 1; else return 0; fi], [$2])])
+dnl ON_EXIT([COMMANDS])
+dnl
+dnl Adds the shell COMMANDS to a collection executed when the current test
+dnl completes, as a cleanup action. (The most common use is to kill a
+dnl daemon started by the test. This is important to prevent tests that
+dnl start daemons from hanging at exit.)
+m4_define([ON_EXIT], [trap '. ./cleanup' 0; cat >>cleanup <<'EOF'
+$1
+EOF
+])
+
m4_include([tests/ovsdb-macros.at])
m4_include([tests/ofproto-macros.at])
m4_include([tests/check-structs.at])
m4_include([tests/daemon.at])
m4_include([tests/daemon-py.at])
+m4_include([tests/ofp-actions.at])
m4_include([tests/ofp-print.at])
m4_include([tests/ofp-errors.at])
m4_include([tests/ovs-ofctl.at])
AT_SETUP([unixctl bad target - Python])
OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
AT_SKIP_IF([test $HAVE_PYTHON = no])
AT_CHECK([PYAPPCTL -t bogus doit], [1], [], [stderr])
AT_SKIP_IF([test $HAVE_PYTHON = no])
OVS_RUNDIR=`pwd`; export OVS_RUNDIR
OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
-trap 'kill `cat test-unixctl.py.pid`' 0
+ON_EXIT([kill `cat test-unixctl.py.pid`])
AT_CAPTURE_FILE([`pwd`/test-unixctl.py.log])
AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file --pidfile --detach])
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])
AT_CHECK([PYAPPCTL -t test-unixctl.py bogus], [2], [], [experr])
AT_CHECK([APPCTL -t test-unixctl.py exit])
-trap '' 0]
AT_CLEANUP
exit 1
fi])
-# This test is a strict subset of the larger test down below, but it
-# completes in a realistic amount of time with the "lcov" wrapper.
-AT_SETUP([UUID generation])
-AT_KEYWORDS([UUID])
-AT_CHECK([test-uuid > uuid])
-AT_CHECK([
- uuid=`cat uuid`
- CHECK_UUID])
-AT_CLEANUP
-
-# This test is a strict subset of the larger test down below, but it
-# completes in a realistic amount of time with the "lcov" wrapper.
-AT_SETUP([UUID parsing and serialization])
-AT_KEYWORDS([UUID])
-AT_CHECK([test-uuid f47ac10b-58cc-4372-a567-0e02b2c3d479], [0],
- [f47ac10b-58cc-4372-a567-0e02b2c3d479
-])
-AT_CLEANUP
-
AT_SETUP([UUID generation, parsing, serialization])
-AT_SKIP_IF([test "$CHECK_LCOV" = true]) # lcov makes this test absurdly slow
AT_KEYWORDS([UUID])
AT_CHECK([
uuids=
AT_SETUP([vlog - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
-AT_CHECK([$PYTHON $srcdir/test-vlog.py --log-file log_file \
--v ANY:ANY:dbg module_1:ANY:info module_2:ANY:warn ANY:syslog:off \
-2>stderr_log])
AT_CAPTURE_FILE([log_file])
AT_CAPTURE_FILE([stderr_log])
+AT_CHECK([$PYTHON $srcdir/test-vlog.py --log-file log_file \
+-v dbg module_1:info module_2:warn syslog:off 2>stderr_log])
AT_CHECK([diff log_file stderr_log])
-AT_CHECK([sed -e 's/.* .* ..:..:..|//' \
--e 's/File "[[^"]]*", line [[0-9]][[0-9]]*,/File <name>, line <number>,/' \
+AT_CHECK([sed -e 's/.*-.*-.*T..:..:..Z|//' \
+-e 's/File ".*", line [[0-9]][[0-9]]*,/File <name>, line <number>,/' \
stderr_log], [0], [dnl
0|module_0|EMER|emergency
1|module_0|ERR|error
])
AT_CLEANUP
+
+AT_SETUP([vlog - vlog/reopen - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+ON_EXIT([kill `cat test-unixctl.py.pid`])
+
+AT_CAPTURE_FILE([log])
+AT_CAPTURE_FILE([log.old])
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach])
+
+AT_CHECK([APPCTL -t test-unixctl.py log message])
+mv log log.old
+AT_CHECK([APPCTL -t test-unixctl.py log message2])
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl.py log message3])
+AT_CHECK([APPCTL -t test-unixctl.py exit])
+
+AT_CHECK([sed 's/.*|//' log.old], [0], [dnl
+Entering run loop.
+message
+message2
+])
+AT_CHECK([sed 's/.*|//' log], [0], [dnl
+message3
+])
+AT_CLEANUP
+
+AT_SETUP([vlog - vlog/reopen without log file - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+ON_EXIT([kill `cat test-unixctl.py.pid`])
+
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --pidfile --detach])
+
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen], [0],
+ [Logging to file not configured
+])
+AT_CLEANUP
+
+dnl This checks that if vlog/reopen can't reopen the log file,
+dnl nothing particularly bad (e.g. Python throws an exception and
+dnl aborts the program) happens.
+AT_SETUP([vlog - vlog/reopen can't reopen log file - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+
+# Verify that /dev/full is a character device that fails writes.
+AT_SKIP_IF([test ! -c /dev/full])
+AT_SKIP_IF([echo > /dev/full])
+
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+ON_EXIT([kill `cat test-unixctl.py.pid`])
+
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach])
+AT_CHECK([APPCTL -t test-unixctl.py log message])
+mv log log.old
+ln -s /dev/full log
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl.py log message2])
+rm log
+AT_CHECK([APPCTL -t test-unixctl.py vlog/reopen])
+AT_CHECK([APPCTL -t test-unixctl.py log message3])
+AT_CHECK([APPCTL -t test-unixctl.py exit])
+AT_CHECK([sed 's/.*|//' log.old], [0], [dnl
+Entering run loop.
+message
+])
+AT_CHECK([sed 's/.*|//' log], [0], [dnl
+message3
+])
+AT_CLEANUP
+
+AT_SETUP([vlog - vlog/set and vlog/list - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+OVS_DBDIR=`pwd`; export OVS_DBDIR
+OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+ON_EXIT([kill `cat test-unixctl.py.pid`])
+
+AT_CAPTURE_FILE([log])
+AT_CHECK([$PYTHON $srcdir/test-unixctl.py --log-file=`pwd`/log --pidfile --detach])
+
+AT_CHECK([APPCTL -t test-unixctl.py vlog/list], [0], [dnl
+ console syslog file
+ ------- ------ ------
+daemon info info info
+fatal-signal info info info
+jsonrpc info info info
+poller info info info
+reconnect info info info
+socket_util info info info
+stream info info info
+test-unixctl info info info
+unixctl_server info info info
+])
+
+AT_CHECK([APPCTL -t test-unixctl.py vlog/set daemon:syslog:err])
+AT_CHECK([APPCTL -t test-unixctl.py vlog/set file:dbg])
+AT_CHECK([APPCTL -t test-unixctl.py vlog/set nonexistent], [0],
+ [no facility, level, or module "nonexistent"
+])
+AT_CHECK([APPCTL -t test-unixctl.py vlog/list], [0], [dnl
+ console syslog file
+ ------- ------ ------
+daemon info err dbg
+fatal-signal info info dbg
+jsonrpc info info dbg
+poller info info dbg
+reconnect info info dbg
+socket_util info info dbg
+stream info info dbg
+test-unixctl info info dbg
+unixctl_server info info dbg
+])
+AT_CLEANUP
--- tcpdump/print-openflow.c 1969-12-31 16:00:00.000000000 -0800
+++ tcpdump/print-openflow.c 2009-05-11 15:38:41.000000000 -0700
@@ -0,0 +1,46 @@
-+/* Copyright (C) 2007, 2008, 2009 Nicira Networks.
++/* Copyright (C) 2007, 2008, 2009 Nicira, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
/ovs-benchmark.1
/ovs-cfg-mod
/ovs-cfg-mod.8
+/ovs-check-dead-ifs
/ovs-controller
/ovs-controller.8
/ovs-ctl
/ovs-dpctl
/ovs-dpctl.8
+/ovs-l3ping
+/ovs-l3ping.8
/ovs-lib
/ovs-ofctl
/ovs-ofctl.8
utilities/ovs-dpctl \
utilities/ovs-ofctl \
utilities/ovs-vsctl
-bin_SCRIPTS += utilities/ovs-pki utilities/ovs-vsctl utilities/ovs-parse-leaks
+bin_SCRIPTS += utilities/ovs-pki utilities/ovs-parse-leaks
if HAVE_PYTHON
bin_SCRIPTS += \
+ utilities/ovs-l3ping \
utilities/ovs-pcap \
utilities/ovs-tcpundump \
utilities/ovs-test \
utilities/ovs-vlan-test
endif
-noinst_SCRIPTS += utilities/ovs-pki-cgi
-scripts_SCRIPTS += utilities/ovs-ctl utilities/ovs-save
+scripts_SCRIPTS += \
+ utilities/ovs-check-dead-ifs \
+ utilities/ovs-ctl \
+ utilities/ovs-save
scripts_DATA += utilities/ovs-lib
EXTRA_DIST += \
+ utilities/ovs-check-dead-ifs.in \
utilities/ovs-ctl.in \
+ utilities/ovs-l3ping.in \
utilities/ovs-lib.in \
utilities/ovs-parse-leaks.in \
utilities/ovs-pcap.in \
- utilities/ovs-pki-cgi.in \
utilities/ovs-pki.in \
utilities/ovs-save \
utilities/ovs-tcpundump.in \
utilities/ovs-controller.8.in \
utilities/ovs-ctl.8 \
utilities/ovs-dpctl.8.in \
+ utilities/ovs-l3ping.8.in \
utilities/ovs-ofctl.8.in \
utilities/ovs-parse-leaks.8 \
utilities/ovs-pcap.1.in \
utilities/ovs-appctl.8 \
utilities/ovs-ctl \
utilities/ovs-benchmark.1 \
+ utilities/ovs-check-dead-ifs \
utilities/ovs-controller.8 \
utilities/ovs-dpctl.8 \
+ utilities/ovs-l3ping \
+ utilities/ovs-l3ping.8 \
utilities/ovs-lib \
utilities/ovs-ofctl.8 \
utilities/ovs-parse-leaks \
utilities/ovs-pcap \
utilities/ovs-pcap.1 \
utilities/ovs-pki \
- utilities/ovs-pki-cgi \
utilities/ovs-pki.8 \
utilities/ovs-tcpundump \
utilities/ovs-tcpundump.1 \
utilities/ovs-benchmark.1 \
utilities/ovs-controller.8 \
utilities/ovs-dpctl.8 \
+ utilities/ovs-l3ping.8 \
utilities/ovs-ofctl.8 \
utilities/ovs-parse-leaks.8 \
utilities/ovs-pcap.1 \
bugtool_scripts = \
utilities/bugtool/ovs-bugtool-cfm-show \
+ utilities/bugtool/ovs-bugtool-coverage-show \
utilities/bugtool/ovs-bugtool-lacp-show \
+ utilities/bugtool/ovs-bugtool-memory-show \
utilities/bugtool/ovs-bugtool-tc-class-show \
+ utilities/bugtool/ovs-bugtool-vsctl-show \
utilities/bugtool/ovs-bugtool-ovsdb-dump \
- utilities/bugtool/ovs-bugtool-daemons-ver
+ utilities/bugtool/ovs-bugtool-daemons-ver \
+ utilities/bugtool/ovs-bugtool-bond-show
scripts_SCRIPTS += $(bugtool_scripts)
bugtoolpluginsdir = $(pkgdatadir)/bugtool-plugins
--- /dev/null
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2012 Nicira, Inc.
+
+ovs-appctl bond/show
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
-# Copyright (C) 2011 Nicira Networks.
+# Copyright (C) 2011 Nicira, Inc.
ovs-appctl cfm/show
--- /dev/null
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2012 Nicira, Inc.
+
+ovs-appctl coverage/show
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
-# Copyright (C) 2012 Nicira Networks.
+# Copyright (C) 2012 Nicira, Inc.
for f in `cd /var/run/openvswitch/; ls *.pid 2>/dev/null`
do
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
-# Copyright (C) 2011 Nicira Networks.
+# Copyright (C) 2011 Nicira, Inc.
ovs-appctl lacp/show
--- /dev/null
+#! /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
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
-# Copyright (C) 2011 Nicira Networks.
+# Copyright (C) 2011 Nicira, Inc.
ovsdb-client -f csv dump unix:/var/run/openvswitch/db.sock Open_vSwitch
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
-# Copyright (C) 2011 Nicira Networks.
+# Copyright (C) 2011 Nicira, Inc.
for iface in $(cd /sys/class/net && echo *); do
if [ -d /sys/class/net/$iface ]; then
--- /dev/null
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2012 Nicira, Inc.
+
+ovs-vsctl --timeout=5 show
.IP "\fB\-\-all\fR"
Use all available capabilities.
.
+.IP "\fB\-\-ovs\fR"
+Use only Open vSwitch relevant capabilities.
+.
.IP "\fB\-\-capabilities\fR"
List \fBovs\-bugtool\fR capabilities.
.
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Copyright (c) 2005, 2007 XenSource Ltd.
-# Copyright (c) 2010, 2011 Nicira Networks.
+# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
#
# To add new entries to the bugtool, you need to:
OPENVSWITCH_DEFAULT_SWITCH = '/etc/default/openvswitch-switch' # Debian
OPENVSWITCH_SYSCONFIG_SWITCH = '/etc/sysconfig/openvswitch' # RHEL
OPENVSWITCH_DEFAULT_CONTROLLER = '/etc/default/openvswitch-controller'
-OPENVSWITCH_CONF_DB = '@sysconfdir@/openvswitch/conf.db'
+OPENVSWITCH_CONF_DB = '@DBDIR@/conf.db'
OPENVSWITCH_VSWITCHD_PID = '@RUNDIR@/ovs-vswitchd.pid'
COLLECTD_LOGS_DIR = '/var/lib/collectd/rrd'
VAR_LOG_DIR = '/var/log/'
def output_ts(x):
output("[%s] %s" % (time.strftime("%x %X %Z"), x))
-def cmd_output(cap, args, label = None, filter = None):
+def cmd_output(cap, args, label=None, filter=None):
if cap in entries:
if not label:
if isinstance(args, list):
run_procs(process_lists.values())
-def main(argv = None):
+def main(argv=None):
global ANSWER_YES_TO_ALL, SILENT_MODE
global entries, data, dbg, unlimited_data
+ # Filter flags
+ only_ovs_info = False
+ collect_all_info = True
+
# we need access to privileged files, exit if we are not running as root
if os.getuid() != 0:
print >>sys.stderr, "Error: ovs-bugtool must be run as root"
(options, params) = getopt.gnu_getopt(
argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=',
'output=', 'outfd=', 'outfile=', 'all', 'unlimited',
- 'debug'])
+ 'debug', 'ovs'])
except getopt.GetoptError, opterr:
print >>sys.stderr, opterr
return 2
dbg = True
ProcOutput.debug = True
+ if k == '--ovs':
+ only_ovs_info = True
+ collect_all_info = False
+
if len(params) != 1:
print >>sys.stderr, "Invalid additional arguments", str(params)
return 2
for d in disk_list():
cmd_output(CAP_HDPARM_T, [HDPARM, '-tT', '/dev/%s' % d])
- file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES,
+ file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES,
PROC_FILESYSTEMS, PROC_CMDLINE])
cmd_output(CAP_KERNEL_INFO, [ZCAT, PROC_CONFIG], label='config')
cmd_output(CAP_KERNEL_INFO, [SYSCTL, '-A'])
cmd_output(CAP_MULTIPATH, [DMSETUP, 'table'])
func_output(CAP_MULTIPATH, 'multipathd_topology', multipathd_topology)
cmd_output(CAP_MULTIPATH, [MPPUTIL, '-a'])
- if CAP_MULTIPATH in entries:
+ if CAP_MULTIPATH in entries and collect_all_info:
dump_rdac_groups(CAP_MULTIPATH)
tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, IFCFG_RE)
f = open('/sys/class/net/%s/type' % p, 'r')
t = f.readline()
f.close()
- if int(t) == 1:
+ if os.path.islink('/sys/class/net/%s/device' % p) and int(t) == 1:
# ARPHRD_ETHER
cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, p])
cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-S', p])
cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-k', p])
cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-i', p])
cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, '-c', p])
+ if int(t) == 1:
cmd_output(CAP_NETWORK_STATUS,
[TC, '-s', '-d', 'class', 'show', 'dev', p])
except:
tree_output(CAP_YUM, APT_SOURCES_LIST_D)
cmd_output(CAP_YUM, [DPKG_QUERY, '-W', '-f=${Package} ${Version} ${Status}\n'], 'dpkg-packages')
+ # Filter out ovs relevant information if --ovs option passed
+ # else collect all information
+ filters = set()
+ if only_ovs_info:
+ filters.add('ovs')
+ ovs_info_caps = [CAP_NETWORK_STATUS, CAP_SYSTEM_LOGS,
+ CAP_NETWORK_CONFIG]
+ ovs_info_list = ['process-tree']
+ # We cannot use iteritems, since we modify 'data' as we pass through
+ for (k, v) in data.items():
+ cap = v['cap']
+ if 'filename' in v:
+ info = k[0]
+ else:
+ info = k
+ if info not in ovs_info_list and cap not in ovs_info_caps:
+ del data[k]
+
+ if filters:
+ filter = ",".join(filters)
+ else:
+ filter = None
+
try:
- load_plugins()
+ load_plugins(filter=filter)
except:
pass
-
+
# permit the user to filter out data
- for k in sorted(data.keys()):
- if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % k):
- del data[k]
+ # We cannot use iteritems, since we modify 'data' as we pass through
+ for (k, v) in sorted(data.items()):
+ cap = v['cap']
+ if 'filename' in v:
+ key = k[0]
+ else:
+ key = k
+ if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % key):
+ del data[k]
# collect selected data now
output_ts('Running commands to collect data')
def multipathd_topology(cap):
- pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE,
+ pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE,
stdout=PIPE, stderr=dev_null)
stdout, stderr = pipe.communicate('show topology')
group, _ = line.split(None, 1)
cmd_output(cap, [MPPUTIL, '-g', group])
-def load_plugins(just_capabilities = False):
+def load_plugins(just_capabilities=False, filter=None):
def getText(nodelist):
rc = ""
for node in nodelist:
rc += node.data
return rc.encode()
- def getBoolAttr(el, attr, default = False):
+ def getBoolAttr(el, attr, default=False):
ret = default
val = el.getAttribute(attr).lower()
if val in ['true', 'false', 'yes', 'no']:
ret = val in ['true', 'yes']
return ret
-
+
for dir in [d for d in os.listdir(PLUGIN_DIR) if os.path.isdir(os.path.join(PLUGIN_DIR, d))]:
if not caps.has_key(dir):
if not os.path.exists("%s/%s.xml" % (PLUGIN_DIR, dir)):
if just_capabilities:
continue
-
+
plugdir = os.path.join(PLUGIN_DIR, dir)
for file in [f for f in os.listdir(plugdir) if f.endswith('.xml')]:
xmldoc = parse(os.path.join(plugdir, file))
assert xmldoc.documentElement.tagName == "collect"
for el in xmldoc.documentElement.getElementsByTagName("*"):
+ filters_tmp = el.getAttribute("filters")
+ if filters_tmp == '':
+ filters = []
+ else:
+ filters = filters_tmp.split(',')
+ if not(filter is None or filter in filters):
+ continue
if el.tagName == "files":
newest_first = getBoolAttr(el, 'newest_first')
file_output(dir, getText(el.childNodes).split(),
if pattern == '': pattern = None
negate = getBoolAttr(el, 'negate')
newest_first = getBoolAttr(el, 'newest_first')
- tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None,
+ tree_output(dir, getText(el.childNodes),
+ pattern and re.compile(pattern) or None,
negate=negate, newest_first=newest_first)
elif el.tagName == "command":
label = el.getAttribute("label")
pass
finally:
zf.close()
-
+
output ('Writing archive %s successful.' % filename)
if SILENT_MODE:
print filename
caps[cap] = tuple(l)
-def size_of_dir(d, pattern = None, negate = False):
+def size_of_dir(d, pattern=None, negate=False):
if os.path.isdir(d):
return size_of_all([os.path.join(d, fn) for fn in os.listdir(d)],
pattern, negate)
return 0
-def size_of_all(files, pattern = None, negate = False):
+def size_of_all(files, pattern=None, negate=False):
return sum([size_of(f, pattern, negate) for f in files])
def terminate(self):
if self.running:
try:
+ self.proc.stdout.close()
os.kill(self.proc.pid, SIGTERM)
except:
pass
line = self.proc.stdout.readline()
if line == '':
# process exited
+ self.proc.stdout.close()
self.status = self.proc.wait()
self.proc = None
self.running = False
class StringIOmtime(StringIO.StringIO):
- def __init__(self, buf = ''):
+ def __init__(self, buf=''):
StringIO.StringIO.__init__(self, buf)
self.mtime = time.time()
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011 Nicira, Inc.
-->
<collect>
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011, 2012 Nicira, Inc.
-->
<collect>
- <command label="tc-class-show">/usr/share/openvswitch/scripts/ovs-bugtool-tc-class-show</command>
- <command label="dump-ovsdb">/usr/share/openvswitch/scripts/ovs-bugtool-ovsdb-dump</command>
- <command label="ovs-lacp-show">/usr/share/openvswitch/scripts/ovs-bugtool-lacp-show</command>
- <command label="ovs-cfm-show">/usr/share/openvswitch/scripts/ovs-bugtool-cfm-show</command>
+ <command label="tc-class-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-tc-class-show</command>
+ <command label="ovs-vsctl-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-vsctl-show</command>
+ <command label="dump-ovsdb" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-ovsdb-dump</command>
+ <command label="ovs-appctl-lacp-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-lacp-show</command>
+ <command label="ovs-appctl-cfm-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-cfm-show</command>
+ <command label="ovs-appctl-coverage-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-coverage-show</command>
+ <command label="ovs-appctl-bond-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-bond-show</command>
+ <command label="ovs-appctl-memory-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-memory-show</command>
</collect>
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011 Nicira, Inc.
-->
<capability pii="no" min_size="-1" max_size="1000000" min_time="-1" max_time="60" mime="text/plain" checked="true" hidden="true">
</capability>
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011 Nicira, Inc.
-->
<collect>
<command label="timezone">date --rfc-3339=seconds</command>
- <command label="ovs-daemons-ver">/usr/share/openvswitch/scripts/ovs-bugtool-daemons-ver</command>
+ <command label="ovs-daemons-ver" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-daemons-ver</command>
</collect>
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
- Copyright (C) 2011 Nicira Networks.
+ Copyright (C) 2011, 2012 Nicira, Inc.
-->
<collect>
- <directory label="ovsdb-backups" pattern=".*/conf.db.backup[0-9][^/]*$">/etc/openvswitch</directory>
+ <directory label="ovsdb-backups" filters="ovs" pattern=".*/conf.db.backup[0-9][^/]*$">/etc/openvswitch</directory>
+ <directory label="ovsdb-backups2" filters="ovs" pattern=".*/conf.db.backup[0-9][^/]*$">/var/lib/openvswitch</directory>
</collect>
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
.SH DESCRIPTION
Open vSwitch daemons accept certain commands at runtime to control their
behavior and query their settings. Every daemon accepts a common set of
-commands documented under \fBCOMMON COMMANDS\fR below, and
+commands documented under \fBCOMMON COMMANDS\fR below. Some daemons
+support additional commands documented in their own manpages.
\fBovs\-vswitchd\fR in particular accepts a number of additional
commands documented in \fBovs\-vswitchd\fR(8).
.PP
The number of milliseconds elapsed from the start of the application
to the time the message was logged.
.
+.IP \fB%t\fR
+The subprogram name, that is, an identifying name for the process or
+thread that emitted the log message, such as \fBmonitor\fR for the
+process used for \fB\-\-monitor\fR or \fBmain\fR for the primary
+process or thread in a program.
+.
+.IP \fB%T\fR
+The subprogram name enclosed in parentheses, e.g. \fB(monitor)\fR, or
+the empty string for the primary process or thread in a program.
+.
.IP \fB%%\fR
A literal \fB%\fR.
.RE
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
.
.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).
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright (c) 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
--- /dev/null
+#! @PYTHON@
+
+import os
+import re
+import stat
+import sys
+
+if "--help" in sys.argv:
+ sys.stdout.write("""\
+ovs-check-dead-ifs: Check for packet sockets for nonexistent network devices.
+
+One side effect of the "force-reload-kmod" command that reloads the
+Open vSwitch kernel module is that all the network devices that the
+Open vSwitch kernel module implemented get destroyed and then replaced
+by new instances with the same names. Unfortunately, programs that
+are listening for packets on the original network devices will not
+receive packets that arrive on the new instances. This causes some
+services, such as DHCP, to silently fail. This program looks for such
+problems and, if it finds any, prints information about programs that
+are in such a state. The system administrator should then take some
+action to fix the problem, such as restarting these programs.
+""")
+ sys.exit(0)
+elif len(sys.argv) > 1:
+ sys.stderr.write("ovs-check-dead-ifs: no arguments or options accepted "
+ "(use --help for help)\n")
+ sys.exit(1)
+
+# Get the set of all valid ifindexes.
+#
+# 0 is always valid for our purposes because it means "any interface".
+valid_ifindexes = set([])
+for ifname in os.listdir("/sys/class/net"):
+ fn = "/sys/class/net/%s/ifindex" % ifname
+ try:
+ valid_ifindexes.add(int(open(fn).readline()))
+ except IOError:
+ pass
+ except ValueError:
+ print "%s: unexpected format\n" % fn
+
+# Get inodes for all packet sockets whose ifindexes don't exist.
+invalid_inodes = set()
+f = open("/proc/net/packet")
+f.readline() # Skip header line.
+for line in f:
+ fields = line.split()
+ ifindex = int(fields[4])
+ if ifindex not in valid_ifindexes:
+ invalid_inodes.add(int(fields[8]))
+f.close()
+
+if not invalid_inodes:
+ sys.exit(0)
+
+# Now find the processes that are using those packet sockets.
+inode_re = re.compile(r'socket:\[([0-9]+)\]$')
+bad_pids = set()
+for pid in os.listdir("/proc"):
+ try:
+ pid = int(pid)
+ except ValueError:
+ continue
+
+ try:
+ fds = os.listdir("/proc/%d/fd" % pid)
+ except OSError:
+ continue
+
+ for fd in fds:
+ try:
+ fd = int(fd)
+ except ValueError:
+ continue
+
+ try:
+ s = os.stat("/proc/%d/fd/%d" % (pid, fd))
+ except OSError:
+ continue
+
+ if not stat.S_ISSOCK(s.st_mode):
+ continue
+
+ try:
+ linkname = os.readlink("/proc/%d/fd/%d" % (pid, fd))
+ except OSError:
+ continue
+
+ m = inode_re.match(linkname)
+ if not m:
+ continue
+
+ inode = int(m.group(1))
+ if inode in invalid_inodes:
+ bad_pids.add(pid)
+
+if bad_pids:
+ print """
+The following processes are listening for packets to arrive on network devices
+that no longer exist. You may want to restart them."""
+ os.execvp("ps", ["ps"] + ["%s" % pid for pid in bad_pids])
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#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"
struct switch_ {
struct lswitch *lswitch;
- struct rconn *rconn;
};
/* -H, --hub: Learn the ports on which MAC addresses appear? */
/* -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;
/* -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;
/* --unixctl: Name of unixctl socket, or null to use the default. */
static char *unixctl_path = NULL;
-static int do_switching(struct switch_ *);
static void new_switch(struct switch_ *, struct vconn *);
static void parse_options(int argc, char *argv[]);
static void usage(void) NO_RETURN;
daemonize_complete();
while (n_switches > 0 || n_listeners > 0) {
- int iteration;
-
/* Accept connections on listening vconns. */
for (i = 0; i < n_listeners && n_switches < MAX_SWITCHES; ) {
struct vconn *new_vconn;
}
}
- /* Do some switching work. Limit the number of iterations so that
- * callbacks registered with the poll loop don't starve. */
- for (iteration = 0; iteration < 50; iteration++) {
- bool progress = false;
- for (i = 0; i < n_switches; ) {
- struct switch_ *this = &switches[i];
-
- retval = do_switching(this);
- if (!retval || retval == EAGAIN) {
- if (!retval) {
- progress = true;
- }
- i++;
- } else {
- rconn_destroy(this->rconn);
- lswitch_destroy(this->lswitch);
- switches[i] = switches[--n_switches];
- }
- }
- if (!progress) {
- break;
- }
- }
- for (i = 0; i < n_switches; i++) {
+ /* Do some switching work. . */
+ for (i = 0; i < n_switches; ) {
struct switch_ *this = &switches[i];
lswitch_run(this->lswitch);
+ if (lswitch_is_alive(this->lswitch)) {
+ i++;
+ } else {
+ lswitch_destroy(this->lswitch);
+ switches[i] = switches[--n_switches];
+ }
}
unixctl_server_run(unixctl);
}
for (i = 0; i < n_switches; i++) {
struct switch_ *sw = &switches[i];
- rconn_run_wait(sw->rconn);
- rconn_recv_wait(sw->rconn);
lswitch_wait(sw->lswitch);
}
unixctl_server_wait(unixctl);
new_switch(struct switch_ *sw, struct vconn *vconn)
{
struct lswitch_config cfg;
+ struct rconn *rconn;
- sw->rconn = rconn_create(60, 0, DSCP_DEFAULT);
- rconn_connect_unreliably(sw->rconn, vconn, NULL);
+ rconn = rconn_create(60, 0, DSCP_DEFAULT);
+ rconn_connect_unreliably(rconn, vconn, NULL);
cfg.mode = (action_normal ? LSW_NORMAL
: learn_macs ? LSW_LEARN
cfg.n_default_flows = n_default_flows;
cfg.default_queue = default_queue;
cfg.port_queues = &port_queues;
- sw->lswitch = lswitch_create(sw->rconn, &cfg);
-}
-
-static int
-do_switching(struct switch_ *sw)
-{
- unsigned int packets_sent;
- struct ofpbuf *msg;
-
- packets_sent = rconn_packets_sent(sw->rconn);
-
- msg = rconn_recv(sw->rconn);
- if (msg) {
- if (!mute) {
- lswitch_process_packet(sw->lswitch, sw->rconn, msg);
- }
- ofpbuf_delete(msg);
- }
- rconn_run(sw->rconn);
-
- return (!rconn_is_alive(sw->rconn) ? EOF
- : rconn_packets_sent(sw->rconn) != packets_sent ? 0
- : EAGAIN);
+ cfg.mute = mute;
+ sw->lswitch = lswitch_create(rconn, &cfg);
}
static void
"\"<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");
}
}
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");
.
.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
.IP 6.
Restores the kernel configuration state that was saved in step 3.
.
-.PP
-The steps above are often enough to hot-upgrade a new kernel module
-with only a few seconds of downtime. DHCP is a common problem: if the
-ISC DHCP client is running on an OVS internal interface, then it will
-have to be restarted after completing the above procedure.
+.IP 7.
+Checks for daemons that may need to be restarted because they have
+packet sockets that are listening on old instances of Open vSwitch
+kernel interfaces and, if it finds any, prints a warning on stdout.
+DHCP is a common example: if the ISC DHCP client is running on an OVS
+internal interface, then it will have to be restarted after completing
+the above procedure. (It would be nice if \fBovs\-ctl\fR could restart
+daemons automatically, but the details are far too specific to a
+particular distribution and installation.)
.
.PP
\fBforce\-kmod\-reload\fR internally stops and starts OVS, so it
.
.IP "\fBOVS_LOGDIR\fR"
.IQ "\fBOVS_RUNDIR\fR"
+.IQ "\fBOVS_DBDIR\fR"
.IQ "\fBOVS_SYSCONFDIR\fR"
.IQ "\fBOVS_PKGDATADIR\fR"
.IQ "\fBOVS_BINDIR\fR"
The OVS database schema used to initialize the database (use
\fB\-\-db\-schema to override this location).
.
-.IP "\fIsysconfdir\fB/openvswitch/conf.db\fR"
+.IP "\fIdbdir\fB/conf.db\fR"
The OVS database (use \fB\-\-db\-file\fR to override this location).
.
.IP "\fIrundir\fB/openvswitch/db.sock\fR"
#! /bin/sh
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
insert_mod_if_required () {
insert_openvswitch_mod_if_required || return 1
if test X"$BRCOMPAT" = Xyes; then
- insert_brcompat_mod_if_required || return 1
+ if insert_brcompat_mod_if_required; then
+ :
+ else
+ log_warning_msg "could not load brcompat module, disabling bridge compatibility"
+ BRCOMPAT=no
+ fi
fi
}
}
ovsdb_tool () {
- ovsdb-tool -vANY:console:off "$@"
+ ovsdb-tool -vconsole:off "$@"
}
create_db () {
action "Configuring Open vSwitch system IDs" "$@" $extra_ids
}
-start () {
+check_force_cores () {
if test X"$FORCE_COREFILES" = Xyes; then
ulimit -Sc 67108864
fi
+}
- insert_mod_if_required || return 1
+start_ovsdb () {
+ check_force_cores
if daemon_is_running ovsdb-server; then
- log_success_msg "ovsdb-server is already running"
+ log_success_msg "ovsdb-server is already running"
else
- # Create initial database or upgrade database schema.
- upgrade_db || return 1
-
- # Start ovsdb-server.
- set ovsdb-server "$DB_FILE"
- set "$@" -vANY:CONSOLE:EMER -vANY:SYSLOG:ERR -vANY:FILE:INFO
- set "$@" --remote=punix:"$DB_SOCK"
- set "$@" --remote=db:Open_vSwitch,manager_options
- set "$@" --private-key=db:SSL,private_key
- set "$@" --certificate=db:SSL,certificate
- set "$@" --bootstrap-ca-cert=db:SSL,ca_cert
- start_daemon "$OVSDB_SERVER_PRIORITY" "$@" || return 1
-
- # Initialize database settings.
- ovs_vsctl -- init -- set Open_vSwitch . db-version="$schemaver" \
- || return 1
- set_system_ids || return 1
- if test X"$DELETE_BRIDGES" = Xyes; then
+ # Create initial database or upgrade database schema.
+ upgrade_db || return 1
+
+ # Start ovsdb-server.
+ set ovsdb-server "$DB_FILE"
+ set "$@" -vconsole:emer -vsyslog:err -vfile:info
+ set "$@" --remote=punix:"$DB_SOCK"
+ set "$@" --remote=db:Open_vSwitch,manager_options
+ set "$@" --private-key=db:SSL,private_key
+ set "$@" --certificate=db:SSL,certificate
+ set "$@" --bootstrap-ca-cert=db:SSL,ca_cert
+ start_daemon "$OVSDB_SERVER_PRIORITY" "$OVSDB_SERVER_WRAPPER" "$@" \
+ || return 1
+
+ # Initialize database settings.
+ ovs_vsctl -- init -- set Open_vSwitch . db-version="$schemaver" \
+ || return 1
+ set_system_ids || return 1
+ if test X"$DELETE_BRIDGES" = Xyes; then
for bridge in `ovs_vsctl list-br`; do
- ovs_vsctl del-br $bridge
+ ovs_vsctl del-br $bridge
done
- fi
+ fi
fi
+}
+
+start_forwarding () {
+ check_force_cores
+
+ insert_mod_if_required || return 1
if daemon_is_running ovs-vswitchd; then
- log_success_msg "ovs-vswitchd is already running"
+ log_success_msg "ovs-vswitchd is already running"
else
- # Increase the limit on the number of open file descriptors.
- # ovs-vswitchd needs 16 per datapath, plus a few extra, so this
- # should allow for 256 (or more) bridges.
- ulimit -n 5000
-
- # Start ovs-vswitchd.
- set ovs-vswitchd unix:"$DB_SOCK"
- set "$@" -vANY:CONSOLE:EMER -vANY:SYSLOG:ERR -vANY:FILE:INFO
- if test X"$MLOCKALL" != Xno; then
- set "$@" --mlockall
- fi
- start_daemon "$OVS_VSWITCHD_PRIORITY" "$@"
+ # Increase the limit on the number of open file descriptors.
+ # ovs-vswitchd needs 16 per datapath, plus a few extra, so this
+ # should allow for 256 (or more) bridges.
+ ulimit -n 5000
+
+ # Start ovs-vswitchd.
+ set ovs-vswitchd unix:"$DB_SOCK"
+ set "$@" -vconsole:emer -vsyslog:err -vfile:info
+ if test X"$MLOCKALL" != Xno; then
+ set "$@" --mlockall
+ fi
+ start_daemon "$OVS_VSWITCHD_PRIORITY" "$OVS_VSWITCHD_WRAPPER" "$@"
fi
if daemon_is_running ovs-brcompatd; then
- log_success_msg "ovs-brcompatd is already running"
+ log_success_msg "ovs-brcompatd is already running"
elif test X"$BRCOMPAT" = Xyes; then
set ovs-brcompatd
- set "$@" -vANY:CONSOLE:EMER -vANY:SYSLOG:ERR -vANY:FILE:INFO
- start_daemon "$OVS_BRCOMPATD_PRIORITY" "$@"
+ set "$@" -vconsole:emer -vsyslog:err -vfile:info
+ start_daemon "$OVS_BRCOMPATD_PRIORITY" "$OVS_BRCOMPATD_WRAPPER" "$@"
fi
}
## stop ##
## ---- ##
-stop () {
+stop_ovsdb () {
+ stop_daemon ovsdb-server
+}
+
+stop_forwarding () {
stop_daemon ovs-brcompatd
stop_daemon ovs-vswitchd
- stop_daemon ovsdb-server
}
## ----------------- ##
# But ignore interfaces that don't really exist.
for d in `(ovs_vsctl --bare \
-- --columns=name find Interface type=internal \
- -- list-br) | sort -u`
+ -- list-br) | sort -u`
do
if test -e "/sys/class/net/$d"; then
- printf "%s " "$d"
- fi
+ printf "%s " "$d"
+ fi
done
}
ifaces=`internal_interfaces`
action "Detected internal interfaces: $ifaces" true
- stop
+ # Restart the database first, since a large database may take a
+ # while to load, and we want to minimize forwarding disruption.
+ stop_ovsdb
+ start_ovsdb
+
+ stop_forwarding
script=`mktemp`
trap 'rm -f "$script"' 0 1 2 13 15
:
else
log_warning_msg "Failed to save configuration, not replacing kernel module"
- start
+ start_forwarding
exit 1
fi
chmod +x "$script"
action "Removing openvswitch module" rmmod openvswitch
fi
- start
+ start_forwarding
action "Restoring interface configuration" "$script"
rc=$?
log="logger -p daemon.$level -t ovs-save"
$log "force-reload-kmod interface restore script exited with status $rc:"
$log -f "$script"
+
+ "$datadir/scripts/ovs-check-dead-ifs"
}
## --------------- ##
OVSDB_SERVER_PRIORITY=-10
OVS_VSWITCHD_PRIORITY=-10
OVS_BRCOMPATD_PRIORITY=-10
+ OVSDB_SERVER_WRAPPER=
+ OVS_VSWITCHD_WRAPPER=
+ OVS_BRCOMPATD_WRAPPER=
- DB_FILE=$etcdir/conf.db
+ DB_FILE=$dbdir/conf.db
DB_SOCK=$rundir/db.sock
DB_SCHEMA=$datadir/vswitch.ovsschema
--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
-V, --version display version information
Default directories with "configure" option and environment variable override:
- logs: @LOGDIR@ (--log-dir, OVS_LOGDIR)
- pidfiles and sockets: @RUNDIR@ (--run-dir, OVS_RUNDIR)
+ logs: @LOGDIR@ (--with-logdir, OVS_LOGDIR)
+ pidfiles and sockets: @RUNDIR@ (--with-rundir, OVS_RUNDIR)
+ conf.db: @DBDIR@ (--with-dbdir, OVS_DBDIR)
system configuration: @sysconfdir@ (--sysconfdir, OVS_SYSCONFDIR)
data files: @pkgdatadir@ (--pkgdatadir, OVS_PKGDATADIR)
user binaries: @bindir@ (--bindir, OVS_BINDIR)
done
case $command in
start)
- start
+ start_ovsdb
+ start_forwarding
;;
stop)
- stop
+ stop_forwarding
+ stop_ovsdb
;;
status)
rc=0
done
;;
force-reload-kmod)
- force_reload_kmod
+ force_reload_kmod
;;
load-kmod)
insert_mod_if_required
.SH DESCRIPTION
.PP
The \fBovs\-dpctl\fR program can create, modify, and delete Open vSwitch
-datapaths. A single machine may host up to 256 datapaths (numbered 0
-to 255).
+datapaths. A single machine may host any number of datapaths.
.PP
A newly created datapath is associated with only one network device, a
virtual network device sometimes called the datapath's ``local port''.
.RS
.IP "\fBtype=\fItype\fR"
Specifies the type of port to add. The default type is \fBsystem\fR.
+.IP "\fBport_no=\fIport\fR"
+Requests a specific port number within the datapath. If this option is
+not specified then one will be automatically assigned.
.IP "\fIkey\fB=\fIvalue\fR"
Adds an arbitrary key-value option to the port's configuration.
.RE
\fIoption\fR of the form \fIkey\fB=\fIvalue\fR adds the specified
key-value option to the port or overrides an existing key's value. An
\fIoption\fR of the form \fIkey\fB=\fR, that is, without a value,
-deletes the key-value named \fIkey\fR. The type of a port cannot be
-changed, so \fBtype=\fItype\fR is only allowed if \fItype\fR is the
-port's existing type.
+deletes the key-value named \fIkey\fR. The type and port number of a
+port cannot be changed, so \fBtype\fR and \fBport_no\fR are only allowed if
+they match the existing configuration.
.TP
\fBdel\-if \fIdp netdev\fR...
Removes each \fInetdev\fR from the list of network devices datapath
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "ofpbuf.h"
#include "packets.h"
#include "shash.h"
+#include "simap.h"
+#include "smap.h"
#include "sset.h"
#include "timeval.h"
#include "util.h"
}
}
\f
-static void do_add_if(int argc, char *argv[]);
+static void dpctl_add_if(int argc, char *argv[]);
static int if_up(const char *netdev_name)
{
}
static void
-do_add_dp(int argc OVS_UNUSED, char *argv[])
+dpctl_add_dp(int argc OVS_UNUSED, char *argv[])
{
struct dpif *dpif;
run(parsed_dpif_open(argv[1], true, &dpif), "add_dp");
dpif_close(dpif);
if (argc > 2) {
- do_add_if(argc, argv);
+ dpctl_add_if(argc, argv);
}
}
static void
-do_del_dp(int argc OVS_UNUSED, char *argv[])
+dpctl_del_dp(int argc OVS_UNUSED, char *argv[])
{
struct dpif *dpif;
run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
}
static void
-do_add_if(int argc OVS_UNUSED, char *argv[])
+dpctl_add_if(int argc OVS_UNUSED, char *argv[])
{
bool failure = false;
struct dpif *dpif;
const char *name, *type;
char *save_ptr = NULL;
struct netdev *netdev = NULL;
- struct shash args;
+ struct smap args;
+ uint16_t port_no = UINT16_MAX;
char *option;
int error;
continue;
}
- shash_init(&args);
+ smap_init(&args);
while ((option = strtok_r(NULL, ",", &save_ptr)) != NULL) {
char *save_ptr_2 = NULL;
char *key, *value;
if (!strcmp(key, "type")) {
type = value;
- } else if (!shash_add_once(&args, key, value)) {
+ } else if (!strcmp(key, "port_no")) {
+ port_no = atoi(value);
+ } else if (!smap_add_once(&args, key, value)) {
ovs_error(0, "duplicate \"%s\" option", key);
}
}
goto next;
}
- error = dpif_port_add(dpif, netdev, NULL);
+ error = dpif_port_add(dpif, netdev, &port_no);
if (error) {
ovs_error(error, "adding %s to %s failed", name, argv[1]);
goto next;
}
static void
-do_set_if(int argc, char *argv[])
+dpctl_set_if(int argc, char *argv[])
{
bool failure = false;
struct dpif *dpif;
char *save_ptr = NULL;
char *type = NULL;
const char *name;
- struct shash args;
+ struct smap args;
+ uint32_t port_no;
char *option;
int error;
goto next;
}
type = xstrdup(dpif_port.type);
+ port_no = dpif_port.port_no;
dpif_port_destroy(&dpif_port);
/* Retrieve its existing configuration. */
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);
name, type, value);
failure = true;
}
+ } else if (!strcmp(key, "port_no")) {
+ if (port_no != atoi(value)) {
+ ovs_error(0, "%s: can't change port number from "
+ "%"PRIu32" to %d",
+ name, port_no, atoi(value));
+ failure = true;
+ }
} else if (value[0] == '\0') {
- free(shash_find_and_delete(&args, key));
+ smap_remove(&args, key);
} else {
- free(shash_replace(&args, key, xstrdup(value)));
+ smap_replace(&args, key, value);
}
}
}
static void
-do_del_if(int argc OVS_UNUSED, char *argv[])
+dpctl_del_if(int argc OVS_UNUSED, char *argv[])
{
bool failure = false;
struct dpif *dpif;
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 {
}
static void
-do_show(int argc, char *argv[])
+dpctl_show(int argc, char *argv[])
{
bool failure = false;
if (argc > 1) {
}
static void
-do_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+dpctl_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
struct sset dpif_names, dpif_types;
const char *type;
}
static void
-do_dump_flows(int argc OVS_UNUSED, char *argv[])
+dpctl_dump_flows(int argc OVS_UNUSED, char *argv[])
{
const struct dpif_flow_stats *stats;
const struct nlattr *actions;
}
static void
-do_del_flows(int argc OVS_UNUSED, char *argv[])
+dpctl_del_flows(int argc OVS_UNUSED, char *argv[])
{
struct dpif *dpif;
}
static void
-do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+dpctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
usage();
}
/* Undocumented commands for unit testing. */
static void
-do_parse_actions(int argc, char *argv[])
+dpctl_parse_actions(int argc, char *argv[])
{
int i;
* The idea here generalizes beyond VLANs (e.g. to setting other fields) but
* so far the implementation only covers VLANs. */
static void
-do_normalize_actions(int argc, char *argv[])
+dpctl_normalize_actions(int argc, char *argv[])
{
- struct shash port_names;
+ struct simap port_names;
struct ofpbuf keybuf;
struct flow flow;
struct ofpbuf odp_actions;
ds_init(&s);
- shash_init(&port_names);
+ simap_init(&port_names);
for (i = 3; i < argc; i++) {
char name[16];
int number;
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]);
}
}
static const struct command all_commands[] = {
- { "add-dp", 1, INT_MAX, do_add_dp },
- { "del-dp", 1, 1, do_del_dp },
- { "add-if", 2, INT_MAX, do_add_if },
- { "del-if", 2, INT_MAX, do_del_if },
- { "set-if", 2, INT_MAX, do_set_if },
- { "dump-dps", 0, 0, do_dump_dps },
- { "show", 0, INT_MAX, do_show },
- { "dump-flows", 1, 1, do_dump_flows },
- { "del-flows", 1, 1, do_del_flows },
- { "help", 0, INT_MAX, do_help },
+ { "add-dp", 1, INT_MAX, dpctl_add_dp },
+ { "del-dp", 1, 1, dpctl_del_dp },
+ { "add-if", 2, INT_MAX, dpctl_add_if },
+ { "del-if", 2, INT_MAX, dpctl_del_if },
+ { "set-if", 2, INT_MAX, dpctl_set_if },
+ { "dump-dps", 0, 0, dpctl_dump_dps },
+ { "show", 0, INT_MAX, dpctl_show },
+ { "dump-flows", 1, 1, dpctl_dump_flows },
+ { "del-flows", 1, 1, dpctl_del_flows },
+ { "help", 0, INT_MAX, dpctl_help },
/* Undocumented commands for testing. */
- { "parse-actions", 1, INT_MAX, do_parse_actions },
- { "normalize-actions", 2, INT_MAX, do_normalize_actions },
+ { "parse-actions", 1, INT_MAX, dpctl_parse_actions },
+ { "normalize-actions", 2, INT_MAX, dpctl_normalize_actions },
{ NULL, 0, 0, NULL },
};
--- /dev/null
+.de IQ
+. br
+. ns
+. IP "\\$1"
+..
+.TH ovs\-l3ping 1 "June 2012" "Open vSwitch" "Open vSwitch Manual"
+.
+.SH NAME
+\fBovs\-l3ping\fR \- check network deployment for L3 tunneling
+problems
+.
+.SH SYNOPSIS
+\fBovs\-l3ping\fR \fB\-s\fR \fITunnelRemoteIP,InnerIP[/mask]\fR \fB\-t\fR \fItunnelmode\fR
+.br
+\fBovs\-l3ping\fR \fB\-s\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR \fB\-t\fR \fItunnelmode\fR
+.PP
+\fBovs\-l3ping\fR \fB\-c\fR \fITunnelRemoteIP,InnerIP[/mask],RemoteInnerIP\fR \fB\-t\fR \fItunnelmode\fR
+.br
+\fBovs\-l3ping\fR \fB\-c\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\
+[:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR
+[\fB\-b\fR \fItargetbandwidth\fR] [\fB\-i\fR \fItestinterval\fR]
+\fB\-t\fR \fItunnelmode\fR
+.so lib/common-syn.man
+.
+.SH DESCRIPTION
+The \fBovs\-l3ping\fR program may be used to check for problems that could
+be caused by invalid routing policy, misconfigured firewall in the tunnel
+path or a bad NIC driver. On one of the nodes, run \fBovs\-l3ping\fR in
+server mode and on the other node run it in client mode. The client and
+server will establish L3 tunnel, over which client will give further testing
+instructions. The \fBovs\-l3ping\fR client will perform UDP and TCP tests.
+This tool is different from \fBovs\-test\fR that it encapsulates XML/RPC
+control connection over the tunnel, so there is no need to open special holes
+in firewall.
+.PP
+UDP tests can report packet loss and achieved bandwidth for various
+datagram sizes. By default target bandwidth for UDP tests is 1Mbit/s.
+.PP
+TCP tests report only achieved bandwidth, because kernel TCP stack
+takes care of flow control and packet loss.
+.
+.SS "Client Mode"
+An \fBovs\-l3ping\fR client will create a L3 tunnel and connect over it to the
+\fBovs\-l3ping\fR server to schedule the tests. \fITunnelRemoteIP\fR is the
+peer's IP address, where tunnel will be terminated. \fIInnerIP\fR is the
+address that will be temporarily assigned during testing. All test traffic
+originating from this IP address to the \fIRemoteInnerIP\fR will be tunneled.
+It is possible to override default \fIControlPort\fR and \fIDataPort\fR, if
+there is any other application that already listens on those two ports.
+.
+.SS "Server Mode"
+To conduct tests, \fBovs\-l3ping\fR server must be running. It is required
+that both client and server \fIInnerIP\fR addresses are in the same subnet.
+It is possible to specify \fIInnerIP\fR with netmask in CIDR format.
+.
+.SH OPTIONS
+One of \fB\-s\fR or \fB\-c\fR is required. The \fB\-t\fR option is
+also required.
+.
+.IP "\fB\-s \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR"
+.IQ "\fB\-\-server\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR"
+Run in server mode and create L3 tunnel with the client that will be
+accepting tunnel at \fITunnelRemoteIP\fR address. The socket on
+\fIInnerIP[:ControlPort]\fR will be used to receive further instructions
+from the client.
+.
+.IP "\fB\-c \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\
+[:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR"
+.IQ "\fB\-\-client \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\
+[:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR"
+Run in client mode and create L3 tunnel with the server on
+\fITunnelRemoteIP\fR. The client will use \fIInnerIP\fR to generate test
+traffic with the server's \fIRemoteInnerIP\fR.
+.
+.IP "\fB\-b \fItargetbandwidth\fR"
+.IQ "\fB\-\-bandwidth\fR \fItargetbandwidth\fR"
+Target bandwidth for UDP tests. The \fItargetbandwidth\fR must be given in
+bits per second. It is possible to use postfix M or K to alter the target
+bandwidth magnitude.
+.
+.IP "\fB\-i \fItestinterval\fR"
+.IQ "\fB\-\-interval\fR \fItestinterval\fR"
+How long each test should run. By default 5 seconds.
+.
+.IP "\fB\-t \fItunnelmode\fR"
+.IQ "\fB\-\-tunnel\-mode\fR \fItunnelmode\fR"
+Specify the tunnel type. This option must match on server and client.
+.
+.so lib/common.man
+.
+.SH EXAMPLES
+.PP
+On host 192.168.122.220 start \fBovs\-l3ping\fR in server mode. This command
+will create a temporary GRE tunnel with the host 192.168.122.236 and assign
+10.1.1.1/28 as the inner IP address, where client will have to connect:
+.IP
+.B ovs\-l3ping -s 192.168.122.236,10.1.1.1/28 -t gre
+.
+.PP
+On host 192.168.122.236 start \fBovs\-l3ping\fR in client mode. This command
+will use 10.1.1.2/28 as the local inner IP address and will connect over the
+L3 tunnel to the server's inner IP address at 10.1.1.1.
+.IP
+.B ovs\-l3ping -c 192.168.122.220,10.1.1.2/28,10.1.1.1 -t gre
+.
+.SH SEE ALSO
+.
+.BR ovs\-vswitchd (8),
+.BR ovs\-ofctl (8),
+.BR ovs\-vsctl (8),
+.BR ovs\-vlan\-test (8),
+.BR ovs\-test (8),
+.BR ethtool (8),
+.BR uname (1)
--- /dev/null
+#! @PYTHON@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+ovs L3 ping utility allows to do tests between two remote hosts without
+opening holes in the firewall for the XML RPC control connection. This is
+achieved by tunneling the control connection inside the tunnel itself.
+"""
+
+import socket
+import xmlrpclib
+
+import ovstest.args as args
+import ovstest.tests as tests
+import ovstest.util as util
+
+
+def get_packet_sizes(me, he, remote_ip):
+ """
+ This function retrieves MTUs from both hosts and returns a list of
+ packet sizes, that are more likely to uncover possible configuration
+ issues.
+ """
+ mtu_node1 = 1500
+ mtu_node2 = 1500
+ server1 = util.rpc_client(me[0], me[1])
+ server2 = util.rpc_client(he[0], he[1])
+ iface1 = server2.get_interface(remote_ip)
+ iface2 = server1.get_interface_from_routing_decision(remote_ip)
+ if iface1:
+ mtu_node1 = server2.get_interface_mtu(iface1)
+ if iface2:
+ mtu_node2 = server1.get_interface_mtu(iface2)
+ return util.get_datagram_sizes(mtu_node1, mtu_node2)
+
+
+if __name__ == '__main__':
+ local_server = None
+ try:
+ args = args.l3_initialize_args()
+ tunnel_mode = args.tunnelMode
+ if args.server is not None: # Start in server mode
+ local_server = tests.configure_l3(args.server, tunnel_mode)
+ local_server.wait()
+ elif args.client is not None: # Run in client mode
+ bandwidth = args.targetBandwidth
+ interval = args.testInterval
+ me = (util.ip_from_cidr(args.client[1][0]), args.client[1][1],
+ args.client[1][0], args.client[1][2])
+ he = (args.client[2][0], args.client[2][1],
+ args.client[2][0], args.client[2][2])
+ local_server = tests. configure_l3(args.client, tunnel_mode)
+ ps = get_packet_sizes(me, he, args.client[0])
+ tests.do_direct_tests(me, he, bandwidth, interval, ps)
+ except KeyboardInterrupt:
+ print "Terminating"
+ except xmlrpclib.Fault:
+ print "Couldn't contact peer"
+ except socket.error:
+ print "Couldn't contact peer"
+ except xmlrpclib.ProtocolError:
+ print "XMLRPC control channel was abruptly terminated"
+ finally:
+ if local_server is not None:
+ local_server.terminate()
# This is a shell function library sourced by some Open vSwitch scripts.
# It is not intended to be invoked on its own.
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
bindir=${OVS_BINDIR-'@bindir@'} # /usr/bin
sbindir=${OVS_SBINDIR-'@sbindir@'} # /usr/sbin
+# /etc/openvswitch or /var/lib/openvswitch
+if test X"$OVS_DBDIR" != X; then
+ dbdir=$OVS_DBDIR
+elif test X"$OVS_SYSCONFDIR" != X; then
+ dbdir=$OVS_SYSCONFDIR/openvswitch
+else
+ dbdir='@DBDIR@'
+fi
+
VERSION='@VERSION@'
LC_ALL=C; export LC_ALL
start_daemon () {
priority=$1
- shift
+ wrapper=$2
+ shift; shift
daemon=$1
+ strace=""
# drop core files in a sensible place
test -d "$DAEMON_CWD" || install -d -m 755 -o root -g root "$DAEMON_CWD"
set "$@" --pidfile="$rundir/$daemon.pid"
set "$@" --detach --monitor
+ # wrapper
+ case $wrapper in
+ valgrind)
+ if (valgrind --version) > /dev/null 2>&1; then
+ set valgrind -q --leak-check=full --time-stamp=yes \
+ --log-file="$logdir/$daemon.valgrind.log.%p" "$@"
+ else
+ log_failure_msg "valgrind not installed, running $daemon without it"
+ fi
+ ;;
+ strace)
+ if (strace -V) > /dev/null 2>&1; then
+ strace="strace -tt -T -s 256 -ff"
+ if (strace -DV) > /dev/null 2>&1; then
+ # Has the -D option.
+ set $strace -D -o "$logdir/$daemon.strace.log" "$@"
+ strace=""
+ fi
+ else
+ log_failure_msg "strace not installed, running $daemon without it"
+ fi
+ ;;
+ '')
+ ;;
+ *)
+ log_failure_msg "unknown wrapper $wrapper, running $daemon without it"
+ ;;
+ esac
+
# priority
if test X"$priority" != X; then
set nice -n "$priority" "$@"
fi
action "Starting $daemon" "$@"
+
+ if test X"$strace" != X; then
+ # Strace doesn't have the -D option so we attach after the fact.
+ setsid $strace -o "$logdir/$daemon.strace.log" \
+ -p `cat $rundir/$daemon.pid` > /dev/null 2>&1 &
+ fi
}
DAEMON_CWD=/
OpenFlow assigned port number or device name, e.g. \fBeth0\fR.
.
.TP
+\fBdump\-ports\-desc \fIswitch\fR
+Prints to the console detailed information about network devices
+associated with \fIswitch\fR (version 1.7 or later). This is a subset
+of the information provided by the \fBshow\fR command.
+.
+.TP
\fBmod\-port \fIswitch\fR \fInetdev\fR \fIaction\fR
Modify characteristics of an interface monitored by \fIswitch\fR.
\fInetdev\fR can be referred to by its OpenFlow assigned port number or
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,
Prints to the console all flow entries in \fIswitch\fR's
tables that match \fIflows\fR. If \fIflows\fR is omitted, all flows
in the switch are retrieved. See \fBFlow Syntax\fR, below, for the
-syntax of \fIflows\fR. The output format is described in
+syntax of \fIflows\fR. The output format is described in
\fBTable Entry Output\fR.
.
+.IP
+By default, \fBovs\-ofctl\fR prints flow entries in the same order
+that the switch sends them, which is unlikely to be intuitive or
+consistent. See the description of \fB\-\-sort\fR and \fB\-\-rsort\fR,
+under \fBOPTIONS\fR below, to influence the display order.
+.
.TP
\fBdump\-aggregate \fIswitch \fR[\fIflows\fR]
-Prints to the console aggregate statistics for flows in
+Prints to the console aggregate statistics for flows in
\fIswitch\fR's tables that match \fIflows\fR. If \fIflows\fR is omitted,
the statistics are aggregated across all flows in the switch's flow
tables. See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR.
the configured controller is disconnected, no traffic is sent, so
monitoring will not show any traffic.
.
-.IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR] [\fIinvalid_ttl\fR]"
+.IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR] [\fBinvalid_ttl\fR] [\fBwatch:\fR[\fIspec\fR...]]"
Connects to \fIswitch\fR and prints to the console all OpenFlow
messages received. Usually, \fIswitch\fR should specify the name of a
bridge in the \fBovs\-vswitchd\fR database.
specified on this argument. (Thus, if \fImiss\-len\fR is not
specified, very little traffic will ordinarily be printed.)
.IP
-.IP
If \fBinvalid_ttl\fR is passed, \fBovs\-ofctl\fR sends an OpenFlow ``set
configuration'' message at connection setup time that requests
-\fIINVALID_TTL_TO_CONTROLLER\fR, so that \fBovs\-ofctl monitor\fR can
-receive ``packets-in'' messages when TTL reaches zero on \fBdec_ttl\fR action.
+\fBINVALID_TTL_TO_CONTROLLER\fR, so that \fBovs\-ofctl monitor\fR can
+receive ``packet-in'' messages when TTL reaches zero on \fBdec_ttl\fR action.
+.IP
+\fBwatch:\fR[\fB\fIspec\fR...] causes \fBovs\-ofctl\fR to send a
+``monitor request'' Nicira extension message to the switch at
+connection setup time. This message causes the switch to send
+information about flow table changes as they occur. The following
+comma-separated \fIspec\fR syntax is available:
+.RS
+.IP "\fB!initial\fR"
+Do not report the switch's initial flow table contents.
+.IP "\fB!add\fR"
+Do not report newly added flows.
+.IP "\fB!delete\fR"
+Do not report deleted flows.
+.IP "\fB!modify\fR"
+Do not report modifications to existing flows.
+.IP "\fB!own\fR"
+Abbreviate changes made to the flow table by \fBovs\-ofctl\fR's own
+connection to the switch. (These could only occur using the
+\fBofctl/send\fR command described below under \fBRUNTIME MANAGEMENT
+COMMANDS\fR.)
+.IP "\fB!actions\fR"
+Do not report actions as part of flow updates.
+.IP "\fBtable=\fInumber\fR"
+Limits the monitoring to the table with the given \fInumber\fR between
+0 and 254. By default, all tables are monitored.
+.IP "\fBout_port=\fIport\fR"
+If set, only flows that output to \fIport\fR are monitored.
+.IP "\fIfield\fB=\fIvalue\fR"
+Monitors only flows that have \fIfield\fR specified as the given
+\fIvalue\fR. Any syntax valid for matching on \fBdump\-flows\fR may
+be used.
+.RE
.IP
-
This command may be useful for debugging switch or controller
-implementations.
+implementations. With \fBwatch:\fR, it is particularly useful for
+observing how a controller updates flow tables.
.
.SS "OpenFlow Switch and Controller Commands"
.
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,
\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
extension to OpenFlow, which as of this writing is only known to be
implemented by Open vSwitch.
.
+.IP \fBmetadata=\fIvalue\fR[\fB/\fImask\fR]
+Matches \fIvalue\fR either exactly or with optional \fImask\fR in the metadata
+field. \fIvalue\fR and \fImask\fR are 64-bit integers, by default in decimal
+(use a \fB0x\fR prefix to specify hexadecimal). Arbitrary \fImask\fR values
+are allowed: a 1-bit in \fImask\fR indicates that the corresponding bit in
+\fIvalue\fR must match exactly, and a 0-bit wildcards that bit. Matching on
+metadata was added in Open vSwitch 1.8.
+.
.PP
The following shorthand notations are also available:
.
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
actions were applied.
.
.IP \fBdec_ttl\fR
+.IQ \fBdec_ttl\fB[\fR(\fIid1,id2\fI)\fR]\fR
Decrement TTL of IPv4 packet or hop limit of IPv6 packet. If the
TTL or hop limit is initially zero, no decrement occurs. Instead,
a ``packet-in'' message with reason code \fBOFPR_INVALID_TTL\fR is
sent to each connected controller that has enabled receiving them,
-if any. Processing the current set of actions then stops.
-However, if the current set of actions was reached through
-``resubmit'' then remaining actions in outer levels resume
-processing.
+if any. Processing the current set of actions then stops. However,
+if the current set of actions was reached through ``resubmit'' then
+remaining actions in outer levels resume processing. This action
+also optionally supports the ability to specify a list of valid
+controller ids. Each of controllers in the list will receive the
+``packet_in'' message only if they have registered to receive the
+invalid ttl packets. If controller ids are not specified, the
+``packet_in'' message will be sent only to the controllers having
+controller id zero which have registered for the invalid ttl packets.
.
.IP \fBnote:\fR[\fIhh\fR]...
Does nothing at all. Any number of bytes represented as hex digits
\fIhh\fR may be included. Pairs of hex digits may be separated by
periods for readability.
+The \fBnote\fR action's format doesn't include an exact length for its
+payload, so the provided bytes will be padded on the right by enough
+bytes with value 0 to make the total number 6 more than a multiple of
+8.
.
.IP "\fBmove:\fIsrc\fB[\fIstart\fB..\fIend\fB]\->\fIdst\fB[\fIstart\fB..\fIend\fB]\fR"
Copies the named bits from field \fIsrc\fR to field \fIdst\fR.
Refer to \fBnicira\-ext.h\fR for more details.
.
.IP "\fBautopath(\fIid\fB, \fIdst\fB[\fIstart\fB..\fIend\fB])\fR"
+Deprecated and slated for removal in Feburary 2013.
+.IP
Given \fIid\fR, chooses an OpenFlow port and populates it in
\fIdst\fB[\fIstart\fB..\fIend\fB]\fR, which must be an NXM field as
described above.
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
have priority over an entry containing wildcards, so it has an implicit
priority value of 65535. When adding a flow, if the field is not specified,
the flow's priority will default to 32768.
+.IP
+OpenFlow leaves behavior undefined when two or more flows with the
+same priority can match a single packet. Some users expect
+``sensible'' behavior, such as more specific flows taking precedence
+over less specific flows, but OpenFlow does not specify this and Open
+vSwitch does not implement it. Users should therefore take care to
+use priorities to ensure the behavior that they expect.
.
.PP
The \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands
\fBovs\-ofctl\fR commands. Specify this option more than once to
increase verbosity further.
.
+.IP \fB\-\-sort\fR[\fB=\fIfield\fR]
+.IQ \fB\-\-rsort\fR[\fB=\fIfield\fR]
+Display output sorted by flow \fIfield\fR in ascending
+(\fB\-\-sort\fR) or descending (\fB\-\-rsort\fR) order, where
+\fIfield\fR is any of the fields that are allowed for matching or
+\fBpriority\fR to sort by priority. When \fIfield\fR is omitted, the
+output is sorted by priority. Specify these options multiple times to
+sort by multiple fields.
+.IP
+Any given flow will not necessarily specify a value for a given
+field. This requires special treatement:
+.RS
+.IP \(bu
+A flow that does not specify any part of a field that is used for sorting is
+sorted after all the flows that do specify the field. For example,
+\fB\-\-sort=tcp_src\fR will sort all the flows that specify a TCP
+source port in ascending order, followed by the flows that do not
+specify a TCP source port at all.
+.IP \(bu
+A flow that only specifies some bits in a field is sorted as if the
+wildcarded bits were zero. For example, \fB\-\-sort=nw_src\fR would
+sort a flow that specifies \fBnw_src=192.168.0.0/24\fR the same as
+\fBnw_src=192.168.0.0\fR.
+.RE
+.IP
+These options currently affect only \fBdump\-flows\fR output.
+.
.ds DD \
\fBovs\-ofctl\fR detaches only when executing the \fBmonitor\fR or \
\fBsnoop\fR commands.
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
#include <config.h>
+#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include "compiler.h"
#include "dirs.h"
#include "dynamic-string.h"
-#include "netlink.h"
#include "nx-match.h"
#include "odp-util.h"
+#include "ofp-actions.h"
#include "ofp-errors.h"
+#include "ofp-msgs.h"
#include "ofp-parse.h"
#include "ofp-print.h"
#include "ofp-util.h"
#include "util.h"
#include "vconn.h"
#include "vlog.h"
+#include "meta-flow.h"
+#include "sort.h"
VLOG_DEFINE_THIS_MODULE(ofctl);
* "snoop" command? */
static bool timestamp;
+/* --sort, --rsort: Sort order. */
+enum sort_order { SORT_ASC, SORT_DESC };
+struct sort_criterion {
+ const struct mf_field *field; /* NULL means to sort by priority. */
+ enum sort_order order;
+};
+static struct sort_criterion *criteria;
+static size_t n_criteria, allocated_criteria;
+
static const struct command all_commands[];
static void usage(void) NO_RETURN;
static void parse_options(int argc, char *argv[]);
+static bool recv_flow_stats_reply(struct vconn *, ovs_be32 send_xid,
+ struct ofpbuf **replyp,
+ struct ofputil_flow_stats *,
+ struct ofpbuf *ofpacts);
int
main(int argc, char *argv[])
{
return 0;
}
+static void
+add_sort_criterion(enum sort_order order, const char *field)
+{
+ struct sort_criterion *sc;
+
+ if (n_criteria >= allocated_criteria) {
+ criteria = x2nrealloc(criteria, &allocated_criteria, sizeof *criteria);
+ }
+
+ sc = &criteria[n_criteria++];
+ if (!field || !strcasecmp(field, "priority")) {
+ sc->field = NULL;
+ } else {
+ sc->field = mf_from_name(field);
+ if (!sc->field) {
+ ovs_fatal(0, "%s: unknown field name", field);
+ }
+ }
+ sc->order = order;
+}
+
static void
parse_options(int argc, char *argv[])
{
OPT_STRICT = UCHAR_MAX + 1,
OPT_READD,
OPT_TIMESTAMP,
+ OPT_SORT,
+ OPT_RSORT,
DAEMON_OPTION_ENUMS,
VLOG_OPTION_ENUMS
};
{"packet-in-format", required_argument, NULL, 'P'},
{"more", no_argument, NULL, 'm'},
{"timestamp", no_argument, NULL, OPT_TIMESTAMP},
+ {"sort", optional_argument, NULL, OPT_SORT},
+ {"rsort", optional_argument, NULL, OPT_RSORT},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
DAEMON_LONG_OPTIONS,
timestamp = true;
break;
+ case OPT_SORT:
+ add_sort_criterion(SORT_ASC, optarg);
+ break;
+
+ case OPT_RSORT:
+ add_sort_criterion(SORT_DESC, optarg);
+ break;
+
DAEMON_OPTION_HANDLERS
VLOG_OPTION_HANDLERS
STREAM_SSL_OPTION_HANDLERS
abort();
}
}
+
+ if (n_criteria) {
+ /* Always do a final sort pass based on priority. */
+ add_sort_criterion(SORT_DESC, "priority");
+ }
+
free(short_options);
}
" get-frags SWITCH print fragment handling behavior\n"
" set-frags SWITCH FRAG_MODE set fragment handling behavior\n"
" dump-ports SWITCH [PORT] print port statistics\n"
+ " dump-ports-desc SWITCH print port descriptions\n"
" dump-flows SWITCH print all flow entries\n"
" dump-flows SWITCH FLOW print matching FLOWs\n"
" dump-aggregate SWITCH print aggregate flow statistics\n"
" diff-flows SOURCE1 SOURCE2 compare flows from two sources\n"
" packet-out SWITCH IN_PORT ACTIONS PACKET...\n"
" execute ACTIONS on PACKET\n"
- " monitor SWITCH [MISSLEN] [invalid_ttl]\n"
+ " monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n"
" print packets received from SWITCH\n"
" snoop SWITCH snoop on SWITCH and its controller\n"
"\nFor OpenFlow switches and controllers:\n"
" -m, --more be more verbose printing OpenFlow\n"
" --timestamp (monitor, snoop) print timestamps\n"
" -t, --timeout=SECS give up after SECS seconds\n"
+ " --sort[=field] sort in ascending order\n"
+ " --rsort[=field] sort in descending order\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
exit(EXIT_SUCCESS);
return open_vconn__(name, "mgmt", vconnp);
}
-static void *
-alloc_stats_request(size_t rq_len, uint16_t type, struct ofpbuf **bufferp)
-{
- struct ofp_stats_msg *rq;
-
- rq = make_openflow(rq_len, OFPT10_STATS_REQUEST, bufferp);
- rq->type = htons(type);
- rq->flags = htons(0);
- return rq;
-}
-
static void
send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer)
{
- update_openflow_length(buffer);
+ ofpmsg_update_length(buffer);
run(vconn_send_block(vconn, buffer), "failed to send packet to switch");
}
struct vconn *vconn;
struct ofpbuf *reply;
- update_openflow_length(request);
+ ofpmsg_update_length(request);
open_vconn(vconn_name, &vconn);
run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
ofp_print(stdout, reply->data, reply->size, verbosity + 1);
}
static void
-dump_trivial_transaction(const char *vconn_name, uint8_t request_type)
+dump_trivial_transaction(const char *vconn_name, enum ofpraw raw)
{
struct ofpbuf *request;
- make_openflow(sizeof(struct ofp_header), request_type, &request);
+ request = ofpraw_alloc(raw, OFP10_VERSION, 0);
dump_transaction(vconn_name, request);
}
static void
-dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
+dump_stats_transaction(struct vconn *vconn, struct ofpbuf *request)
{
- ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid;
- struct vconn *vconn;
+ const struct ofp_header *request_oh = request->data;
+ ovs_be32 send_xid = request_oh->xid;
+ enum ofpraw request_raw;
+ enum ofpraw reply_raw;
bool done = false;
- open_vconn(vconn_name, &vconn);
+ ofpraw_decode_partial(&request_raw, request->data, request->size);
+ reply_raw = ofpraw_stats_request_to_reply(request_raw,
+ request_oh->version);
+
send_openflow_buffer(vconn, request);
while (!done) {
ovs_be32 recv_xid;
run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
recv_xid = ((struct ofp_header *) reply->data)->xid;
if (send_xid == recv_xid) {
- struct ofp_stats_msg *osm;
+ enum ofpraw raw;
ofp_print(stdout, reply->data, reply->size, verbosity + 1);
- osm = ofpbuf_at(reply, 0, sizeof *osm);
- done = !osm || !(ntohs(osm->flags) & OFPSF_REPLY_MORE);
+ ofpraw_decode(&raw, reply->data);
+ if (ofptype_from_ofpraw(raw) == OFPTYPE_ERROR) {
+ done = true;
+ } else if (raw == reply_raw) {
+ done = !ofpmp_more(reply->data);
+ } else {
+ ovs_fatal(0, "received bad reply: %s",
+ ofp_to_string(reply->data, reply->size,
+ verbosity + 1));
+ }
} else {
VLOG_DBG("received reply with xid %08"PRIx32" "
"!= expected %08"PRIx32, recv_xid, send_xid);
}
ofpbuf_delete(reply);
}
- vconn_close(vconn);
}
static void
-dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
+dump_trivial_stats_transaction(const char *vconn_name, enum ofpraw raw)
{
struct ofpbuf *request;
- alloc_stats_request(sizeof(struct ofp_stats_msg), stats_type, &request);
- dump_stats_transaction(vconn_name, request);
+ struct vconn *vconn;
+
+ open_vconn(vconn_name, &vconn);
+ request = ofpraw_alloc(raw, vconn_get_version(vconn), 0);
+ dump_stats_transaction(vconn, request);
+ vconn_close(vconn);
}
/* Sends 'request', which should be a request that only has a reply if an error
struct ofpbuf *request, *reply;
LIST_FOR_EACH (request, list_node, requests) {
- update_openflow_length(request);
+ ofpmsg_update_length(request);
}
run(vconn_transact_multiple_noreply(vconn, requests, &reply),
fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
{
struct ofp_switch_config *config;
- struct ofp_header *header;
struct ofpbuf *request;
struct ofpbuf *reply;
+ enum ofptype type;
- make_openflow(sizeof(struct ofp_header), OFPT_GET_CONFIG_REQUEST,
- &request);
+ request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST, OFP10_VERSION, 0);
run(vconn_transact(vconn, request, &reply),
"talking to %s", vconn_get_name(vconn));
- header = reply->data;
- if (header->type != OFPT_GET_CONFIG_REPLY ||
- header->length != htons(sizeof *config)) {
+ if (ofptype_pull(&type, reply) || type != OFPTYPE_GET_CONFIG_REPLY) {
ovs_fatal(0, "%s: bad reply to config request", vconn_get_name(vconn));
}
- config = reply->data;
+ config = ofpbuf_pull(reply, sizeof *config);
*config_ = *config;
ofpbuf_delete(reply);
}
static void
-set_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
+set_switch_config(struct vconn *vconn, const struct ofp_switch_config *config)
{
- struct ofp_switch_config *config;
- struct ofp_header save_header;
struct ofpbuf *request;
- config = make_openflow(sizeof *config, OFPT_SET_CONFIG, &request);
- save_header = config->header;
- *config = *config_;
- config->header = save_header;
+ request = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, OFP10_VERSION, 0);
+ ofpbuf_put(request, config, sizeof *config);
transact_noreply(vconn, request);
}
static void
-do_show(int argc OVS_UNUSED, char *argv[])
+ofctl_show(int argc OVS_UNUSED, char *argv[])
{
- dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
- dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
+ const char *vconn_name = argv[1];
+ struct vconn *vconn;
+ struct ofpbuf *request;
+ struct ofpbuf *reply;
+ bool trunc;
+
+ request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, OFP10_VERSION, 0);
+ open_vconn(vconn_name, &vconn);
+ run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
+
+ trunc = ofputil_switch_features_ports_trunc(reply);
+ ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+
+ ofpbuf_delete(reply);
+ vconn_close(vconn);
+
+ if (trunc) {
+ /* The Features Reply may not contain all the ports, so send a
+ * Port Description stats request, which doesn't have size
+ * constraints. */
+ dump_trivial_stats_transaction(vconn_name,
+ OFPRAW_OFPST_PORT_DESC_REQUEST);
+ }
+ dump_trivial_transaction(vconn_name, OFPRAW_OFPT_GET_CONFIG_REQUEST);
}
static void
-do_dump_desc(int argc OVS_UNUSED, char *argv[])
+ofctl_dump_desc(int argc OVS_UNUSED, char *argv[])
{
- dump_trivial_stats_transaction(argv[1], OFPST_DESC);
+ dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_DESC_REQUEST);
}
static void
-do_dump_tables(int argc OVS_UNUSED, char *argv[])
+ofctl_dump_tables(int argc OVS_UNUSED, char *argv[])
{
- dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
+ dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_TABLE_REQUEST);
}
-/* Opens a connection to 'vconn_name', fetches the port structure for
- * 'port_name' (which may be a port name or number), and copies it into
- * '*oppp'. */
-static void
-fetch_ofputil_phy_port(const char *vconn_name, const char *port_name,
- struct ofputil_phy_port *pp)
+static bool
+fetch_port_by_features(const char *vconn_name,
+ const char *port_name, unsigned int port_no,
+ struct ofputil_phy_port *pp, bool *trunc)
{
struct ofputil_switch_features features;
- const struct ofp_switch_features *osf;
+ const struct ofp_header *oh;
struct ofpbuf *request, *reply;
- unsigned int port_no;
struct vconn *vconn;
enum ofperr error;
+ enum ofptype type;
struct ofpbuf b;
-
- /* Try to interpret the argument as a port number. */
- if (!str_to_uint(port_name, 10, &port_no)) {
- port_no = UINT_MAX;
- }
+ bool found = false;
/* Fetch the switch's ofp_switch_features. */
- make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request);
+ request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, OFP10_VERSION, 0);
open_vconn(vconn_name, &vconn);
run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
+ vconn_close(vconn);
+
+ oh = reply->data;
+ if (ofptype_decode(&type, reply->data)
+ || type != OFPTYPE_FEATURES_REPLY) {
+ ovs_fatal(0, "%s: received bad features reply", vconn_name);
+ }
- osf = reply->data;
- if (reply->size < sizeof *osf) {
- ovs_fatal(0, "%s: received too-short features reply (only %zu bytes)",
- vconn_name, reply->size);
+ *trunc = false;
+ if (ofputil_switch_features_ports_trunc(reply)) {
+ *trunc = true;
+ goto exit;
}
- error = ofputil_decode_switch_features(osf, &features, &b);
+
+ error = ofputil_decode_switch_features(oh, &features, &b);
if (error) {
ovs_fatal(0, "%s: failed to decode features reply (%s)",
vconn_name, ofperr_to_string(error));
}
- while (!ofputil_pull_switch_features_port(&b, pp)) {
+ while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
if (port_no != UINT_MAX
? port_no == pp->port_no
: !strcmp(pp->name, port_name)) {
- ofpbuf_delete(reply);
- vconn_close(vconn);
- return;
+ found = true;
+ goto exit;
+ }
+ }
+
+exit:
+ ofpbuf_delete(reply);
+ return found;
+}
+
+static bool
+fetch_port_by_stats(const char *vconn_name,
+ const char *port_name, unsigned int port_no,
+ struct ofputil_phy_port *pp)
+{
+ struct ofpbuf *request;
+ struct vconn *vconn;
+ ovs_be32 send_xid;
+ bool done = false;
+ bool found = false;
+
+ request = ofpraw_alloc(OFPRAW_OFPST_PORT_DESC_REQUEST, OFP10_VERSION, 0);
+ send_xid = ((struct ofp_header *) request->data)->xid;
+
+ open_vconn(vconn_name, &vconn);
+ send_openflow_buffer(vconn, request);
+ while (!done) {
+ ovs_be32 recv_xid;
+ struct ofpbuf *reply;
+
+ run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
+ recv_xid = ((struct ofp_header *) reply->data)->xid;
+ if (send_xid == recv_xid) {
+ struct ofp_header *oh = reply->data;
+ enum ofptype type;
+ struct ofpbuf b;
+ uint16_t flags;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ if (ofptype_pull(&type, &b)
+ || type != OFPTYPE_PORT_DESC_STATS_REPLY) {
+ ovs_fatal(0, "received bad reply: %s",
+ ofp_to_string(reply->data, reply->size,
+ verbosity + 1));
+ }
+
+ flags = ofpmp_flags(oh);
+ done = !(flags & OFPSF_REPLY_MORE);
+
+ if (found) {
+ /* We've already found the port, but we need to drain
+ * the queue of any other replies for this request. */
+ continue;
+ }
+
+ while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
+ if (port_no != UINT_MAX ? port_no == pp->port_no
+ : !strcmp(pp->name, port_name)) {
+ found = true;
+ break;
+ }
+ }
+ } else {
+ VLOG_DBG("received reply with xid %08"PRIx32" "
+ "!= expected %08"PRIx32, recv_xid, send_xid);
}
+ ofpbuf_delete(reply);
+ }
+ vconn_close(vconn);
+
+ return found;
+}
+
+
+/* Opens a connection to 'vconn_name', fetches the port structure for
+ * 'port_name' (which may be a port name or number), and copies it into
+ * '*pp'. */
+static void
+fetch_ofputil_phy_port(const char *vconn_name, const char *port_name,
+ struct ofputil_phy_port *pp)
+{
+ unsigned int port_no;
+ bool found;
+ bool trunc;
+
+ /* Try to interpret the argument as a port number. */
+ if (!str_to_uint(port_name, 10, &port_no)) {
+ port_no = UINT_MAX;
+ }
+
+ /* Try to find the port based on the Features Reply. If it looks
+ * like the results may be truncated, then use the Port Description
+ * stats message introduced in OVS 1.7. */
+ found = fetch_port_by_features(vconn_name, port_name, port_no, pp,
+ &trunc);
+ if (trunc) {
+ found = fetch_port_by_stats(vconn_name, port_name, port_no, pp);
+ }
+
+ if (!found) {
+ ovs_fatal(0, "%s: couldn't find port `%s'", vconn_name, port_name);
}
- ovs_fatal(0, "%s: couldn't find port `%s'", vconn_name, port_name);
}
/* Returns the port number corresponding to 'port_name' (which may be a port
}
}
-static void
-do_dump_flows__(int argc, char *argv[], bool aggregate)
+static struct vconn *
+prepare_dump_flows(int argc, char *argv[], bool aggregate,
+ struct ofpbuf **requestp)
{
enum ofputil_protocol usable_protocols, protocol;
struct ofputil_flow_stats_request fsr;
- struct ofpbuf *request;
struct vconn *vconn;
parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "");
protocol = open_vconn(argv[1], &vconn);
protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
- request = ofputil_encode_flow_stats_request(&fsr, protocol);
- dump_stats_transaction(argv[1], request);
+ *requestp = ofputil_encode_flow_stats_request(&fsr, protocol);
+ return vconn;
+}
+
+static void
+ofctl_dump_flows__(int argc, char *argv[], bool aggregate)
+{
+ struct ofpbuf *request;
+ struct vconn *vconn;
+
+ vconn = prepare_dump_flows(argc, argv, aggregate, &request);
+ dump_stats_transaction(vconn, request);
vconn_close(vconn);
}
+static int
+compare_flows(const void *afs_, const void *bfs_)
+{
+ const struct ofputil_flow_stats *afs = afs_;
+ const struct ofputil_flow_stats *bfs = bfs_;
+ const struct cls_rule *a = &afs->rule;
+ const struct cls_rule *b = &bfs->rule;
+ const struct sort_criterion *sc;
+
+ for (sc = criteria; sc < &criteria[n_criteria]; sc++) {
+ const struct mf_field *f = sc->field;
+ int ret;
+
+ if (!f) {
+ ret = a->priority < b->priority ? -1 : a->priority > b->priority;
+ } else {
+ bool ina, inb;
+
+ ina = mf_are_prereqs_ok(f, &a->flow) && !mf_is_all_wild(f, &a->wc);
+ inb = mf_are_prereqs_ok(f, &b->flow) && !mf_is_all_wild(f, &b->wc);
+ if (ina != inb) {
+ /* Skip the test for sc->order, so that missing fields always
+ * sort to the end whether we're sorting in ascending or
+ * descending order. */
+ return ina ? -1 : 1;
+ } else {
+ union mf_value aval, bval;
+
+ mf_get_value(f, &a->flow, &aval);
+ mf_get_value(f, &b->flow, &bval);
+ ret = memcmp(&aval, &bval, f->n_bytes);
+ }
+ }
+
+ if (ret) {
+ return sc->order == SORT_ASC ? ret : -ret;
+ }
+ }
+
+ return 0;
+}
+
static void
-do_dump_flows(int argc, char *argv[])
+ofctl_dump_flows(int argc, char *argv[])
{
- return do_dump_flows__(argc, argv, false);
+ if (!n_criteria) {
+ return ofctl_dump_flows__(argc, argv, false);
+ } else {
+ struct ofputil_flow_stats *fses;
+ size_t n_fses, allocated_fses;
+ struct ofpbuf *request;
+ struct ofpbuf ofpacts;
+ struct ofpbuf *reply;
+ struct vconn *vconn;
+ ovs_be32 send_xid;
+ struct ds s;
+ size_t i;
+
+ vconn = prepare_dump_flows(argc, argv, false, &request);
+ send_xid = ((struct ofp_header *) request->data)->xid;
+ send_openflow_buffer(vconn, request);
+
+ fses = NULL;
+ n_fses = allocated_fses = 0;
+ reply = NULL;
+ ofpbuf_init(&ofpacts, 0);
+ for (;;) {
+ struct ofputil_flow_stats *fs;
+
+ if (n_fses >= allocated_fses) {
+ fses = x2nrealloc(fses, &allocated_fses, sizeof *fses);
+ }
+
+ fs = &fses[n_fses];
+ if (!recv_flow_stats_reply(vconn, send_xid, &reply, fs,
+ &ofpacts)) {
+ break;
+ }
+ fs->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len);
+ n_fses++;
+ }
+ ofpbuf_uninit(&ofpacts);
+
+ qsort(fses, n_fses, sizeof *fses, compare_flows);
+
+ ds_init(&s);
+ for (i = 0; i < n_fses; i++) {
+ ds_clear(&s);
+ ofp_print_flow_stats(&s, &fses[i]);
+ puts(ds_cstr(&s));
+ }
+ ds_destroy(&s);
+
+ for (i = 0; i < n_fses; i++) {
+ free(fses[i].ofpacts);
+ }
+ free(fses);
+
+ vconn_close(vconn);
+ }
}
static void
-do_dump_aggregate(int argc, char *argv[])
+ofctl_dump_aggregate(int argc, char *argv[])
{
- return do_dump_flows__(argc, argv, true);
+ return ofctl_dump_flows__(argc, argv, true);
}
static void
-do_queue_stats(int argc, char *argv[])
+ofctl_queue_stats(int argc, char *argv[])
{
- struct ofp_queue_stats_request *req;
+ struct ofp10_queue_stats_request *req;
struct ofpbuf *request;
+ struct vconn *vconn;
- req = alloc_stats_request(sizeof *req, OFPST_QUEUE, &request);
+ open_vconn(argv[1], &vconn);
+ request = ofpraw_alloc(OFPRAW_OFPST_QUEUE_REQUEST,
+ vconn_get_version(vconn), 0);
+ req = ofpbuf_put_zeros(request, sizeof *req);
if (argc > 2 && argv[2][0] && strcasecmp(argv[2], "all")) {
req->port_no = htons(str_to_port_no(argv[1], argv[2]));
memset(req->pad, 0, sizeof req->pad);
- dump_stats_transaction(argv[1], request);
+ dump_stats_transaction(vconn, request);
+ vconn_close(vconn);
}
static enum ofputil_protocol
}
static void
-do_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms)
+ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
+ size_t n_fms)
{
enum ofputil_protocol protocol;
struct vconn *vconn;
struct ofputil_flow_mod *fm = &fms[i];
transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
- free(fm->actions);
+ free(fm->ofpacts);
}
vconn_close(vconn);
}
static void
-do_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
+ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
{
struct ofputil_flow_mod *fms = NULL;
size_t n_fms = 0;
parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
- do_flow_mod__(argv[1], fms, n_fms);
+ ofctl_flow_mod__(argv[1], fms, n_fms);
free(fms);
}
static void
-do_flow_mod(int argc, char *argv[], uint16_t command)
+ofctl_flow_mod(int argc, char *argv[], uint16_t command)
{
if (argc > 2 && !strcmp(argv[2], "-")) {
- do_flow_mod_file(argc, argv, command);
+ ofctl_flow_mod_file(argc, argv, command);
} else {
struct ofputil_flow_mod fm;
parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, false);
- do_flow_mod__(argv[1], &fm, 1);
+ ofctl_flow_mod__(argv[1], &fm, 1);
}
}
static void
-do_add_flow(int argc, char *argv[])
+ofctl_add_flow(int argc, char *argv[])
{
- do_flow_mod(argc, argv, OFPFC_ADD);
+ ofctl_flow_mod(argc, argv, OFPFC_ADD);
}
static void
-do_add_flows(int argc, char *argv[])
+ofctl_add_flows(int argc, char *argv[])
{
- do_flow_mod_file(argc, argv, OFPFC_ADD);
+ ofctl_flow_mod_file(argc, argv, OFPFC_ADD);
}
static void
-do_mod_flows(int argc, char *argv[])
+ofctl_mod_flows(int argc, char *argv[])
{
- do_flow_mod(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
+ ofctl_flow_mod(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
}
static void
-do_del_flows(int argc, char *argv[])
+ofctl_del_flows(int argc, char *argv[])
{
- do_flow_mod(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
+ ofctl_flow_mod(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
}
static void
return;
}
- msg = ofputil_encode_barrier_request();
+ msg = ofputil_encode_barrier_request(vconn_get_version(aux->vconn));
error = vconn_send_block(aux->vconn, msg);
if (error) {
ofpbuf_delete(msg);
unixctl_command_reply(conn, NULL);
}
+static void
+ofctl_block(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *blocked_)
+{
+ bool *blocked = blocked_;
+
+ if (!*blocked) {
+ *blocked = true;
+ unixctl_command_reply(conn, NULL);
+ } else {
+ unixctl_command_reply(conn, "already blocking");
+ }
+}
+
+static void
+ofctl_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *blocked_)
+{
+ bool *blocked = blocked_;
+
+ if (*blocked) {
+ *blocked = false;
+ unixctl_command_reply(conn, NULL);
+ } else {
+ unixctl_command_reply(conn, "already unblocked");
+ }
+}
+
static void
monitor_vconn(struct vconn *vconn)
{
struct barrier_aux barrier_aux = { vconn, NULL };
struct unixctl_server *server;
bool exiting = false;
+ bool blocked = false;
int error;
daemon_save_fd(STDERR_FILENO);
ofctl_barrier, &barrier_aux);
unixctl_command_register("ofctl/set-output-file", "FILE", 1, 1,
ofctl_set_output_file, NULL);
+
+ unixctl_command_register("ofctl/block", "", 0, 0, ofctl_block, &blocked);
+ unixctl_command_register("ofctl/unblock", "", 0, 0, ofctl_unblock,
+ &blocked);
+
daemonize_complete();
for (;;) {
unixctl_server_run(server);
- for (;;) {
- uint8_t msg_type;
+ while (!blocked) {
+ enum ofptype type;
retval = vconn_recv(vconn, &b);
if (retval == EAGAIN) {
time_t now = time_wall();
char s[32];
- strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S: ", localtime(&now));
+ strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S: ", gmtime(&now));
fputs(s, stderr);
}
- msg_type = ((const struct ofp_header *) b->data)->type;
+ ofptype_decode(&type, b->data);
ofp_print(stderr, b->data, b->size, verbosity + 2);
ofpbuf_delete(b);
- if (barrier_aux.conn && msg_type == OFPT10_BARRIER_REPLY) {
+ if (barrier_aux.conn && type == OFPTYPE_BARRIER_REPLY) {
unixctl_command_reply(barrier_aux.conn, NULL);
barrier_aux.conn = NULL;
}
vconn_run(vconn);
vconn_run_wait(vconn);
- vconn_recv_wait(vconn);
+ if (!blocked) {
+ vconn_recv_wait(vconn);
+ }
unixctl_server_wait(server);
poll_block();
}
}
static void
-do_monitor(int argc, char *argv[])
+ofctl_monitor(int argc, char *argv[])
{
struct vconn *vconn;
+ int i;
open_vconn(argv[1], &vconn);
- if (argc > 2) {
- struct ofp_switch_config config;
+ for (i = 2; i < argc; i++) {
+ const char *arg = argv[i];
- fetch_switch_config(vconn, &config);
- config.miss_send_len = htons(atoi(argv[2]));
- set_switch_config(vconn, &config);
- }
- if (argc > 3) {
- if (!strcmp(argv[3], "invalid_ttl")) {
+ if (isdigit((unsigned char) *arg)) {
+ struct ofp_switch_config config;
+
+ fetch_switch_config(vconn, &config);
+ config.miss_send_len = htons(atoi(arg));
+ set_switch_config(vconn, &config);
+ } else if (!strcmp(arg, "invalid_ttl")) {
monitor_set_invalid_ttl_to_controller(vconn);
+ } else if (!strncmp(arg, "watch:", 6)) {
+ struct ofputil_flow_monitor_request fmr;
+ struct ofpbuf *msg;
+
+ parse_flow_monitor_request(&fmr, arg + 6);
+
+ msg = ofpbuf_new(0);
+ ofputil_append_flow_monitor_request(&fmr, msg);
+ dump_stats_transaction(vconn, msg);
+ } else {
+ ovs_fatal(0, "%s: unsupported \"monitor\" argument", arg);
}
}
+
if (preferred_packet_in_format >= 0) {
set_packet_in_format(vconn, preferred_packet_in_format);
} else {
}
static void
-do_snoop(int argc OVS_UNUSED, char *argv[])
+ofctl_snoop(int argc OVS_UNUSED, char *argv[])
{
struct vconn *vconn;
}
static void
-do_dump_ports(int argc, char *argv[])
+ofctl_dump_ports(int argc, char *argv[])
{
- struct ofp_port_stats_request *req;
+ struct ofp10_port_stats_request *req;
struct ofpbuf *request;
+ struct vconn *vconn;
uint16_t port;
- req = alloc_stats_request(sizeof *req, OFPST_PORT, &request);
+ open_vconn(argv[1], &vconn);
+ request = ofpraw_alloc(OFPRAW_OFPST_PORT_REQUEST,
+ vconn_get_version(vconn), 0);
+ req = ofpbuf_put_zeros(request, sizeof *req);
port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_NONE;
req->port_no = htons(port);
- dump_stats_transaction(argv[1], request);
+ dump_stats_transaction(vconn, request);
+ vconn_close(vconn);
}
static void
-do_probe(int argc OVS_UNUSED, char *argv[])
+ofctl_dump_ports_desc(int argc OVS_UNUSED, char *argv[])
+{
+ dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_PORT_DESC_REQUEST);
+}
+
+static void
+ofctl_probe(int argc OVS_UNUSED, char *argv[])
{
struct ofpbuf *request;
struct vconn *vconn;
struct ofpbuf *reply;
- make_openflow(sizeof(struct ofp_header), OFPT_ECHO_REQUEST, &request);
open_vconn(argv[1], &vconn);
+ request = make_echo_request(vconn_get_version(vconn));
run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
if (reply->size != sizeof(struct ofp_header)) {
ovs_fatal(0, "reply does not match request");
}
static void
-do_packet_out(int argc, char *argv[])
+ofctl_packet_out(int argc, char *argv[])
{
+ enum ofputil_protocol protocol;
struct ofputil_packet_out po;
- struct ofpbuf actions;
+ struct ofpbuf ofpacts;
struct vconn *vconn;
int i;
- ofpbuf_init(&actions, sizeof(union ofp_action));
- parse_ofp_actions(argv[3], &actions);
+ ofpbuf_init(&ofpacts, 64);
+ parse_ofpacts(argv[3], &ofpacts);
po.buffer_id = UINT32_MAX;
po.in_port = (!strcasecmp(argv[2], "none") ? OFPP_NONE
: !strcasecmp(argv[2], "local") ? OFPP_LOCAL
: str_to_port_no(argv[1], argv[2]));
- po.actions = actions.data;
- po.n_actions = actions.size / sizeof(union ofp_action);
+ po.ofpacts = ofpacts.data;
+ po.ofpacts_len = ofpacts.size;
- open_vconn(argv[1], &vconn);
+ protocol = open_vconn(argv[1], &vconn);
for (i = 4; i < argc; i++) {
struct ofpbuf *packet, *opo;
const char *error_msg;
po.packet = packet->data;
po.packet_len = packet->size;
- opo = ofputil_encode_packet_out(&po);
+ opo = ofputil_encode_packet_out(&po, protocol);
transact_noreply(vconn, opo);
ofpbuf_delete(packet);
}
vconn_close(vconn);
- ofpbuf_uninit(&actions);
+ ofpbuf_uninit(&ofpacts);
}
static void
-do_mod_port(int argc OVS_UNUSED, char *argv[])
+ofctl_mod_port(int argc OVS_UNUSED, char *argv[])
{
+ struct ofp_config_flag {
+ const char *name; /* The flag's name. */
+ enum ofputil_port_config bit; /* Bit to turn on or off. */
+ bool on; /* Value to set the bit to. */
+ };
+ static const struct ofp_config_flag flags[] = {
+ { "up", OFPUTIL_PC_PORT_DOWN, false },
+ { "down", OFPUTIL_PC_PORT_DOWN, true },
+ { "stp", OFPUTIL_PC_NO_STP, false },
+ { "receive", OFPUTIL_PC_NO_RECV, false },
+ { "receive-stp", OFPUTIL_PC_NO_RECV_STP, false },
+ { "flood", OFPUTIL_PC_NO_FLOOD, false },
+ { "forward", OFPUTIL_PC_NO_FWD, false },
+ { "packet-in", OFPUTIL_PC_NO_PACKET_IN, false },
+ };
+
+ const struct ofp_config_flag *flag;
enum ofputil_protocol protocol;
struct ofputil_port_mod pm;
struct ofputil_phy_port pp;
struct vconn *vconn;
+ const char *command;
+ bool not;
fetch_ofputil_phy_port(argv[1], argv[2], &pp);
pm.mask = 0;
pm.advertise = 0;
- if (!strcasecmp(argv[3], "up")) {
- pm.mask |= OFPUTIL_PC_PORT_DOWN;
- } else if (!strcasecmp(argv[3], "down")) {
- pm.mask |= OFPUTIL_PC_PORT_DOWN;
- pm.config |= OFPUTIL_PC_PORT_DOWN;
- } else if (!strcasecmp(argv[3], "flood")) {
- pm.mask |= OFPUTIL_PC_NO_FLOOD;
- } else if (!strcasecmp(argv[3], "noflood")) {
- pm.mask |= OFPUTIL_PC_NO_FLOOD;
- pm.config |= OFPUTIL_PC_NO_FLOOD;
- } else if (!strcasecmp(argv[3], "forward")) {
- pm.mask |= OFPUTIL_PC_NO_FWD;
- } else if (!strcasecmp(argv[3], "noforward")) {
- pm.mask |= OFPUTIL_PC_NO_FWD;
- pm.config |= OFPUTIL_PC_NO_FWD;
+ if (!strncasecmp(argv[3], "no-", 3)) {
+ command = argv[3] + 3;
+ not = true;
+ } else if (!strncasecmp(argv[3], "no", 2)) {
+ command = argv[3] + 2;
+ not = true;
} else {
- ovs_fatal(0, "unknown mod-port command '%s'", argv[3]);
+ command = argv[3];
+ not = false;
+ }
+ for (flag = flags; flag < &flags[ARRAY_SIZE(flags)]; flag++) {
+ if (!strcasecmp(command, flag->name)) {
+ pm.mask = flag->bit;
+ pm.config = flag->on ^ not ? flag->bit : 0;
+ goto found;
+ }
}
+ ovs_fatal(0, "unknown mod-port command '%s'", argv[3]);
+found:
protocol = open_vconn(argv[1], &vconn);
transact_noreply(vconn, ofputil_encode_port_mod(&pm, protocol));
vconn_close(vconn);
}
static void
-do_get_frags(int argc OVS_UNUSED, char *argv[])
+ofctl_get_frags(int argc OVS_UNUSED, char *argv[])
{
struct ofp_switch_config config;
struct vconn *vconn;
}
static void
-do_set_frags(int argc OVS_UNUSED, char *argv[])
+ofctl_set_frags(int argc OVS_UNUSED, char *argv[])
{
struct ofp_switch_config config;
enum ofp_config_flags mode;
}
static void
-do_ping(int argc, char *argv[])
+ofctl_ping(int argc, char *argv[])
{
size_t max_payload = 65535 - sizeof(struct ofp_header);
unsigned int payload;
for (i = 0; i < 10; i++) {
struct timeval start, end;
struct ofpbuf *request, *reply;
- struct ofp_header *rq_hdr, *rpy_hdr;
+ const struct ofp_header *rpy_hdr;
+ enum ofptype type;
- rq_hdr = make_openflow(sizeof(struct ofp_header) + payload,
- OFPT_ECHO_REQUEST, &request);
- random_bytes(rq_hdr + 1, payload);
+ request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+ payload);
+ random_bytes(ofpbuf_put_uninit(request, payload), payload);
xgettimeofday(&start);
run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact");
xgettimeofday(&end);
rpy_hdr = reply->data;
- if (reply->size != request->size
- || memcmp(rpy_hdr + 1, rq_hdr + 1, payload)
- || rpy_hdr->xid != rq_hdr->xid
- || rpy_hdr->type != OFPT_ECHO_REPLY) {
+ if (ofptype_pull(&type, reply)
+ || type != OFPTYPE_ECHO_REPLY
+ || reply->size != payload
+ || memcmp(request->l3, reply->l3, payload)) {
printf("Reply does not match request. Request:\n");
ofp_print(stdout, request, request->size, verbosity + 2);
printf("Reply:\n");
ofp_print(stdout, reply, reply->size, verbosity + 2);
}
printf("%zu bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
- reply->size - sizeof *rpy_hdr, argv[1], ntohl(rpy_hdr->xid),
+ reply->size, argv[1], ntohl(rpy_hdr->xid),
(1000*(double)(end.tv_sec - start.tv_sec))
+ (.001*(end.tv_usec - start.tv_usec)));
ofpbuf_delete(request);
}
static void
-do_benchmark(int argc OVS_UNUSED, char *argv[])
+ofctl_benchmark(int argc OVS_UNUSED, char *argv[])
{
size_t max_payload = 65535 - sizeof(struct ofp_header);
struct timeval start, end;
xgettimeofday(&start);
for (i = 0; i < count; i++) {
struct ofpbuf *request, *reply;
- struct ofp_header *rq_hdr;
- rq_hdr = make_openflow(message_size, OFPT_ECHO_REQUEST, &request);
- memset(rq_hdr + 1, 0, payload_size);
+ request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+ payload_size);
+ ofpbuf_put_zeros(request, payload_size);
run(vconn_transact(vconn, request, &reply), "transact");
ofpbuf_delete(reply);
}
}
static void
-do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
usage();
}
uint16_t idle_timeout;
uint16_t hard_timeout;
uint16_t flags;
- union ofp_action *actions;
- size_t n_actions;
+ struct ofpact *ofpacts;
+ size_t ofpacts_len;
};
/* Frees 'version' and the data that it owns. */
fte_version_free(struct fte_version *version)
{
if (version) {
- free(version->actions);
+ free(version->ofpacts);
free(version);
}
}
return (a->cookie == b->cookie
&& a->idle_timeout == b->idle_timeout
&& a->hard_timeout == b->hard_timeout
- && a->n_actions == b->n_actions
- && !memcmp(a->actions, b->actions,
- a->n_actions * sizeof *a->actions));
+ && ofpacts_equal(a->ofpacts, a->ofpacts_len,
+ b->ofpacts, b->ofpacts_len));
}
-/* Prints 'version' on stdout. Expects the caller to have printed the rule
- * associated with the version. */
+/* Clears 's', then if 's' has a version 'index', formats 'fte' and version
+ * 'index' into 's', followed by a new-line. */
static void
-fte_version_print(const struct fte_version *version)
+fte_version_format(const struct fte *fte, int index, struct ds *s)
{
- struct ds s;
+ const struct fte_version *version = fte->versions[index];
+
+ ds_clear(s);
+ if (!version) {
+ return;
+ }
+ cls_rule_format(&fte->rule, s);
if (version->cookie != htonll(0)) {
- printf(" cookie=0x%"PRIx64, ntohll(version->cookie));
+ ds_put_format(s, " cookie=0x%"PRIx64, ntohll(version->cookie));
}
if (version->idle_timeout != OFP_FLOW_PERMANENT) {
- printf(" idle_timeout=%"PRIu16, version->idle_timeout);
+ ds_put_format(s, " idle_timeout=%"PRIu16, version->idle_timeout);
}
if (version->hard_timeout != OFP_FLOW_PERMANENT) {
- printf(" hard_timeout=%"PRIu16, version->hard_timeout);
+ ds_put_format(s, " hard_timeout=%"PRIu16, version->hard_timeout);
}
- ds_init(&s);
- ofp_print_actions(&s, version->actions, version->n_actions);
- printf(" %s\n", ds_cstr(&s));
- ds_destroy(&s);
+ ds_put_char(s, ' ');
+ ofpacts_format(version->ofpacts, version->ofpacts_len, s);
+
+ ds_put_char(s, '\n');
}
static struct fte *
parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
version = xmalloc(sizeof *version);
- version->cookie = fm.cookie;
+ version->cookie = fm.new_cookie;
version->idle_timeout = fm.idle_timeout;
version->hard_timeout = fm.hard_timeout;
- version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF_EMERG);
- version->actions = fm.actions;
- version->n_actions = fm.n_actions;
+ version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF10_EMERG);
+ version->ofpacts = fm.ofpacts;
+ version->ofpacts_len = fm.ofpacts_len;
usable_protocols &= ofputil_usable_protocols(&fm.cr);
return usable_protocols;
}
+static bool
+recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
+ struct ofpbuf **replyp,
+ struct ofputil_flow_stats *fs, struct ofpbuf *ofpacts)
+{
+ struct ofpbuf *reply = *replyp;
+
+ for (;;) {
+ int retval;
+ bool more;
+
+ /* Get a flow stats reply message, if we don't already have one. */
+ if (!reply) {
+ enum ofptype type;
+ enum ofperr error;
+
+ do {
+ run(vconn_recv_block(vconn, &reply),
+ "OpenFlow packet receive failed");
+ } while (((struct ofp_header *) reply->data)->xid != send_xid);
+
+ error = ofptype_decode(&type, reply->data);
+ if (error || type != OFPTYPE_FLOW_STATS_REPLY) {
+ ovs_fatal(0, "received bad reply: %s",
+ ofp_to_string(reply->data, reply->size,
+ verbosity + 1));
+ }
+ }
+
+ /* Pull an individual flow stats reply out of the message. */
+ retval = ofputil_decode_flow_stats_reply(fs, reply, false, ofpacts);
+ switch (retval) {
+ case 0:
+ *replyp = reply;
+ return true;
+
+ case EOF:
+ more = ofpmp_more(reply->l2);
+ ofpbuf_delete(reply);
+ reply = NULL;
+ if (!more) {
+ *replyp = NULL;
+ return false;
+ }
+ break;
+
+ default:
+ ovs_fatal(0, "parse error in reply (%s)",
+ ofperr_to_string(retval));
+ }
+ }
+}
+
/* Reads the OpenFlow flow table from 'vconn', which has currently active flow
* format 'protocol', and adds them as flow table entries in 'cls' for the
* version with the specified 'index'. */
struct classifier *cls, int index)
{
struct ofputil_flow_stats_request fsr;
+ struct ofputil_flow_stats fs;
struct ofpbuf *request;
+ struct ofpbuf ofpacts;
+ struct ofpbuf *reply;
ovs_be32 send_xid;
- bool done;
fsr.aggregate = false;
cls_rule_init_catchall(&fsr.match, 0);
send_xid = ((struct ofp_header *) request->data)->xid;
send_openflow_buffer(vconn, request);
- done = false;
- while (!done) {
- ovs_be32 recv_xid;
- struct ofpbuf *reply;
-
- run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
- recv_xid = ((struct ofp_header *) reply->data)->xid;
- if (send_xid == recv_xid) {
- const struct ofputil_msg_type *type;
- const struct ofp_stats_msg *osm;
- enum ofputil_msg_code code;
-
- ofputil_decode_msg_type(reply->data, &type);
- code = ofputil_msg_type_code(type);
- if (code != OFPUTIL_OFPST_FLOW_REPLY &&
- code != OFPUTIL_NXST_FLOW_REPLY) {
- ovs_fatal(0, "received bad reply: %s",
- ofp_to_string(reply->data, reply->size,
- verbosity + 1));
- }
-
- osm = reply->data;
- if (!(osm->flags & htons(OFPSF_REPLY_MORE))) {
- done = true;
- }
-
- for (;;) {
- struct fte_version *version;
- struct ofputil_flow_stats fs;
- int retval;
-
- retval = ofputil_decode_flow_stats_reply(&fs, reply, false);
- if (retval) {
- if (retval != EOF) {
- ovs_fatal(0, "parse error in reply");
- }
- break;
- }
+ reply = NULL;
+ ofpbuf_init(&ofpacts, 0);
+ while (recv_flow_stats_reply(vconn, send_xid, &reply, &fs, &ofpacts)) {
+ struct fte_version *version;
- version = xmalloc(sizeof *version);
- version->cookie = fs.cookie;
- version->idle_timeout = fs.idle_timeout;
- version->hard_timeout = fs.hard_timeout;
- version->flags = 0;
- version->n_actions = fs.n_actions;
- version->actions = xmemdup(fs.actions,
- fs.n_actions * sizeof *fs.actions);
+ version = xmalloc(sizeof *version);
+ version->cookie = fs.cookie;
+ version->idle_timeout = fs.idle_timeout;
+ version->hard_timeout = fs.hard_timeout;
+ version->flags = 0;
+ version->ofpacts_len = fs.ofpacts_len;
+ version->ofpacts = xmemdup(fs.ofpacts, fs.ofpacts_len);
- fte_insert(cls, &fs.rule, version, index);
- }
- } else {
- VLOG_DBG("received reply with xid %08"PRIx32" "
- "!= expected %08"PRIx32, recv_xid, send_xid);
- }
- ofpbuf_delete(reply);
+ fte_insert(cls, &fs.rule, version, index);
}
+ ofpbuf_uninit(&ofpacts);
}
static void
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;
fm.flags = version->flags;
if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
command == OFPFC_MODIFY_STRICT) {
- fm.actions = version->actions;
- fm.n_actions = version->n_actions;
+ fm.ofpacts = version->ofpacts;
+ fm.ofpacts_len = version->ofpacts_len;
} else {
- fm.actions = NULL;
- fm.n_actions = 0;
+ fm.ofpacts = NULL;
+ fm.ofpacts_len = 0;
}
ofm = ofputil_encode_flow_mod(&fm, protocol);
}
static void
-do_replace_flows(int argc OVS_UNUSED, char *argv[])
+ofctl_replace_flows(int argc OVS_UNUSED, char *argv[])
{
enum { FILE_IDX = 0, SWITCH_IDX = 1 };
enum ofputil_protocol usable_protocols, protocol;
}
static void
-do_diff_flows(int argc OVS_UNUSED, char *argv[])
+ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
{
bool differences = false;
struct cls_cursor cursor;
struct classifier cls;
+ struct ds a_s, b_s;
struct fte *fte;
classifier_init(&cls);
read_flows_from_source(argv[1], &cls, 0);
read_flows_from_source(argv[2], &cls, 1);
+ ds_init(&a_s);
+ ds_init(&b_s);
+
cls_cursor_init(&cursor, &cls, NULL);
CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
struct fte_version *a = fte->versions[0];
struct fte_version *b = fte->versions[1];
if (!a || !b || !fte_version_equals(a, b)) {
- char *rule_s = cls_rule_to_string(&fte->rule);
- if (a) {
- printf("-%s", rule_s);
- fte_version_print(a);
- }
- if (b) {
- printf("+%s", rule_s);
- fte_version_print(b);
+ fte_version_format(fte, 0, &a_s);
+ fte_version_format(fte, 1, &b_s);
+ if (strcmp(ds_cstr(&a_s), ds_cstr(&b_s))) {
+ if (a_s.length) {
+ printf("-%s", ds_cstr(&a_s));
+ }
+ if (b_s.length) {
+ printf("+%s", ds_cstr(&b_s));
+ }
+ differences = true;
}
- free(rule_s);
-
- differences = true;
}
}
+ ds_destroy(&a_s);
+ ds_destroy(&b_s);
+
fte_free_all(&cls);
if (differences) {
/* Undocumented commands for unit testing. */
static void
-do_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
+ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
{
enum ofputil_protocol usable_protocols;
enum ofputil_protocol protocol = 0;
ofp_print(stdout, msg->data, msg->size, verbosity);
ofpbuf_delete(msg);
- free(fm->actions);
+ free(fm->ofpacts);
}
}
/* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints
* it back to stdout. */
static void
-do_parse_flow(int argc OVS_UNUSED, char *argv[])
+ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
{
struct ofputil_flow_mod fm;
parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
- do_parse_flows__(&fm, 1);
+ ofctl_parse_flows__(&fm, 1);
}
/* "parse-flows FILENAME": reads the named file as a sequence of flows (like
* add-flows) and prints each of the flows back to stdout. */
static void
-do_parse_flows(int argc OVS_UNUSED, char *argv[])
+ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
{
struct ofputil_flow_mod *fms = NULL;
size_t n_fms = 0;
parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
- do_parse_flows__(fms, n_fms);
+ ofctl_parse_flows__(fms, n_fms);
free(fms);
}
-/* "parse-nx-match": reads a series of nx_match specifications as strings from
- * stdin, does some internal fussing with them, and then prints them back as
- * strings on stdout. */
static void
-do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+ofctl_parse_nxm__(bool oxm)
{
struct ds in;
ds_init(&in);
- while (!ds_get_line(&in, stdin)) {
+ while (!ds_get_test_line(&in, stdin)) {
struct ofpbuf nx_match;
struct cls_rule rule;
ovs_be64 cookie, cookie_mask;
enum ofperr error;
int match_len;
- char *s;
-
- /* Delete comments, skip blank lines. */
- s = ds_cstr(&in);
- if (*s == '#') {
- puts(s);
- continue;
- }
- if (strchr(s, '#')) {
- *strchr(s, '#') = '\0';
- }
- if (s[strspn(s, " ")] == '\0') {
- putchar('\n');
- continue;
- }
/* Convert string to nx_match. */
ofpbuf_init(&nx_match, 0);
- match_len = nx_match_from_string(ds_cstr(&in), &nx_match);
+ if (oxm) {
+ match_len = oxm_match_from_string(ds_cstr(&in), &nx_match);
+ } else {
+ match_len = nx_match_from_string(ds_cstr(&in), &nx_match);
+ }
/* Convert nx_match to cls_rule. */
if (strict) {
- error = nx_pull_match(&nx_match, match_len, 0, &rule,
- &cookie, &cookie_mask);
+ if (oxm) {
+ error = oxm_pull_match(&nx_match, 0, &rule);
+ } else {
+ error = nx_pull_match(&nx_match, match_len, 0, &rule,
+ &cookie, &cookie_mask);
+ }
} else {
- error = nx_pull_match_loose(&nx_match, match_len, 0, &rule,
- &cookie, &cookie_mask);
+ if (oxm) {
+ error = oxm_pull_match_loose(&nx_match, 0, &rule);
+ } else {
+ error = nx_pull_match_loose(&nx_match, match_len, 0, &rule,
+ &cookie, &cookie_mask);
+ }
}
+
if (!error) {
char *out;
/* Convert cls_rule back to nx_match. */
ofpbuf_uninit(&nx_match);
ofpbuf_init(&nx_match, 0);
- match_len = nx_put_match(&nx_match, &rule, cookie, cookie_mask);
+ if (oxm) {
+ match_len = oxm_put_match(&nx_match, &rule);
+ out = oxm_match_to_string(nx_match.data, match_len);
+ } else {
+ match_len = nx_put_match(&nx_match, &rule,
+ cookie, cookie_mask);
+ out = nx_match_to_string(nx_match.data, match_len);
+ }
- /* Convert nx_match to string. */
- out = nx_match_to_string(nx_match.data, match_len);
puts(out);
free(out);
} else {
ds_destroy(&in);
}
+/* "parse-nxm": reads a series of NXM nx_match specifications as strings from
+ * stdin, does some internal fussing with them, and then prints them back as
+ * strings on stdout. */
+static void
+ofctl_parse_nxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ return ofctl_parse_nxm__(false);
+}
+
+/* "parse-oxm": reads a series of OXM nx_match specifications as strings from
+ * stdin, does some internal fussing with them, and then prints them back as
+ * strings on stdout. */
+static void
+ofctl_parse_oxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ return ofctl_parse_nxm__(true);
+}
+
+static void
+print_differences(const char *prefix,
+ const void *a_, size_t a_len,
+ const void *b_, size_t b_len)
+{
+ const uint8_t *a = a_;
+ const uint8_t *b = b_;
+ size_t i;
+
+ for (i = 0; i < MIN(a_len, b_len); i++) {
+ if (a[i] != b[i]) {
+ printf("%s%2zu: %02"PRIx8" -> %02"PRIx8"\n",
+ prefix, i, a[i], b[i]);
+ }
+ }
+ for (i = a_len; i < b_len; i++) {
+ printf("%s%2zu: (none) -> %02"PRIx8"\n", prefix, i, b[i]);
+ }
+ for (i = b_len; i < a_len; i++) {
+ printf("%s%2zu: %02"PRIx8" -> (none)\n", prefix, i, a[i]);
+ }
+}
+
+/* "parse-ofp10-actions": reads a series of OpenFlow 1.0 action specifications
+ * as hex bytes from stdin, converts them to ofpacts, prints them as strings
+ * on stdout, and then converts them back to hex bytes and prints any
+ * differences from the input. */
+static void
+ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ struct ds in;
+
+ ds_init(&in);
+ while (!ds_get_preprocessed_line(&in, stdin)) {
+ struct ofpbuf of10_out;
+ struct ofpbuf of10_in;
+ struct ofpbuf ofpacts;
+ enum ofperr error;
+ size_t size;
+ struct ds s;
+
+ /* Parse hex bytes. */
+ ofpbuf_init(&of10_in, 0);
+ if (ofpbuf_put_hex(&of10_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+
+ /* Convert to ofpacts. */
+ ofpbuf_init(&ofpacts, 0);
+ size = of10_in.size;
+ error = ofpacts_pull_openflow10(&of10_in, of10_in.size, &ofpacts);
+ if (error) {
+ printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of10_in);
+ continue;
+ }
+ ofpbuf_push_uninit(&of10_in, size);
+
+ /* Print cls_rule. */
+ ds_init(&s);
+ ofpacts_format(ofpacts.data, ofpacts.size, &s);
+ puts(ds_cstr(&s));
+ ds_destroy(&s);
+
+ /* Convert back to ofp10 actions and print differences from input. */
+ ofpbuf_init(&of10_out, 0);
+ ofpacts_put_openflow10(ofpacts.data, ofpacts.size, &of10_out);
+
+ print_differences("", of10_in.data, of10_in.size,
+ of10_out.data, of10_out.size);
+ putchar('\n');
+
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of10_in);
+ ofpbuf_uninit(&of10_out);
+ }
+ ds_destroy(&in);
+}
+
+/* "parse-ofp10-match": reads a series of ofp10_match specifications as hex
+ * bytes from stdin, converts them to cls_rules, prints them as strings on
+ * stdout, and then converts them back to hex bytes and prints any differences
+ * from the input.
+ *
+ * The input hex bytes may contain "x"s to represent "don't-cares", bytes whose
+ * values are ignored in the input and will be set to zero when OVS converts
+ * them back to hex bytes. ovs-ofctl actually sets "x"s to random bits when
+ * it does the conversion to hex, to ensure that in fact they are ignored. */
+static void
+ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ struct ds expout;
+ struct ds in;
+
+ ds_init(&in);
+ ds_init(&expout);
+ while (!ds_get_preprocessed_line(&in, stdin)) {
+ struct ofpbuf match_in, match_expout;
+ struct ofp10_match match_out;
+ struct ofp10_match match_normal;
+ struct cls_rule rule;
+ char *p;
+
+ /* Parse hex bytes to use for expected output. */
+ ds_clear(&expout);
+ ds_put_cstr(&expout, ds_cstr(&in));
+ for (p = ds_cstr(&expout); *p; p++) {
+ if (*p == 'x') {
+ *p = '0';
+ }
+ }
+ ofpbuf_init(&match_expout, 0);
+ if (ofpbuf_put_hex(&match_expout, ds_cstr(&expout), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+ if (match_expout.size != sizeof(struct ofp10_match)) {
+ ovs_fatal(0, "Input is %zu bytes, expected %zu",
+ match_expout.size, sizeof(struct ofp10_match));
+ }
+
+ /* Parse hex bytes for input. */
+ for (p = ds_cstr(&in); *p; p++) {
+ if (*p == 'x') {
+ *p = "0123456789abcdef"[random_uint32() & 0xf];
+ }
+ }
+ ofpbuf_init(&match_in, 0);
+ if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+ if (match_in.size != sizeof(struct ofp10_match)) {
+ ovs_fatal(0, "Input is %zu bytes, expected %zu",
+ match_in.size, sizeof(struct ofp10_match));
+ }
+
+ /* Convert to cls_rule and print. */
+ ofputil_cls_rule_from_ofp10_match(match_in.data, OFP_DEFAULT_PRIORITY,
+ &rule);
+ cls_rule_print(&rule);
+
+ /* Convert back to ofp10_match and print differences from input. */
+ ofputil_cls_rule_to_ofp10_match(&rule, &match_out);
+ print_differences("", match_expout.data, match_expout.size,
+ &match_out, sizeof match_out);
+
+ /* Normalize, then convert and compare again. */
+ ofputil_normalize_rule(&rule);
+ ofputil_cls_rule_to_ofp10_match(&rule, &match_normal);
+ print_differences("normal: ", &match_out, sizeof match_out,
+ &match_normal, sizeof match_normal);
+ putchar('\n');
+
+ ofpbuf_uninit(&match_in);
+ ofpbuf_uninit(&match_expout);
+ }
+ ds_destroy(&in);
+ ds_destroy(&expout);
+}
+
+/* "parse-ofp11-match": reads a series of ofp11_match specifications as hex
+ * bytes from stdin, converts them to cls_rules, prints them as strings on
+ * stdout, and then converts them back to hex bytes and prints any differences
+ * from the input. */
+static void
+ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ struct ds in;
+
+ ds_init(&in);
+ while (!ds_get_preprocessed_line(&in, stdin)) {
+ struct ofpbuf match_in;
+ struct ofp11_match match_out;
+ struct cls_rule rule;
+ enum ofperr error;
+
+ /* Parse hex bytes. */
+ ofpbuf_init(&match_in, 0);
+ if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+ if (match_in.size != sizeof(struct ofp11_match)) {
+ ovs_fatal(0, "Input is %zu bytes, expected %zu",
+ match_in.size, sizeof(struct ofp11_match));
+ }
+
+ /* Convert to cls_rule. */
+ error = ofputil_cls_rule_from_ofp11_match(match_in.data,
+ OFP_DEFAULT_PRIORITY, &rule);
+ if (error) {
+ printf("bad ofp11_match: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&match_in);
+ continue;
+ }
+
+ /* Print cls_rule. */
+ cls_rule_print(&rule);
+
+ /* Convert back to ofp11_match and print differences from input. */
+ ofputil_cls_rule_to_ofp11_match(&rule, &match_out);
+
+ print_differences("", match_in.data, match_in.size,
+ &match_out, sizeof match_out);
+ putchar('\n');
+
+ ofpbuf_uninit(&match_in);
+ }
+ ds_destroy(&in);
+}
+
+/* "parse-ofp11-actions": reads a series of OpenFlow 1.1 action specifications
+ * as hex bytes from stdin, converts them to ofpacts, prints them as strings
+ * on stdout, and then converts them back to hex bytes and prints any
+ * differences from the input. */
+static void
+ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ struct ds in;
+
+ ds_init(&in);
+ while (!ds_get_preprocessed_line(&in, stdin)) {
+ struct ofpbuf of11_out;
+ struct ofpbuf of11_in;
+ struct ofpbuf ofpacts;
+ enum ofperr error;
+ size_t size;
+ struct ds s;
+
+ /* Parse hex bytes. */
+ ofpbuf_init(&of11_in, 0);
+ if (ofpbuf_put_hex(&of11_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+
+ /* Convert to ofpacts. */
+ ofpbuf_init(&ofpacts, 0);
+ size = of11_in.size;
+ error = ofpacts_pull_openflow11_actions(&of11_in, of11_in.size,
+ &ofpacts);
+ if (error) {
+ printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of11_in);
+ continue;
+ }
+ ofpbuf_push_uninit(&of11_in, size);
+
+ /* Print cls_rule. */
+ ds_init(&s);
+ ofpacts_format(ofpacts.data, ofpacts.size, &s);
+ puts(ds_cstr(&s));
+ ds_destroy(&s);
+
+ /* Convert back to ofp11 actions and print differences from input. */
+ ofpbuf_init(&of11_out, 0);
+ ofpacts_put_openflow11_actions(ofpacts.data, ofpacts.size, &of11_out);
+
+ print_differences("", of11_in.data, of11_in.size,
+ of11_out.data, of11_out.size);
+ putchar('\n');
+
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of11_in);
+ ofpbuf_uninit(&of11_out);
+ }
+ ds_destroy(&in);
+}
+
+/* "parse-ofp11-instructions": reads a series of OpenFlow 1.1 instruction
+ * specifications as hex bytes from stdin, converts them to ofpacts, prints
+ * them as strings on stdout, and then converts them back to hex bytes and
+ * prints any differences from the input. */
+static void
+ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ struct ds in;
+
+ ds_init(&in);
+ while (!ds_get_preprocessed_line(&in, stdin)) {
+ struct ofpbuf of11_out;
+ struct ofpbuf of11_in;
+ struct ofpbuf ofpacts;
+ enum ofperr error;
+ size_t size;
+ struct ds s;
+
+ /* Parse hex bytes. */
+ ofpbuf_init(&of11_in, 0);
+ if (ofpbuf_put_hex(&of11_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+
+ /* Convert to ofpacts. */
+ ofpbuf_init(&ofpacts, 0);
+ size = of11_in.size;
+ error = ofpacts_pull_openflow11_instructions(&of11_in, of11_in.size,
+ &ofpacts);
+ if (error) {
+ printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of11_in);
+ continue;
+ }
+ ofpbuf_push_uninit(&of11_in, size);
+
+ /* Print cls_rule. */
+ ds_init(&s);
+ ofpacts_format(ofpacts.data, ofpacts.size, &s);
+ puts(ds_cstr(&s));
+ ds_destroy(&s);
+
+ /* Convert back to ofp11 instructions and print differences from
+ * input. */
+ ofpbuf_init(&of11_out, 0);
+ ofpacts_put_openflow11_instructions(ofpacts.data, ofpacts.size,
+ &of11_out);
+
+ print_differences("", of11_in.data, of11_in.size,
+ of11_out.data, of11_out.size);
+ putchar('\n');
+
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of11_in);
+ ofpbuf_uninit(&of11_out);
+ }
+ ds_destroy(&in);
+}
+
+/* "check-vlan VLAN_TCI VLAN_TCI_MASK": converts the specified vlan_tci and
+ * mask values to and from various formats and prints the results. */
+static void
+ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
+{
+ struct cls_rule rule;
+
+ char *string_s;
+ struct ofputil_flow_mod fm;
+
+ struct ofpbuf nxm;
+ struct cls_rule nxm_rule;
+ int nxm_match_len;
+ char *nxm_s;
+
+ struct ofp10_match of10_match;
+ struct cls_rule of10_rule;
+
+ struct ofp11_match of11_match;
+ struct cls_rule of11_rule;
+
+ enum ofperr error;
+
+ cls_rule_init_catchall(&rule, OFP_DEFAULT_PRIORITY);
+ rule.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
+ rule.wc.vlan_tci_mask = htons(strtoul(argv[2], NULL, 16));
+
+ /* Convert to and from string. */
+ string_s = cls_rule_to_string(&rule);
+ printf("%s -> ", string_s);
+ fflush(stdout);
+ parse_ofp_str(&fm, -1, string_s, false);
+ printf("%04"PRIx16"/%04"PRIx16"\n",
+ ntohs(fm.cr.flow.vlan_tci),
+ ntohs(fm.cr.wc.vlan_tci_mask));
+ free(string_s);
+
+ /* Convert to and from NXM. */
+ ofpbuf_init(&nxm, 0);
+ nxm_match_len = nx_put_match(&nxm, &rule, htonll(0), htonll(0));
+ nxm_s = nx_match_to_string(nxm.data, nxm_match_len);
+ error = nx_pull_match(&nxm, nxm_match_len, 0, &nxm_rule, NULL, NULL);
+ printf("NXM: %s -> ", nxm_s);
+ if (error) {
+ printf("%s\n", ofperr_to_string(error));
+ } else {
+ printf("%04"PRIx16"/%04"PRIx16"\n",
+ ntohs(nxm_rule.flow.vlan_tci),
+ ntohs(nxm_rule.wc.vlan_tci_mask));
+ }
+ free(nxm_s);
+ ofpbuf_uninit(&nxm);
+
+ /* Convert to and from OXM. */
+ ofpbuf_init(&nxm, 0);
+ nxm_match_len = oxm_put_match(&nxm, &rule);
+ nxm_s = oxm_match_to_string(nxm.data, nxm_match_len);
+ error = oxm_pull_match(&nxm, 0, &nxm_rule);
+ printf("OXM: %s -> ", nxm_s);
+ if (error) {
+ printf("%s\n", ofperr_to_string(error));
+ } else {
+ uint16_t vid = ntohs(nxm_rule.flow.vlan_tci) &
+ (VLAN_VID_MASK | VLAN_CFI);
+ uint16_t mask = ntohs(nxm_rule.wc.vlan_tci_mask) &
+ (VLAN_VID_MASK | VLAN_CFI);
+
+ printf("%04"PRIx16"/%04"PRIx16",", vid, mask);
+ if (vid && vlan_tci_to_pcp(nxm_rule.wc.vlan_tci_mask)) {
+ printf("%02"PRIx8"\n", vlan_tci_to_pcp(nxm_rule.flow.vlan_tci));
+ } else {
+ printf("--\n");
+ }
+ }
+ free(nxm_s);
+ ofpbuf_uninit(&nxm);
+
+ /* Convert to and from OpenFlow 1.0. */
+ ofputil_cls_rule_to_ofp10_match(&rule, &of10_match);
+ ofputil_cls_rule_from_ofp10_match(&of10_match, 0, &of10_rule);
+ printf("OF1.0: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
+ ntohs(of10_match.dl_vlan),
+ (of10_match.wildcards & htonl(OFPFW10_DL_VLAN)) != 0,
+ of10_match.dl_vlan_pcp,
+ (of10_match.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0,
+ ntohs(of10_rule.flow.vlan_tci),
+ ntohs(of10_rule.wc.vlan_tci_mask));
+
+ /* Convert to and from OpenFlow 1.1. */
+ ofputil_cls_rule_to_ofp11_match(&rule, &of11_match);
+ ofputil_cls_rule_from_ofp11_match(&of11_match, 0, &of11_rule);
+ printf("OF1.1: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
+ ntohs(of11_match.dl_vlan),
+ (of11_match.wildcards & htonl(OFPFW11_DL_VLAN)) != 0,
+ of11_match.dl_vlan_pcp,
+ (of11_match.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0,
+ ntohs(of11_rule.flow.vlan_tci),
+ ntohs(of11_rule.wc.vlan_tci_mask));
+}
+
/* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow
* version. */
static void
-do_print_error(int argc OVS_UNUSED, char *argv[])
+ofctl_print_error(int argc OVS_UNUSED, char *argv[])
{
enum ofperr error;
int version;
}
for (version = 0; version <= UINT8_MAX; version++) {
- const struct ofperr_domain *domain;
-
- domain = ofperr_domain_from_version(version);
- if (!domain) {
+ const char *name = ofperr_domain_get_name(version);
+ if (!name) {
continue;
}
-
printf("%s: %d,%d\n",
- ofperr_domain_get_name(domain),
- ofperr_get_type(error, domain),
- ofperr_get_code(error, domain));
+ ofperr_domain_get_name(version),
+ ofperr_get_type(error, version),
+ ofperr_get_code(error, version));
}
}
* binary data, interpreting them as an OpenFlow message, and prints the
* OpenFlow message on stdout, at VERBOSITY (level 2 by default). */
static void
-do_ofp_print(int argc, char *argv[])
+ofctl_ofp_print(int argc, char *argv[])
{
struct ofpbuf packet;
}
static const struct command all_commands[] = {
- { "show", 1, 1, do_show },
- { "monitor", 1, 3, do_monitor },
- { "snoop", 1, 1, do_snoop },
- { "dump-desc", 1, 1, do_dump_desc },
- { "dump-tables", 1, 1, do_dump_tables },
- { "dump-flows", 1, 2, do_dump_flows },
- { "dump-aggregate", 1, 2, do_dump_aggregate },
- { "queue-stats", 1, 3, do_queue_stats },
- { "add-flow", 2, 2, do_add_flow },
- { "add-flows", 2, 2, do_add_flows },
- { "mod-flows", 2, 2, do_mod_flows },
- { "del-flows", 1, 2, do_del_flows },
- { "replace-flows", 2, 2, do_replace_flows },
- { "diff-flows", 2, 2, do_diff_flows },
- { "packet-out", 4, INT_MAX, do_packet_out },
- { "dump-ports", 1, 2, do_dump_ports },
- { "mod-port", 3, 3, do_mod_port },
- { "get-frags", 1, 1, do_get_frags },
- { "set-frags", 2, 2, do_set_frags },
- { "probe", 1, 1, do_probe },
- { "ping", 1, 2, do_ping },
- { "benchmark", 3, 3, do_benchmark },
- { "help", 0, INT_MAX, do_help },
+ { "show", 1, 1, ofctl_show },
+ { "monitor", 1, 3, ofctl_monitor },
+ { "snoop", 1, 1, ofctl_snoop },
+ { "dump-desc", 1, 1, ofctl_dump_desc },
+ { "dump-tables", 1, 1, ofctl_dump_tables },
+ { "dump-flows", 1, 2, ofctl_dump_flows },
+ { "dump-aggregate", 1, 2, ofctl_dump_aggregate },
+ { "queue-stats", 1, 3, ofctl_queue_stats },
+ { "add-flow", 2, 2, ofctl_add_flow },
+ { "add-flows", 2, 2, ofctl_add_flows },
+ { "mod-flows", 2, 2, ofctl_mod_flows },
+ { "del-flows", 1, 2, ofctl_del_flows },
+ { "replace-flows", 2, 2, ofctl_replace_flows },
+ { "diff-flows", 2, 2, ofctl_diff_flows },
+ { "packet-out", 4, INT_MAX, ofctl_packet_out },
+ { "dump-ports", 1, 2, ofctl_dump_ports },
+ { "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
+ { "mod-port", 3, 3, ofctl_mod_port },
+ { "get-frags", 1, 1, ofctl_get_frags },
+ { "set-frags", 2, 2, ofctl_set_frags },
+ { "probe", 1, 1, ofctl_probe },
+ { "ping", 1, 2, ofctl_ping },
+ { "benchmark", 3, 3, ofctl_benchmark },
+ { "help", 0, INT_MAX, ofctl_help },
/* Undocumented commands for testing. */
- { "parse-flow", 1, 1, do_parse_flow },
- { "parse-flows", 1, 1, do_parse_flows },
- { "parse-nx-match", 0, 0, do_parse_nx_match },
- { "print-error", 1, 1, do_print_error },
- { "ofp-print", 1, 2, do_ofp_print },
+ { "parse-flow", 1, 1, ofctl_parse_flow },
+ { "parse-flows", 1, 1, ofctl_parse_flows },
+ { "parse-nx-match", 0, 0, ofctl_parse_nxm },
+ { "parse-nxm", 0, 0, ofctl_parse_nxm },
+ { "parse-oxm", 0, 0, ofctl_parse_oxm },
+ { "parse-ofp10-actions", 0, 0, ofctl_parse_ofp10_actions },
+ { "parse-ofp10-match", 0, 0, ofctl_parse_ofp10_match },
+ { "parse-ofp11-match", 0, 0, ofctl_parse_ofp11_match },
+ { "parse-ofp11-actions", 0, 0, ofctl_parse_ofp11_actions },
+ { "parse-ofp11-instructions", 0, 0, ofctl_parse_ofp11_instructions },
+ { "check-vlan", 2, 2, ofctl_check_vlan },
+ { "print-error", 1, 1, ofctl_print_error },
+ { "ofp-print", 1, 2, ofctl_ofp_print },
{ NULL, 0, 0, NULL },
};
#! @PERL@
-# Copyright (c) 2009, 2010 Nicira Networks.
+# Copyright (c) 2009, 2010 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#! @PYTHON@
#
-# Copyright (c) 2010 Nicira Networks.
+# Copyright (c) 2010 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
+++ /dev/null
-#! @PERL@
-
-# Copyright (c) 2008, 2009 Nicira Networks.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-use CGI;
-use Digest::SHA1;
-use Fcntl;
-
-$CGI::POST_MAX = 65536; # Limit POSTs to 64 kB.
-
-use strict;
-use warnings;
-
-my $pkidir = '@PKIDIR@';
-my $q = new CGI;
-
-die unless $q->request_method() eq 'POST';
-
-my $type = $q->param('type');
-die unless defined $type;
-die unless $type eq 'switch' or $type eq 'controller';
-
-my $req = $q->param('req');
-die unless defined $req;
-die unless $req =~ /^-----BEGIN CERTIFICATE REQUEST-----$/m;
-die unless $req =~ /^-----END CERTIFICATE REQUEST-----$/m;
-
-my $digest = Digest::SHA1::sha1_hex($req);
-my $incoming = "$pkidir/${type}ca/incoming";
-my $dst = "$incoming/$digest-req.pem";
-
-sysopen(REQUEST, "$dst.tmp", O_RDWR | O_CREAT | O_EXCL, 0600)
- or die "sysopen $dst.tmp: $!";
-print REQUEST $req;
-close(REQUEST) or die "close $dst.tmp: $!";
-
-rename("$dst.tmp", $dst) or die "rename $dst.tmp to $dst: $!";
-
-print $q->header('text/html', '204 No response');
-
-# Local Variables:
-# mode: perl
-# End:
ovs\-pki \- OpenFlow public key infrastructure management utility
.SH SYNOPSIS
-\fBovs\-pki\fR [\fIOPTIONS\fR] \fICOMMAND\fR [\fIARGS\fR]
+Each command takes the form:
.sp
-Stand\-alone commands with their arguments:
+\fBovs\-pki\fR [\fIoptions\fR] \fIcommand\fR [\fIargs\fR]
+.sp
+The implemented commands and their arguments are:
.br
\fBovs\-pki\fR \fBinit\fR
.br
-\fBovs\-pki\fR \fBreq\fR \fINAME\fR
-.br
-\fBovs\-pki\fR \fBsign\fR \fINAME\fR [\fITYPE\fR]
-.br
-\fBovs\-pki\fR \fBreq+sign\fR \fINAME\fR [\fITYPE\fR]
-.br
-\fBovs\-pki\fR \fBverify\fR \fINAME\fR [\fITYPE\fR]
-.br
-\fBovs\-pki\fR \fBfingerprint\fR \fIFILE\fR
-.br
-\fBovs\-pki\fR \fBself\-sign\fR \fINAME\fR
-.sp
-The following additional commands manage an online PKI:
-.br
-\fBovs\-pki\fR \fBls\fR [\fIPREFIX\fR] [\fITYPE\fR]
+\fBovs\-pki\fR \fBreq\fR \fIname\fR
.br
-\fBovs\-pki\fR \fBflush\fR [\fITYPE\fR]
+\fBovs\-pki\fR \fBsign\fR \fIname\fR [\fItype\fR]
.br
-\fBovs\-pki\fR \fBreject\fR \fIPREFIX\fR [\fITYPE\fR]
+\fBovs\-pki\fR \fBreq+sign\fR \fIname\fR [\fItype\fR]
.br
-\fBovs\-pki\fR \fBapprove\fR \fIPREFIX\fR [\fITYPE\fR]
+\fBovs\-pki\fR \fBverify\fR \fIname\fR [\fItype\fR]
.br
-\fBovs\-pki\fR \fBprompt\fR [\fITYPE\fR]
+\fBovs\-pki\fR \fBfingerprint\fR \fIfile\fR
.br
-\fBovs\-pki\fR \fBexpire\fR [\fIAGE\fR]
+\fBovs\-pki\fR \fBself\-sign\fR \fIname\fR
.sp
-Each \fITYPE\fR above is a certificate type, either \fBswitch\fR
+Each \fItype\fR above is a certificate type, either \fBswitch\fR
(default) or \fBcontroller\fR.
.sp
The available options are:
contents that should not be exposed.
.TP
-\fBreq\fR \fINAME\fR
-Generates a new private key named \fINAME\fR\fB\-privkey.pem\fR and
-corresponding certificate request named \fINAME\fR\fB\-req.pem\fR.
+\fBreq\fR \fIname\fR
+Generates a new private key named \fIname\fR\fB\-privkey.pem\fR and
+corresponding certificate request named \fIname\fR\fB\-req.pem\fR.
The private key can be intended for use by a switch or a controller.
This command should ideally be run on the switch or controller that
will use the private key to identify itself. The file
-\fINAME\fR\fB\-req.pem\fR must be copied to the CA machine for signing
+\fIname\fR\fB\-req.pem\fR must be copied to the CA machine for signing
with the \fBsign\fR command (below).
This command will output a fingerprint to stdout as its final step.
the \fB\-D\fR or \fB\-\^\-dsaparam\fR option (see below) may be used to
specify an alternate location.
-\fINAME\fR\fB\-privkey.pem\fR has sensitive contents that should not be
-exposed. \fINAME\fR\fB\-req.pem\fR may be safely made public.
+\fIname\fR\fB\-privkey.pem\fR has sensitive contents that should not be
+exposed. \fIname\fR\fB\-req.pem\fR may be safely made public.
.TP
-\fBsign\fR \fINAME\fR [\fITYPE\fR]
-Signs the certificate request named \fINAME\fR\fB\-req.pem\fR that was
+\fBsign\fR \fIname\fR [\fItype\fR]
+Signs the certificate request named \fIname\fR\fB\-req.pem\fR that was
produced in the previous step, producing a certificate named
-\fINAME\fR\fB\-cert.pem\fR. \fITYPE\fR, either \fBswitch\fR (default) or
+\fIname\fR\fB\-cert.pem\fR. \fItype\fR, either \fBswitch\fR (default) or
\fBcontroller\fR, indicates the use for which the key is being
certified.
produced by \fBreq\fR. (The \fB\-b\fR or \fB\-\^\-batch\fR option
suppresses the verification step.)
-The file \fINAME\fR\fB\-cert.pem\fR will need to be copied back to the
+The file \fIname\fR\fB\-cert.pem\fR will need to be copied back to the
switch or controller for which it is intended. Its contents may
safely be made public.
.TP
-\fBreq+sign\fR \fINAME\fR [\fITYPE\fR]
+\fBreq+sign\fR \fIname\fR [\fItype\fR]
Combines the \fBreq\fR and \fBsign\fR commands into a single step,
outputting all the files produced by each. The
-\fINAME\fR\fB\-privkey.pem\fR and \fINAME\fR\fB\-cert.pem\fR files must
+\fIname\fR\fB\-privkey.pem\fR and \fIname\fR\fB\-cert.pem\fR files must
be copied securely to the switch or controller.
-\fINAME\fR\fB\-privkey.pem\fR has sensitive contents and must not be
+\fIname\fR\fB\-privkey.pem\fR has sensitive contents and must not be
exposed in transit. Afterward, it should be deleted from the CA
machine.
key. However, it is also more convenient.
.TP
-\fBverify\fR \fINAME\fR [\fITYPE\fR]
-Verifies that \fINAME\fR\fB\-cert.pem\fR is a valid certificate for the
-given \fITYPE\fR of use, either \fBswitch\fR (default) or
+\fBverify\fR \fIname\fR [\fItype\fR]
+Verifies that \fIname\fR\fB\-cert.pem\fR is a valid certificate for the
+given \fItype\fR of use, either \fBswitch\fR (default) or
\fBcontroller\fR. If the certificate is valid for this use, it prints
-the message ``\fINAME\fR\fB\-cert.pem\fR: OK''; otherwise, it prints an
+the message ``\fIname\fR\fB\-cert.pem\fR: OK''; otherwise, it prints an
error message.
.TP
-\fBfingerprint\fR \fIFILE\fR
-Prints the fingerprint for \fIFILE\fR. If \fIFILE\fR is a
+\fBfingerprint\fR \fIfile\fR
+Prints the fingerprint for \fIfile\fR. If \fIfile\fR is a
certificate, then this is the SHA\-1 digest of the DER encoded version
of the certificate; otherwise, it is the SHA\-1 digest of the entire
file.
.TP
-\fBself\-sign\fR \fINAME\fR
-Signs the certificate request named \fINAME\fB\-req.pem\fR using the
-private key \fINAME\fB\-privkey.pem\fR, producing a self-signed
-certificate named \fINAME\fB\-cert.pem\fR. The input files should have
+\fBself\-sign\fR \fIname\fR
+Signs the certificate request named \fIname\fB\-req.pem\fR using the
+private key \fIname\fB\-privkey.pem\fR, producing a self-signed
+certificate named \fIname\fB\-cert.pem\fR. The input files should have
been produced with \fBovs\-pki req\fR.
Some controllers accept such self-signed certificates.
-.SH "ONLINE COMMANDS"
-
-An OpenFlow PKI can be administered online, in conjunction with
-.BR ovs\-pki\-cgi (8)
-and a web server such as Apache:
-
-.IP \(bu
-The web server exports the contents of the PKI via HTTP. All files in
-a PKI hierarchy files may be made public, except for the files
-\fBpki/controllerca/private/cakey.pem\fR and
-\fBpki/switchca/private/cakey.pem\fR, which must not be exposed.
-
-.IP \(bu
-\fBovs\-pki\-cgi\fR allows newly generated certificate requests for
-controllers and switches to be uploaded into the
-\fBpki/controllerca/incoming\fR and \fBpki/switchca/incoming\fR
-directories, respectively. Uploaded certificate requests are stored
-in those directories under names of the form
-\fIFINGERPRINT\fB\-req.pem\fR, which \fIFINGERPRINT\fR is the SHA\-1
-hash of the file.
-
-.IP \(bu
-These \fBovs\-pki\fR commands allow incoming certificate requests to
-be approved or rejected, in a form are suitable for use by humans or
-other software.
-
-.PP
-The following \fBovs\-pki\fR commands support online administration:
-
-.TP
-\fBovs\-pki\fR \fBls\fR [\fIPREFIX\fR] [\fITYPE\fR]
-Lists all of the incoming certificate requests of the given \fITYPE\fR
-(either \fBswitch\fR, the default, or \fBcontroller\fR). If
-\fIPREFIX\fR, which must be at least 4 characters long, is specified,
-it causes the list to be limited to files whose names begin with
-\fIPREFIX\fR. This is useful, for example, to avoid typing in an
-entire fingerprint when checking that a specific certificate request
-has been received.
-
-.TP
-\fBovs\-pki\fR \fBflush\fR [\fITYPE\fR]
-Deletes all certificate requests of the given \fITYPE\fR.
-
-.TP
-\fBovs\-pki\fR \fBreject\fR \fIPREFIX\fR [\fITYPE\fR]
-Rejects the certificate request whose name begins with \fIPREFIX\fR,
-which must be at least 4 characters long, of the given type (either
-\fBswitch\fR, the default, or \fBcontroller\fR). \fIPREFIX\fR must
-match exactly one certificate request; its purpose is to allow the
-user to type fewer characters, not to match multiple certificate
-requests.
-
-.TP
-\fBovs\-pki\fR \fBapprove\fR \fIPREFIX\fR [\fITYPE\fR]
-Approves the certificate request whose name begins with \fIPREFIX\fR,
-which must be at least 4 characters long, of the given \fITYPE\fR
-(either \fBswitch\fR, the default, or \fBcontroller\fR). \fIPREFIX\fR
-must match exactly one certificate request; its purpose is to allow
-the user to type fewer characters, not to match multiple certificate
-requests.
-
-The command will output a fingerprint to stdout and request that you
-verify that it is correct. (The \fB\-b\fR or \fB\-\^\-batch\fR option
-suppresses the verification step.)
-
-.TP
-\fBovs\-pki\fR \fBprompt\fR [\fITYPE\fR]
-Prompts the user for each incoming certificate request of the given
-\fITYPE\fR (either \fBswitch\fR, the default, or \fBcontroller\fR).
-Based on the certificate request's fingerprint, the user is given the
-option of approving, rejecting, or skipping the certificate request.
-
-.TP
-\fBovs\-pki\fR \fBexpire\fR [\fIAGE\fR]
-
-Rejects all the incoming certificate requests, of either type, that is
-older than \fIAGE\fR, which must in one of the forms \fIN\fBs\fR,
-\fIN\fBmin\fR, \fIN\fBh\fR, \fIN\fBday\fR. The default is \fB1day\fR.
-
.SH OPTIONS
.IP "\fB\-k\fR \fItype\fR"
.IQ "\fB\-\^\-key=\fItype\fR"
.IP "\fB\-b\fR"
.IQ "\fB\-\^\-batch\fR"
Suppresses the interactive verification of fingerprints that the
-\fBsign\fR and \fBapprove\fR commands by default require.
+\fBsign\fR command by default requires.
.IP "\fB\-d\fR \fIdir\fR"
.IQ "\fB\-\^\-dir=\fR\fIdir\fR"
.SH "SEE ALSO"
-.BR ovs\-controller (8),
-.BR ovs\-pki\-cgi (8)
+.BR ovs\-controller (8).
#! /bin/sh
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
log='@LOGDIR@/ovs-pki.log'
keytype=rsa
bits=2048
+
+# OS-specific compatibility routines
+case $(uname -s) in
+FreeBSD)
+ file_mod_epoch()
+ {
+ stat -r "$1" | awk '{print $10}'
+ }
+
+ file_mod_date()
+ {
+ stat -f '%Sm' "$1"
+ }
+
+ sha1sum()
+ {
+ sha1 "$@"
+ }
+ ;;
+*)
+ file_mod_epoch()
+ {
+ date -r "$1" +%s
+ }
+
+ file_mod_date()
+ {
+ date -r "$1"
+ }
+ ;;
+esac
+
for option; do
# This option-parsing mechanism borrowed from a Autoconf-generated
# configure script under the following license:
fingerprint FILE Prints the fingerprint for FILE
self-sign NAME Sign NAME-req.pem with NAME-privkey.pem,
producing self-signed certificate NAME-cert.pem
-
-The following additional commands manage an online PKI:
- ls [PREFIX] [TYPE] Lists incoming requests of the given TYPE, optionally
- limited to those whose fingerprint begins with PREFIX
- flush [TYPE] Rejects all incoming requests of the given TYPE
- reject PREFIX [TYPE] Rejects the incoming request(s) whose fingerprint begins
- with PREFIX and has the given TYPE
- approve PREFIX [TYPE] Approves the incoming request whose fingerprint begins
- with PREFIX and has the given TYPE
- expire [AGE] Rejects all incoming requests older than AGE, in
- one of the forms Ns, Nmin, Nh, Nday (default: 1day)
- prompt [TYPE] Interactively prompts to accept or reject each incoming
- request of the given TYPE
-
Each TYPE above is a certificate type: 'switch' (default) or 'controller'.
Options for 'init', 'req', and 'req+sign' only:
this has an effect only on 'init'.
-D, --dsaparam=FILE File with DSA parameters (DSA only)
(default: dsaparam.pem within PKI directory)
-Options for use with the 'sign' and 'approve' commands:
+Options for use with the 'sign' command:
-b, --batch Skip fingerprint verification
Options that apply to any command:
-d, --dir=DIR Directory where the PKI is located
mkdir -p certs crl newcerts
mkdir -p -m 0700 private
- mkdir -p -m 0733 incoming
touch index.txt
test -e crlnumber || echo 01 > crlnumber
test -e serial || echo 01 > serial
fi
}
-zero_or_one_args() {
- if test -n "$arg2"; then
- echo "$0: $command must have zero or one arguments; use --help for help" >&2
- exit 1
- fi
-}
-
one_or_two_args() {
if test -z "$arg1"; then
echo "$0: $command must have one or two arguments; use --help for help" >&2
fi
}
-resolve_prefix() {
- test -n "$type" || exit 123 # Forgot to call check_type?
-
- case $1 in
- ????*)
- ;;
- *)
- echo "Prefix $arg1 is too short (less than 4 hex digits)" >&2
- exit 0
- ;;
- esac
-
- fingerprint=$(cd "$pkidir/${type}ca/incoming" && echo "$1"*-req.pem | sed 's/-req\.pem$//')
- case $fingerprint in
- "${1}*")
- echo "No certificate requests matching $1" >&2
- exit 1
- ;;
- *" "*)
- echo "$1 matches more than one certificate request:" >&2
- echo $fingerprint | sed 's/ /\
-/g' >&2
- exit 1
- ;;
- *)
- # Nothing to do.
- ;;
- esac
- req="$pkidir/${type}ca/incoming/$fingerprint-req.pem"
- cert="$pkidir/${type}ca/certs/$fingerprint-cert.pem"
-}
-
make_tmpdir() {
TMP=/tmp/ovs-pki.tmp$$
rm -rf $TMP
fingerprint() {
file=$1
name=${1-$2}
- date=$(date -r $file)
+ date=$(file_mod_date "$file")
if grep -e '-BEGIN CERTIFICATE-' "$file" > /dev/null; then
fingerprint=$(openssl x509 -noout -in "$file" -fingerprint |
sed 's/SHA1 Fingerprint=//' | tr -d ':')
# Reset the permissions on the certificate to the user's default.
cat "$arg1-cert.pem.tmp" > "$arg1-cert.pem"
rm -f "$arg1-cert.pem.tmp"
-elif test "$command" = ls; then
- check_type "$arg2"
-
- cd "$pkidir/${type}ca/incoming"
- for file in $(glob "$arg1*-req.pem"); do
- fingerprint $file
- done
-elif test "$command" = flush; then
- check_type "$arg1"
-
- rm -f "$pkidir/${type}ca/incoming/"*
-elif test "$command" = reject; then
- one_or_two_args
- check_type "$arg2"
- resolve_prefix "$arg1"
-
- rm -f "$req"
-elif test "$command" = approve; then
- one_or_two_args
- check_type "$arg2"
- resolve_prefix "$arg1"
-
- make_tmpdir
- cp "$req" "$TMP/$req"
- verify_fingerprint "$TMP/$req"
- sign_request "$TMP/$req"
- rm -f "$req" "$TMP/$req"
-elif test "$command" = prompt; then
- zero_or_one_args
- check_type "$arg1"
-
- make_tmpdir
- cd "$pkidir/${type}ca/incoming"
- for req in $(glob "*-req.pem"); do
- cp "$req" "$TMP/$req"
-
- cert=$(echo "$pkidir/${type}ca/certs/$req" |
- sed 's/-req.pem/-cert.pem/')
- if test -f $cert; then
- echo "Request $req already approved--dropping duplicate request"
- rm -f "$req" "$TMP/$req"
- continue
- fi
-
- echo
- echo
- fingerprint "$TMP/$req" "$req"
- printf "Disposition for this request (skip/approve/reject)? "
- read answer
- case $answer in
- approve)
- echo "Approving $req"
- sign_request "$TMP/$req" "$cert"
- rm -f "$req" "$TMP/$req"
- ;;
- r*)
- echo "Rejecting $req"
- rm -f "$req" "$TMP/$req"
- ;;
- *)
- echo "Skipping $req"
- ;;
- esac
- done
-elif test "$command" = expire; then
- zero_or_one_args
- cutoff=$(($(date +%s) - $(parse_age ${arg1-1day})))
- for type in switch controller; do
- cd "$pkidir/${type}ca/incoming" || exit 1
- for file in $(glob "*"); do
- time=$(date -r "$file" +%s)
- if test "$time" -lt "$cutoff"; then
- rm -f "$file"
- fi
- done
- done
else
echo "$0: $command command unknown; use --help for help" >&2
exit 1
#! /bin/sh
-# Copyright (c) 2011 Nicira Networks, Inc.
+# Copyright (c) 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#! @PYTHON@
#
-# Copyright (c) 2010 Nicira Networks.
+# Copyright (c) 2010 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
import ovstest.args as args
import ovstest.rpcserver as rpcserver
+import ovstest.tests as tests
+import ovstest.util as util
DEFAULT_TEST_BRIDGE = "ovstestbr0"
DEFAULT_TEST_PORT = "ovstestport0"
DEFAULT_TEST_TUN = "ovstestport1"
-def rpc_client(ip, port):
- return xmlrpclib.Server("http://%s:%u/" % (ip, port), allow_none=True)
-
-
-def sigint_intercept():
- """
- Intercept SIGINT from child (the local ovs-test server process).
- """
- signal.signal(signal.SIGINT, signal.SIG_IGN)
-
-
-def start_local_server(port):
- """
- This function spawns an ovs-test server that listens on specified port
- and blocks till the spawned ovs-test server is ready to accept XML RPC
- connections.
- """
- p = subprocess.Popen(["ovs-test", "-s", str(port)],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- preexec_fn = sigint_intercept)
- fcntl.fcntl( p.stdout.fileno(),fcntl.F_SETFL,
- fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
-
- while p.poll() is None:
- fd = select.select([p.stdout.fileno()], [], [])[0]
- if fd:
- out = p.stdout.readline()
- if out.startswith("Starting RPC server"):
- break
- if p.poll() is not None:
- raise RuntimeError("Couldn't start local instance of ovs-test server")
- return p
-
-
-def get_datagram_sizes(mtu1, mtu2):
- """
- This function calculates all the "interesting" datagram sizes so that
- we test both - receive and send side with different packets sizes.
- """
- s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1])
- s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2])
- return sorted(s1.union(s2))
-
-
-def ip_from_cidr(string):
- """
- This function removes the netmask (if present) from the given string and
- returns the IP address.
- """
- token = string.split("/")
- return token[0]
-
-
-def bandwidth_to_string(bwidth):
- """Convert bandwidth from long to string and add units."""
- bwidth = bwidth * 8 # Convert back to bits/second
- if bwidth >= 10000000:
- return str(int(bwidth / 1000000)) + "Mbps"
- elif bwidth > 10000:
- return str(int(bwidth / 1000)) + "Kbps"
- else:
- return str(int(bwidth)) + "bps"
-
-
def collect_information(node):
"""Print information about hosts that will do testing"""
print "Node %s:%u " % (node[0], node[1])
- server = rpc_client(node[0], node[1])
+ server = util.rpc_client(node[0], node[1])
interface_name = server.get_interface(node[0])
phys_iface = None
uname = server.uname()
return mtu
-def do_udp_tests(receiver, sender, tbwidth, duration, port_sizes):
- """Schedule UDP tests between receiver and sender"""
- server1 = rpc_client(receiver[0], receiver[1])
- server2 = rpc_client(sender[0], sender[1])
-
- udpformat = '{0:>15} {1:>15} {2:>15} {3:>15} {4:>15}'
-
- print ("UDP test from %s:%u to %s:%u with target bandwidth %s" %
- (sender[0], sender[1], receiver[0], receiver[1],
- bandwidth_to_string(tbwidth)))
- print udpformat.format("Datagram Size", "Snt Datagrams", "Rcv Datagrams",
- "Datagram Loss", "Bandwidth")
-
- for size in port_sizes:
- listen_handle = -1
- send_handle = -1
- try:
- packetcnt = (tbwidth * duration) / size
-
- listen_handle = server1.create_udp_listener(receiver[3])
- if listen_handle == -1:
- print ("Server could not open UDP listening socket on port"
- " %u. Try to restart the server.\n" % receiver[3])
- return
- send_handle = server2.create_udp_sender(
- (ip_from_cidr(receiver[2]),
- receiver[3]), packetcnt, size,
- duration)
-
- # Using sleep here because there is no other synchronization source
- # that would notify us when all sent packets were received
- time.sleep(duration + 1)
-
- rcv_packets = server1.get_udp_listener_results(listen_handle)
- snt_packets = server2.get_udp_sender_results(send_handle)
-
- loss = math.ceil(((snt_packets - rcv_packets) * 10000.0) /
- snt_packets) / 100
- bwidth = (rcv_packets * size) / duration
-
- print udpformat.format(size, snt_packets, rcv_packets,
- '%.2f%%' % loss, bandwidth_to_string(bwidth))
- finally:
- if listen_handle != -1:
- server1.close_udp_listener(listen_handle)
- if send_handle != -1:
- server2.close_udp_sender(send_handle)
- print "\n"
-
-
-def do_tcp_tests(receiver, sender, duration):
- """Schedule TCP tests between receiver and sender"""
- server1 = rpc_client(receiver[0], receiver[1])
- server2 = rpc_client(sender[0], sender[1])
-
- tcpformat = '{0:>15} {1:>15} {2:>15}'
- print "TCP test from %s:%u to %s:%u (full speed)" % (sender[0], sender[1],
- receiver[0], receiver[1])
- print tcpformat.format("Snt Bytes", "Rcv Bytes", "Bandwidth")
-
- listen_handle = -1
- send_handle = -1
- try:
- listen_handle = server1.create_tcp_listener(receiver[3])
- if listen_handle == -1:
- print ("Server was unable to open TCP listening socket on port"
- " %u. Try to restart the server.\n" % receiver[3])
- return
- send_handle = server2.create_tcp_sender(ip_from_cidr(receiver[2]),
- receiver[3], duration)
-
- time.sleep(duration + 1)
-
- rcv_bytes = long(server1.get_tcp_listener_results(listen_handle))
- snt_bytes = long(server2.get_tcp_sender_results(send_handle))
-
- bwidth = rcv_bytes / duration
-
- print tcpformat.format(snt_bytes, rcv_bytes,
- bandwidth_to_string(bwidth))
- finally:
- if listen_handle != -1:
- server1.close_tcp_listener(listen_handle)
- if send_handle != -1:
- server2.close_tcp_sender(send_handle)
- print "\n"
-
-
-def do_l3_tests(node1, node2, bandwidth, duration, ps, type):
- """
- Do L3 tunneling tests.
- """
- server1 = rpc_client(node1[0], node1[1])
- server2 = rpc_client(node2[0], node2[1])
- servers_with_bridges = []
- try:
- server1.create_bridge(DEFAULT_TEST_BRIDGE)
- servers_with_bridges.append(server1)
- server2.create_bridge(DEFAULT_TEST_BRIDGE)
- servers_with_bridges.append(server2)
-
- server1.interface_up(DEFAULT_TEST_BRIDGE)
- server2.interface_up(DEFAULT_TEST_BRIDGE)
-
- server1.interface_assign_ip(DEFAULT_TEST_BRIDGE, node1[2], None)
- server2.interface_assign_ip(DEFAULT_TEST_BRIDGE, node2[2], None)
-
- server1.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
- server2.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN)
-
- server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
- None, type)
- server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type",
- None, type)
- server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
- "remote_ip", node2[0])
- server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options",
- "remote_ip", node1[0])
-
- do_udp_tests(node1, node2, bandwidth, duration, ps)
- do_udp_tests(node2, node1, bandwidth, duration, ps)
- do_tcp_tests(node1, node2, duration)
- do_tcp_tests(node2, node1, duration)
-
- finally:
- for server in servers_with_bridges:
- server.del_bridge(DEFAULT_TEST_BRIDGE)
-
-
-
-def do_vlan_tests(node1, node2, bandwidth, duration, ps, tag):
- """
- Do VLAN tests between node1 and node2.
- """
- server1 = rpc_client(node1[0], node1[1])
- server2 = rpc_client(node2[0], node2[1])
-
- br_name1 = None
- br_name2 = None
-
- servers_with_test_ports = []
-
- try:
- interface_node1 = server1.get_interface(node1[0])
- interface_node2 = server2.get_interface(node2[0])
-
- if server1.is_ovs_bridge(interface_node1):
- br_name1 = interface_node1
- else:
- br_name1 = DEFAULT_TEST_BRIDGE
- server1.create_test_bridge(br_name1, interface_node1)
-
- if server2.is_ovs_bridge(interface_node2):
- br_name2 = interface_node2
- else:
- br_name2 = DEFAULT_TEST_BRIDGE
- server2.create_test_bridge(br_name2, interface_node2)
-
- server1.add_port_to_bridge(br_name1, DEFAULT_TEST_PORT)
- servers_with_test_ports.append(server1)
- server2.add_port_to_bridge(br_name2, DEFAULT_TEST_PORT)
- servers_with_test_ports.append(server2)
-
- server1.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
- server2.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag)
-
- server1.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
- "internal")
- server2.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None,
- "internal")
-
- server1.interface_assign_ip(DEFAULT_TEST_PORT, node1[2], None)
- server2.interface_assign_ip(DEFAULT_TEST_PORT, node2[2], None)
-
- server1.interface_up(DEFAULT_TEST_PORT)
- server2.interface_up(DEFAULT_TEST_PORT)
-
- do_udp_tests(node1, node2, bandwidth, duration, ps)
- do_udp_tests(node2, node1, bandwidth, duration, ps)
- do_tcp_tests(node1, node2, duration)
- do_tcp_tests(node2, node1, duration)
-
- finally:
- for server in servers_with_test_ports:
- server.del_port_from_bridge(DEFAULT_TEST_PORT)
- if br_name1 == DEFAULT_TEST_BRIDGE:
- server1.del_test_bridge(br_name1, interface_node1)
- if br_name2 == DEFAULT_TEST_BRIDGE:
- server2.del_test_bridge(br_name2, interface_node2)
-
-
-def do_direct_tests(node1, node2, bandwidth, duration, ps):
- """
- Do tests between outer IPs without involving Open vSwitch
- """
- n1 = (node1[0], node1[1], node1[0], node1[3])
- n2 = (node2[0], node2[1], node2[0], node2[3])
-
- do_udp_tests(n1, n2, bandwidth, duration, ps)
- do_udp_tests(n2, n1, bandwidth, duration, ps)
- do_tcp_tests(n1, n2, duration)
- do_tcp_tests(n2, n1, duration)
-
-
if __name__ == '__main__':
local_server = None
try:
# ovs-test server by looking at the first OuterIP. if it is a
# 127.0.0.1 then spawn local ovs-test server.
if node1[0] == "127.0.0.1":
- local_server = start_local_server(node1[1])
+ local_server = util.start_local_server(node1[1])
# We must determine the IP address that local ovs-test server
# will use:
- me = rpc_client(node1[0], node1[1])
+ me = util.rpc_client(node1[0], node1[1])
my_ip = me.get_my_address_from(node2[0], node2[1])
node1 = (my_ip, node1[1], node1[2], node1[3])
bandwidth = ovs_args.targetBandwidth
interval = ovs_args.testInterval
- ps = get_datagram_sizes(mtu_node1, mtu_node2)
+ ps = util.get_datagram_sizes(mtu_node1, mtu_node2)
direct = ovs_args.direct
vlan_tag = ovs_args.vlanTag
if direct is not None:
print "Performing direct tests"
- do_direct_tests(node2, node1, bandwidth, interval, ps)
+ tests.do_direct_tests(node2, node1, bandwidth, interval, ps)
if vlan_tag is not None:
print "Performing VLAN tests"
- do_vlan_tests(node2, node1, bandwidth, interval, ps, vlan_tag)
+ tests.do_vlan_tests(node2, node1, bandwidth, interval, ps,
+ vlan_tag)
for tmode in tunnel_modes:
print "Performing", tmode, "tests"
- do_l3_tests(node2, node1, bandwidth, interval, ps, tmode)
+ tests.do_l3_tests(node2, node1, bandwidth, interval, ps,
+ tmode)
except KeyboardInterrupt:
pass
/*
- * Copyright (c) 2011 Nicira Networks.
+ * Copyright (c) 2011 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#! @PYTHON@
#
-# Copyright (c) 2010 Nicira Networks.
+# Copyright (c) 2010 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
.
.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.
.IP "\fR[\fB\-\-if\-exists\fR] \fBdestroy \fItable record\fR..."
Deletes each specified \fIrecord\fR from \fItable\fR. Unless
\fB\-\-if\-exists\fR is specified, each \fIrecord\fRs must exist.
+.IP "\fB\-\-all destroy \fItable\fR"
+Deletes all records from the \fItable\fR.
.IP
The \fBdestroy\fR command is only useful for records in the \fBQoS\fR
or \fBQueue\fR tables. Records in other tables are automatically
Remove the mirror created above from \fBbr0\fR, which also destroys
the Mirror record (since it is now unreferenced):
.IP
-.B "remove Bridge br0 mirrors mymirror"
+.B "ovs\-vsctl \-\- \-\-id=@rec get Mirror mymirror \(rs"
+.IP
+.B "\-\- remove Bridge br0 mirrors @rec"
+.PP
+The following simpler command also works:
+.IP
+.B "ovs\-vsctl clear Bridge br0 mirrors"
.SS "Quality of Service (QoS)"
.PP
Create a \fBlinux\-htb\fR QoS record that points to a few queues and
(This command will leave two unreferenced Queue records in the
database. To delete them, use "\fBovs\-vsctl list Queue\fR" to find
their UUIDs, then "\fBovs\-vsctl destroy Queue \fIuuid1\fR
-\fIuuid2\fR" to destroy each of them.)
+\fIuuid2\fR" to destroy each of them or use
+"\fBovs\-vsctl -- --all destroy Queue\fR" to delete all records.)
.SS "Connectivity Monitoring"
.PP
Monitor connectivity to a remote maintenance point on eth0.
.SH "SEE ALSO"
.
.BR ovsdb\-server (1),
-.BR ovs\-vswitchd (8).
+.BR ovs\-vswitchd (8),
+.BR ovs\-vswitchd.conf.db (5).
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "process.h"
#include "stream.h"
#include "stream-ssl.h"
+#include "smap.h"
#include "sset.h"
#include "svec.h"
#include "lib/vswitch-idl.h"
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);
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) {
}
}
-/* 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
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
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);
}
}
cmd_destroy(struct vsctl_context *ctx)
{
bool must_exist = !shash_find(&ctx->options, "--if-exists");
+ bool delete_all = shash_find(&ctx->options, "--all");
const char *table_name = ctx->argv[1];
const struct vsctl_table_class *table;
int i;
table = get_table(table_name);
- for (i = 2; i < ctx->argc; i++) {
+
+ if (delete_all && ctx->argc > 2) {
+ vsctl_fatal("--all and records argument should not be specified together");
+ }
+
+ if (delete_all && !must_exist) {
+ vsctl_fatal("--all and --if-exists should not be specified together");
+ }
+
+ if (delete_all) {
const struct ovsdb_idl_row *row;
+ const struct ovsdb_idl_row *next_row;
- row = (must_exist ? must_get_row : get_row)(ctx, table, ctx->argv[i]);
- if (row) {
- ovsdb_idl_txn_delete(row);
+ for (row = ovsdb_idl_first_row(ctx->idl, table->class);
+ row;) {
+ next_row = ovsdb_idl_next_row(row);
+ ovsdb_idl_txn_delete(row);
+ row = next_row;
}
- }
+ } else {
+ for (i = 2; i < ctx->argc; i++) {
+ const struct ovsdb_idl_row *row;
+ row = (must_exist ? must_get_row : get_row)(ctx, table, ctx->argv[i]);
+ if (row) {
+ ovsdb_idl_txn_delete(row);
+ }
+ }
+ }
vsctl_context_invalidate_cache(ctx);
}
if (ctx.try_again) {
vsctl_context_done(&ctx, NULL);
-
- status = TXN_TRY_AGAIN;
goto try_again;
}
}
table_destroy(c->table);
free(c->table);
- smap_destroy(&c->options);
+ shash_destroy_free_data(&c->options);
}
free(commands);
{"remove", 4, INT_MAX, pre_cmd_remove, cmd_remove, NULL, "", RW},
{"clear", 3, INT_MAX, pre_cmd_clear, cmd_clear, NULL, "", RW},
{"create", 2, INT_MAX, pre_create, cmd_create, post_create, "--id=", RW},
- {"destroy", 1, INT_MAX, pre_cmd_destroy, cmd_destroy, NULL, "--if-exists",
- RW},
+ {"destroy", 1, INT_MAX, pre_cmd_destroy, cmd_destroy, NULL,
+ "--if-exists,--all", RW},
{"wait-until", 2, INT_MAX, pre_cmd_wait_until, cmd_wait_until, NULL, "",
RO},
none of the special features of bonded ports described in this section
apply.
-There are many forms of bonding, but ovs-vswitchd currently implements
-only a single kind, called "source load balancing" or SLB bonding.
-SLB bonding divides traffic among the slaves based on the Ethernet
-source address. This is useful only if the traffic over the bond has
-multiple Ethernet source addresses, for example if network traffic
-from multiple VMs are multiplexed over the bond.
+There are many forms of bonding of which ovs-vswitchd implements only
+a few. The most complex bond ovs-vswitchd implements is called
+"source load balancing" or SLB bonding. SLB bonding divides traffic
+among the slaves based on the Ethernet source address. This is useful
+only if the traffic over the bond has multiple Ethernet source
+addresses, for example if network traffic from multiple VMs are
+multiplexed over the bond.
Enabling and Disabling Slaves
-----------------------------
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
-----------------
otherwise very simple in that, after LACP negotiation is complete,
there is no need for special handling of received packets.
+Active Backup Bonding
+---------------------
+
+Active Backup bonds send all traffic out one "active" slave until that
+slave becomes unavailable. Since they are significantly less
+complicated than SLB bonds, they are preferred when LACP is not an
+option. Additionally, they are the only bond mode which supports
+attaching each slave to a different upstream switch.
+
SLB Bonding
-----------
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "poll-loop.h"
#include "sha1.h"
#include "shash.h"
+#include "smap.h"
#include "socket-util.h"
#include "stream.h"
#include "stream-ssl.h"
/* Most recently processed IDL sequence number. */
static unsigned int idl_seqno;
-/* Each time this timer expires, the bridge fetches systems and interface
+/* Each time this timer expires, the bridge fetches interface and mirror
* statistics and pushes them into the database. */
-#define STATS_INTERVAL (5 * 1000) /* In milliseconds. */
-static long long int stats_timer = LLONG_MIN;
-
-/* Stores the time after which rate limited statistics may be written to the
- * database. Only updated when changes to the database require rate limiting.
- */
-#define DB_LIMIT_INTERVAL (1 * 1000) /* In milliseconds. */
-static long long int db_limiter = LLONG_MIN;
+#define IFACE_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
+static long long int iface_stats_timer = LLONG_MIN;
/* In some datapaths, creating and destroying OpenFlow ports can be extremely
* expensive. This can cause bridge_reconfigure() to take a long time during
uint32_t *bond_stable_ids);
static bool port_is_synthetic(const struct port *);
+static void reconfigure_system_stats(const struct ovsrec_open_vswitch *);
+static void run_system_stats(void);
+
static void bridge_configure_mirrors(struct bridge *);
static struct mirror *mirror_create(struct bridge *,
const struct ovsrec_mirror *);
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.)
*
iface_clear_db_record(if_cfg->cfg);
}
}
+
+ reconfigure_system_stats(ovs_cfg);
}
static bool
struct ofpp_garbage *garbage, *next;
LIST_FOR_EACH_SAFE (garbage, next, list_node, &br->ofpp_garbage) {
+ /* It's a bit dangerous to call bridge_run_fast() here as ofproto's
+ * internal datastructures may not be consistent. Eventually, when
+ * port additions and deletions are cheaper, these calls should be
+ * removed. */
+ bridge_run_fast();
ofproto_port_del(br->ofproto, garbage->ofp_port);
list_remove(&garbage->list_node);
free(garbage);
if (time_msec() >= deadline) {
return false;
}
+ bridge_run_fast();
}
}
* forked us to exit successfully. */
daemonize_complete();
reconfiguring = false;
+
+ VLOG_INFO("%s (Open vSwitch) %s", program_name, VERSION);
}
return done;
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);
memcpy(br->ea, ea, ETH_ADDR_LEN);
dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
- ofproto_set_datapath_id(br->ofproto, dpid);
+ if (dpid != ofproto_get_datapath_id(br->ofproto)) {
+ VLOG_INFO("bridge %s: using datapath ID %016"PRIx64, br->name, dpid);
+ ofproto_set_datapath_id(br->ofproto, dpid);
+ }
dpid_string = xasprintf("%016"PRIx64, dpid);
ovsrec_bridge_set_datapath_id(br->cfg, dpid_string);
const char *config_str;
struct iface *iface;
- config_str = ovsrec_port_get_other_config_value(port->cfg, "stp-enable",
- NULL);
- if (config_str && !strcmp(config_str, "false")) {
+ if (!smap_get_bool(&port->cfg->other_config, "stp-enable", true)) {
port_s->enable = false;
return;
} else {
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;
bitmap_set1(port_num_bitmap, port_idx);
port_s->port_num = port_idx;
} else {
- if (*port_num_counter > STP_MAX_PORTS) {
+ if (*port_num_counter >= STP_MAX_PORTS) {
VLOG_ERR("port %s: too many STP ports, disabling", port->name);
port_s->enable = false;
return;
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 {
}
}
- 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 {
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];
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 {
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));
hmap_remove(&br->if_cfg_todo, &if_cfg->hmap_node);
free(if_cfg);
- /* Do the bits that can fail up front. */
+ /* Do the bits that can fail up front.
+ *
+ * It's a bit dangerous to call bridge_run_fast() here as ofproto's
+ * internal datastructures may not be consistent. Eventually, when port
+ * additions and deletions are cheaper, these calls should be removed. */
+ bridge_run_fast();
assert(!iface_lookup(br, iface_cfg->name));
error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev);
+ bridge_run_fast();
if (error) {
iface_clear_db_record(iface_cfg);
return false;
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 {
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'. */
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);
*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 "
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;
}
static void
iface_refresh_status(struct iface *iface)
{
- struct shash sh;
+ struct smap smap;
enum netdev_features current;
- enum netdev_flags flags;
int64_t bps;
int mtu;
int64_t mtu_64;
return;
}
- shash_init(&sh);
-
- if (!netdev_get_drv_info(iface->netdev, &sh)) {
- size_t n;
- char **keys, **values;
+ smap_init(&smap);
- shash_to_ovs_idl_map(&sh, &keys, &values, &n);
- ovsrec_interface_set_status(iface->cfg, keys, values, n);
-
- free(keys);
- free(values);
+ if (!netdev_get_drv_info(iface->netdev, &smap)) {
+ ovsrec_interface_set_status(iface->cfg, &smap);
} else {
- ovsrec_interface_set_status(iface->cfg, NULL, NULL, 0);
+ ovsrec_interface_set_status(iface->cfg, NULL);
}
- shash_destroy_free_data(&sh);
-
- error = netdev_get_flags(iface->netdev, &flags);
- if (!error) {
- ovsrec_interface_set_admin_state(iface->cfg,
- flags & NETDEV_UP ? "up" : "down");
- }
- else {
- ovsrec_interface_set_admin_state(iface->cfg, NULL);
- }
+ smap_destroy(&smap);
error = netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL);
if (!error) {
}
}
-/* Writes 'iface''s CFM statistics to the database. */
+/* Writes 'iface''s CFM statistics to the database. 'iface' must not be
+ * synthetic. */
static void
iface_refresh_cfm_stats(struct iface *iface)
{
const struct ovsrec_interface *cfg = iface->cfg;
- int fault, error;
+ int fault, opup, error;
const uint64_t *rmps;
size_t n_rmps;
int health;
- if (iface_is_synthetic(iface)) {
- return;
- }
-
fault = ofproto_port_get_cfm_fault(iface->port->bridge->ofproto,
iface->ofp_port);
if (fault >= 0) {
ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0);
}
+ opup = ofproto_port_get_cfm_opup(iface->port->bridge->ofproto,
+ iface->ofp_port);
+ if (opup >= 0) {
+ ovsrec_interface_set_cfm_remote_opstate(cfg, opup ? "up" : "down");
+ } else {
+ ovsrec_interface_set_cfm_remote_opstate(cfg, NULL);
+ }
+
error = ofproto_port_get_cfm_remote_mpids(iface->port->bridge->ofproto,
iface->ofp_port, &rmps, &n_rmps);
if (error >= 0) {
static void
br_refresh_stp_status(struct bridge *br)
{
+ struct smap smap = SMAP_INITIALIZER(&smap);
struct ofproto *ofproto = br->ofproto;
struct ofproto_stp_status status;
- char *keys[3], *values[3];
- size_t i;
if (ofproto_get_stp_status(ofproto, &status)) {
return;
}
if (!status.enabled) {
- ovsrec_bridge_set_status(br->cfg, NULL, NULL, 0);
+ ovsrec_bridge_set_status(br->cfg, NULL);
return;
}
- keys[0] = "stp_bridge_id",
- values[0] = xasprintf(STP_ID_FMT, STP_ID_ARGS(status.bridge_id));
- keys[1] = "stp_designated_root",
- values[1] = xasprintf(STP_ID_FMT, STP_ID_ARGS(status.designated_root));
- keys[2] = "stp_root_path_cost",
- values[2] = xasprintf("%d", status.root_path_cost);
+ smap_add_format(&smap, "stp_bridge_id", STP_ID_FMT,
+ STP_ID_ARGS(status.bridge_id));
+ smap_add_format(&smap, "stp_designated_root", STP_ID_FMT,
+ STP_ID_ARGS(status.designated_root));
+ smap_add_format(&smap, "stp_root_path_cost", "%d", status.root_path_cost);
- ovsrec_bridge_set_status(br->cfg, keys, values, ARRAY_SIZE(values));
-
- for (i = 0; i < ARRAY_SIZE(values); i++) {
- free(values[i]);
- }
+ ovsrec_bridge_set_status(br->cfg, &smap);
+ smap_destroy(&smap);
}
static void
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;
/* 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;
}
}
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";
static bool
enable_system_stats(const struct ovsrec_open_vswitch *cfg)
{
- const char *enable;
+ return smap_get_bool(&cfg->other_config, "enable-statistics", false);
+}
- /* Use other-config:enable-system-stats by preference. */
- enable = ovsrec_open_vswitch_get_other_config_value(cfg,
- "enable-statistics",
- NULL);
- if (enable) {
- return !strcmp(enable, "true");
- }
+static void
+reconfigure_system_stats(const struct ovsrec_open_vswitch *cfg)
+{
+ bool enable = enable_system_stats(cfg);
- /* Disable by default. */
- return false;
+ system_stats_enable(enable);
+ if (!enable) {
+ ovsrec_open_vswitch_set_statistics(cfg, NULL);
+ }
}
static void
-refresh_system_stats(const struct ovsrec_open_vswitch *cfg)
+run_system_stats(void)
{
- struct ovsdb_datum datum;
- struct shash stats;
+ const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(idl);
+ struct smap *stats;
- shash_init(&stats);
- if (enable_system_stats(cfg)) {
- get_system_stats(&stats);
- }
+ stats = system_stats_run();
+ if (stats && cfg) {
+ struct ovsdb_idl_txn *txn;
+ struct ovsdb_datum datum;
- ovsdb_datum_from_shash(&datum, &stats);
- ovsdb_idl_txn_write(&cfg->header_, &ovsrec_open_vswitch_col_statistics,
- &datum);
+ txn = ovsdb_idl_txn_create(idl);
+ ovsdb_datum_from_smap(&datum, stats);
+ ovsdb_idl_txn_write(&cfg->header_, &ovsrec_open_vswitch_col_statistics,
+ &datum);
+ ovsdb_idl_txn_commit(txn);
+ ovsdb_idl_txn_destroy(txn);
+
+ free(stats);
+ }
}
static inline const char *
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);
}
}
}
static void
-refresh_cfm_stats(void)
+refresh_instant_stats(void)
{
static struct ovsdb_idl_txn *txn = NULL;
HMAP_FOR_EACH (br, node, &all_bridges) {
struct iface *iface;
+ struct port *port;
+
+ br_refresh_stp_status(br);
+
+ HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+ port_refresh_stp_status(port);
+ }
HMAP_FOR_EACH (iface, name_node, &br->iface_by_name) {
+ enum netdev_flags flags;
+ const char *link_state;
+ int64_t link_resets;
+ int current, error;
+
+ if (iface_is_synthetic(iface)) {
+ continue;
+ }
+
+ current = ofproto_port_is_lacp_current(br->ofproto,
+ iface->ofp_port);
+ if (current >= 0) {
+ bool bl = current;
+ ovsrec_interface_set_lacp_current(iface->cfg, &bl, 1);
+ } else {
+ ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0);
+ }
+
+ error = netdev_get_flags(iface->netdev, &flags);
+ if (!error) {
+ const char *state = flags & NETDEV_UP ? "up" : "down";
+ ovsrec_interface_set_admin_state(iface->cfg, state);
+ } else {
+ ovsrec_interface_set_admin_state(iface->cfg, NULL);
+ }
+
+ link_state = netdev_get_carrier(iface->netdev) ? "up" : "down";
+ ovsrec_interface_set_link_state(iface->cfg, link_state);
+
+ link_resets = netdev_get_carrier_resets(iface->netdev);
+ ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1);
+
iface_refresh_cfm_stats(iface);
}
}
bool vlan_splinters_changed;
struct bridge *br;
+ ovsrec_open_vswitch_init((struct ovsrec_open_vswitch *) &null_cfg);
+
/* (Re)configure if necessary. */
if (!reconfiguring) {
ovsdb_idl_run(idl);
reconf_txn = NULL;
}
- /* Refresh system and interface stats if necessary. */
- if (time_msec() >= stats_timer) {
+ /* Refresh interface and mirror stats if necessary. */
+ if (time_msec() >= iface_stats_timer) {
if (cfg) {
struct ovsdb_idl_txn *txn;
}
}
- refresh_system_stats(cfg);
refresh_controller_status();
ovsdb_idl_txn_commit(txn);
ovsdb_idl_txn_destroy(txn); /* XXX */
}
- stats_timer = time_msec() + STATS_INTERVAL;
+ iface_stats_timer = time_msec() + IFACE_STATS_INTERVAL;
}
- if (time_msec() >= db_limiter) {
- struct ovsdb_idl_txn *txn;
-
- txn = ovsdb_idl_txn_create(idl);
- HMAP_FOR_EACH (br, node, &all_bridges) {
- struct iface *iface;
- struct port *port;
-
- br_refresh_stp_status(br);
-
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- port_refresh_stp_status(port);
- }
-
- HMAP_FOR_EACH (iface, name_node, &br->iface_by_name) {
- const char *link_state;
- int64_t link_resets;
- int current;
-
- if (iface_is_synthetic(iface)) {
- continue;
- }
-
- current = ofproto_port_is_lacp_current(br->ofproto,
- iface->ofp_port);
- if (current >= 0) {
- bool bl = current;
- ovsrec_interface_set_lacp_current(iface->cfg, &bl, 1);
- } else {
- ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0);
- }
-
- link_state = netdev_get_carrier(iface->netdev) ? "up" : "down";
- ovsrec_interface_set_link_state(iface->cfg, link_state);
-
- link_resets = netdev_get_carrier_resets(iface->netdev);
- ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1);
- }
- }
-
- if (ovsdb_idl_txn_commit(txn) != TXN_UNCHANGED) {
- db_limiter = time_msec() + DB_LIMIT_INTERVAL;
- }
- ovsdb_idl_txn_destroy(txn);
- }
-
- refresh_cfm_stats();
+ run_system_stats();
+ refresh_instant_stats();
}
void
HMAP_FOR_EACH (br, node, &all_bridges) {
ofproto_wait(br->ofproto);
}
- poll_timer_wait_until(stats_timer);
+ poll_timer_wait_until(iface_stats_timer);
+ }
- if (db_limiter > time_msec()) {
- poll_timer_wait_until(db_limiter);
- }
+ system_stats_wait();
+}
+
+/* Adds some memory usage statistics for bridges into 'usage', for use with
+ * memory_report(). */
+void
+bridge_get_memory_usage(struct simap *usage)
+{
+ struct bridge *br;
+
+ HMAP_FOR_EACH (br, node, &all_bridges) {
+ ofproto_get_memory_usage(br->ofproto, usage);
}
}
\f
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");
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);
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;
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;
unixctl_command_reply_error(conn, ds_cstr(&ds));
}
- shash_destroy_free_data(&sh);
+ smap_destroy(&smap);
ds_destroy(&ds);
}
\f
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;
for (i = 0; i < port->n_interfaces; i++) {
const struct ovsrec_interface *cfg = port->interfaces[i];
struct iface *iface = iface_lookup(br, cfg->name);
+ const char *type = iface_get_type(cfg, br->cfg);
if (iface) {
iface->cfg = cfg;
- iface->type = iface_get_type(cfg, br->cfg);
+ iface->type = type;
+ } else if (!strcmp(type, "null")) {
+ VLOG_WARN_ONCE("%s: The null interface type is deprecated and"
+ " may be removed in February 2013. Please email"
+ " dev@openvswitch.org with concerns.",
+ cfg->name);
} else {
bridge_queue_if_cfg(br, cfg, port);
}
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;
? *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
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;
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);
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) {
}
/* 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;
}
{
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;
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, "
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;
}
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;
}
{
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);
}
}
-/* 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;
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_;
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;
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);
}
}
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;
}
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);
}
}
- 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);
}
* 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
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
/* 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);
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;
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;
}
-/* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#ifndef VSWITCHD_BRIDGE_H
#define VSWITCHD_BRIDGE_H 1
+struct simap;
+
void bridge_init(const char *remote);
void bridge_exit(void);
void bridge_run_fast(void);
void bridge_wait(void);
+void bridge_get_memory_usage(struct simap *usage);
+
#endif /* bridge.h */
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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;
return 0;
}
-/* Composes and returns a reply to a request made by the datapath with Netlink
- * sequence number 'seq' and error code 'error'. The caller may add additional
- * attributes to the message, then it may send it with send_reply(). */
+/* Composes and returns a reply to a request made by the datapath with error
+ * code 'error'. The caller may add additional attributes to the message, then
+ * it may send it with send_reply(). */
static struct ofpbuf *
-compose_reply(uint32_t seq, int error)
+compose_reply(int error)
{
struct ofpbuf *reply = ofpbuf_new(4096);
nl_msg_put_genlmsghdr(reply, 32, brc_family, NLM_F_REQUEST,
BRC_GENL_C_DP_RESULT, 1);
- ((struct nlmsghdr *) reply->data)->nlmsg_seq = seq;
nl_msg_put_u32(reply, BRC_GENL_A_ERR_CODE, error);
return reply;
}
-/* Sends 'reply' to the datapath and frees it. */
+/* Sends 'reply' to the datapath, using sequence number 'nlmsg_seq', and frees
+ * it. */
static void
-send_reply(struct ofpbuf *reply)
+send_reply(struct ofpbuf *reply, uint32_t nlmsg_seq)
{
- int retval = nl_sock_send(brc_sock, reply, false);
+ int retval = nl_sock_send_seq(brc_sock, reply, nlmsg_seq, false);
if (retval) {
VLOG_WARN_RL(&rl, "replying to brcompat request: %s",
strerror(retval));
static void
send_simple_reply(uint32_t seq, int error)
{
- send_reply(compose_reply(seq, error));
+ send_reply(compose_reply(error), seq);
}
static int
free(output);
/* Compose and send reply to datapath. */
- reply = compose_reply(seq, 0);
+ reply = compose_reply(0);
nl_msg_put_unspec(reply, BRC_GENL_A_FDB_DATA,
query_data.data, query_data.size);
- send_reply(reply);
+ send_reply(reply, seq);
/* Free memory. */
ofpbuf_uninit(&query_data);
}
/* Compose and send reply. */
- reply = compose_reply(seq, 0);
+ reply = compose_reply(0);
nl_msg_put_unspec(reply, BRC_GENL_A_IFINDEXES,
indices, n_indices * sizeof *indices);
- send_reply(reply);
+ send_reply(reply, seq);
/* Free memory. */
free(indices);
.
.so ofproto/ofproto-unixctl.man
.so lib/vlog-unixctl.man
+.so lib/memory-unixctl.man
.so lib/stress-unixctl.man
+.
+.SH "LIMITS"
+.
+.PP
+We believe these limits to be accurate as of this writing. These
+limits assume the use of the Linux kernel datapath.
+.
+.IP \(bu
+Approximately 256 bridges given the allowance of 5,000 file
+descriptors that \fBovs\-ctl\fR(8) configures. (\fBovs\-vswitchd\fR
+requires 17 file descriptors per datapath.)
+.
+.IP \(bu
+65,280 ports per bridge. Performance will degrade beyond 1,024 ports
+per bridge due to fixed hash table sizing.
+.
+.IP \(bu
+2,048 MAC learning entries per bridge.
+.
+.IP \(bu
+Kernel flows are limited only by memory available to the kernel.
+Performance will degrade beyond 1,048,576 kernel flows per bridge with
+a 32-bit kernel, beyond 262,144 with a 64-bit kernel.
+(\fBovs\-vswitchd\fR should never install anywhere near that many
+flows.)
+.
+.IP \(bu
+OpenFlow flows are limited only by available memory. Performance is
+linear in the number of unique wildcard patterns. That is, an
+OpenFlow table that contains many flows that all match on the same
+fields in the same way has a constant-time lookup, but a table that
+contains many flows that match on different fields requires lookup
+time linear in the number of flows.
+.
+.IP \(bu
+255 ports per bridge participating in 802.1D Spanning Tree Protocol.
+.
+.IP \(bu
+32 mirrors per bridge.
+.
+.IP \(bu
+15 bytes for the name of a port. (This is a Linux kernel limitation.)
+.
.SH "SEE ALSO"
.BR ovs\-appctl (8),
.BR ovs\-brcompatd (8),
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "dpif.h"
#include "dummy.h"
#include "leak-checker.h"
+#include "memory.h"
#include "netdev.h"
#include "openflow/openflow.h"
#include "ovsdb-idl.h"
#include "poll-loop.h"
#include "process.h"
#include "signals.h"
+#include "simap.h"
#include "stream-ssl.h"
#include "stream.h"
#include "stress.h"
#include "vconn.h"
#include "vlog.h"
#include "lib/vswitch-idl.h"
+#include "worker.h"
VLOG_DEFINE_THIS_MODULE(vswitchd);
+/* --mlockall: If set, locks all process memory into physical RAM, preventing
+ * the kernel from paging any of its memory to disk. */
+static bool want_mlockall;
+
static unixctl_cb_func ovs_vswitchd_exit;
static char *parse_options(int argc, char *argv[], char **unixctl_path);
daemonize_start();
+ if (want_mlockall) {
+#ifdef HAVE_MLOCKALL
+ if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
+ VLOG_ERR("mlockall failed: %s", strerror(errno));
+ }
+#else
+ VLOG_ERR("mlockall not supported on this system");
+#endif
+ }
+
+ worker_start();
+
retval = unixctl_server_create(unixctl_path, &unixctl);
if (retval) {
exit(EXIT_FAILURE);
exiting = false;
while (!exiting) {
+ worker_run();
if (signal_poll(sighup)) {
vlog_reopen_log_file();
}
+ memory_run();
+ if (memory_should_report()) {
+ struct simap usage;
+
+ simap_init(&usage);
+ bridge_get_memory_usage(&usage);
+ memory_report(&usage);
+ simap_destroy(&usage);
+ }
bridge_run_fast();
bridge_run();
bridge_run_fast();
unixctl_server_run(unixctl);
netdev_run();
+ worker_wait();
signal_wait(sighup);
+ memory_wait();
bridge_wait();
unixctl_server_wait(unixctl);
netdev_wait();
exit(EXIT_SUCCESS);
case OPT_MLOCKALL:
-#ifdef HAVE_MLOCKALL
- if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
- VLOG_ERR("mlockall failed: %s", strerror(errno));
- }
-#else
- VLOG_ERR("mlockall not supported on this system");
-#endif
+ want_mlockall = true;
break;
case OPT_UNIXCTL:
-/* Copyright (c) 2010 Nicira Networks
+/* Copyright (c) 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <config.h>
+#include "system-stats.h"
+
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include "daemon.h"
#include "dirs.h"
#include "dynamic-string.h"
+#include "json.h"
+#include "ofpbuf.h"
+#include "poll-loop.h"
#include "shash.h"
-#include "system-stats.h"
+#include "smap.h"
#include "timeval.h"
#include "vlog.h"
+#include "worker.h"
VLOG_DEFINE_THIS_MODULE(system_stats);
#endif
static void
-get_cpu_cores(struct shash *stats)
+get_cpu_cores(struct smap *stats)
{
long int n_cores = sysconf(_SC_NPROCESSORS_ONLN);
if (n_cores > 0) {
- shash_add(stats, "cpu", xasprintf("%ld", n_cores));
+ smap_add_format(stats, "cpu", "%ld", n_cores);
}
}
static void
-get_load_average(struct shash *stats OVS_UNUSED)
+get_load_average(struct smap *stats OVS_UNUSED)
{
#if HAVE_GETLOADAVG
double loadavg[3];
if (getloadavg(loadavg, 3) == 3) {
- shash_add(stats, "load_average",
- xasprintf("%.2f,%.2f,%.2f",
- loadavg[0], loadavg[1], loadavg[2]));
+ smap_add_format(stats, "load_average", "%.2f,%.2f,%.2f",
+ loadavg[0], loadavg[1], loadavg[2]);
}
#endif
}
}
static void
-get_memory_stats(struct shash *stats)
+get_memory_stats(struct smap *stats)
{
if (!LINUX) {
unsigned int pagesize = get_page_size();
mem_total = phys_pages * (pagesize / 1024);
mem_used = (phys_pages - avphys_pages) * (pagesize / 1024);
- shash_add(stats, "memory", xasprintf("%d,%d", mem_total, mem_used));
+ smap_add_format(stats, "memory", "%d,%d", mem_total, mem_used);
} else {
static const char file_name[] = "/proc/meminfo";
int mem_used, mem_cache, swap_used;
mem_used = mem_total - mem_free;
mem_cache = buffers + cached;
swap_used = swap_total - swap_free;
- shash_add(stats, "memory",
- xasprintf("%d,%d,%d,%d,%d", mem_total, mem_used, mem_cache,
- swap_total, swap_used));
+ smap_add_format(stats, "memory", "%d,%d,%d,%d,%d",
+ mem_total, mem_used, mem_cache, swap_total, swap_used);
}
}
}
static void
-get_process_stats(struct shash *stats)
+get_process_stats(struct smap *stats)
{
struct dirent *de;
DIR *dir;
while ((de = readdir(dir)) != NULL) {
struct process_info pinfo;
- char *key, *value;
char *file_name;
char *extension;
+ char *key;
pid_t pid;
#ifdef _DIRENT_HAVE_D_TYPE
key = xasprintf("process_%.*s",
(int) (extension - de->d_name), de->d_name);
- if (shash_find(stats, key)) {
- free(key);
- continue;
- }
-
- if (LINUX && get_process_info(pid, &pinfo)) {
- value = xasprintf("%lu,%lu,%lld,%d,%lld,%lld",
- pinfo.vsz, pinfo.rss, pinfo.cputime,
- pinfo.crashes, pinfo.booted, pinfo.uptime);
- } else {
- value = xstrdup("");
+ if (!smap_get(stats, key)) {
+ if (LINUX && get_process_info(pid, &pinfo)) {
+ smap_add_format(stats, key, "%lu,%lu,%lld,%d,%lld,%lld",
+ pinfo.vsz, pinfo.rss, pinfo.cputime,
+ pinfo.crashes, pinfo.booted, pinfo.uptime);
+ } else {
+ smap_add(stats, key, "");
+ }
}
-
- shash_add_nocopy(stats, key, value);
+ free(key);
}
closedir(dir);
}
static void
-get_filesys_stats(struct shash *stats OVS_UNUSED)
+get_filesys_stats(struct smap *stats OVS_UNUSED)
{
#if HAVE_SETMNTENT && HAVE_STATVFS
static const char file_name[] = "/etc/mtab";
endmntent(stream);
if (s.length) {
- shash_add(stats, "file_systems", ds_steal_cstr(&s));
+ smap_add(stats, "file_systems", ds_cstr(&s));
}
ds_destroy(&s);
#endif /* HAVE_SETMNTENT && HAVE_STATVFS */
}
+\f
+#define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
+
+/* Whether the client wants us to report system stats. */
+static bool enabled;
+
+static enum {
+ S_DISABLED, /* Not enabled, nothing going on. */
+ S_WAITING, /* Sleeping for SYSTEM_STATS_INTERVAL ms. */
+ S_REQUEST_SENT, /* Sent a request to worker. */
+ S_REPLY_RECEIVED /* Received a reply from worker. */
+} state;
+
+/* In S_WAITING state: the next time to wake up.
+ * In other states: not meaningful. */
+static long long int next_refresh;
+
+/* In S_REPLY_RECEIVED: the stats that have just been received.
+ * In other states: not meaningful. */
+static struct smap *received_stats;
+
+static worker_request_func system_stats_request_cb;
+static worker_reply_func system_stats_reply_cb;
+
+/* Enables or disables system stats collection, according to 'new_enable'.
+ *
+ * Even if system stats are disabled, the caller should still periodically call
+ * system_stats_run(). */
+void
+system_stats_enable(bool new_enable)
+{
+ if (new_enable != enabled) {
+ if (new_enable) {
+ if (state == S_DISABLED) {
+ state = S_WAITING;
+ next_refresh = time_msec();
+ }
+ } else {
+ if (state == S_WAITING) {
+ state = S_DISABLED;
+ }
+ }
+ enabled = new_enable;
+ }
+}
+/* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL
+ * milliseconds.
+ *
+ * When a new snapshot is available (which only occurs if system stats are
+ * enabled), returns it as an smap owned by the caller. The caller must use
+ * both smap_destroy() and free() to complete free the returned data.
+ *
+ * When no new snapshot is available, returns NULL. */
+struct smap *
+system_stats_run(void)
+{
+ switch (state) {
+ case S_DISABLED:
+ break;
+
+ case S_WAITING:
+ if (time_msec() >= next_refresh) {
+ worker_request(NULL, 0, NULL, 0, system_stats_request_cb,
+ system_stats_reply_cb, NULL);
+ state = S_REQUEST_SENT;
+ }
+ break;
+
+ case S_REQUEST_SENT:
+ break;
+
+ case S_REPLY_RECEIVED:
+ if (enabled) {
+ state = S_WAITING;
+ next_refresh = time_msec() + SYSTEM_STATS_INTERVAL;
+ return received_stats;
+ } else {
+ smap_destroy(received_stats);
+ free(received_stats);
+ state = S_DISABLED;
+ }
+ break;
+ }
+
+ return NULL;
+}
+
+/* Causes poll_block() to wake up when system_stats_run() needs to be
+ * called. */
void
-get_system_stats(struct shash *stats)
+system_stats_wait(void)
{
- get_cpu_cores(stats);
- get_load_average(stats);
- get_memory_stats(stats);
- get_process_stats(stats);
- get_filesys_stats(stats);
+ switch (state) {
+ case S_DISABLED:
+ break;
+
+ case S_WAITING:
+ poll_timer_wait_until(next_refresh);
+ break;
+
+ case S_REQUEST_SENT:
+ /* Someone else should be calling worker_wait() to wake up when the
+ * reply arrives, otherwise there's a bug. */
+ break;
+
+ case S_REPLY_RECEIVED:
+ poll_immediate_wake();
+ break;
+ }
+}
+
+static void
+system_stats_request_cb(struct ofpbuf *request OVS_UNUSED,
+ const int fds[] OVS_UNUSED, size_t n_fds OVS_UNUSED)
+{
+ struct smap stats;
+ struct json *json;
+ char *s;
+
+ smap_init(&stats);
+ get_cpu_cores(&stats);
+ get_load_average(&stats);
+ get_memory_stats(&stats);
+ get_process_stats(&stats);
+ get_filesys_stats(&stats);
+
+ json = smap_to_json(&stats);
+ s = json_to_string(json, 0);
+ worker_reply(s, strlen(s) + 1, NULL, 0);
+
+ free(s);
+ json_destroy(json);
+ smap_destroy(&stats);
+}
+
+static void
+system_stats_reply_cb(struct ofpbuf *reply,
+ const int fds[] OVS_UNUSED, size_t n_fds OVS_UNUSED,
+ void *aux OVS_UNUSED)
+{
+ struct json *json = json_from_string(reply->data);
+
+ received_stats = xmalloc(sizeof *received_stats);
+ smap_init(received_stats);
+ smap_from_json(received_stats, json);
+
+ assert(state == S_REQUEST_SENT);
+ state = S_REPLY_RECEIVED;
+
+ json_destroy(json);
}
-/* Copyright (c) 2010 Nicira Networks
+/* Copyright (c) 2010, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#ifndef VSWITCHD_SYSTEM_STATS
#define VSWITCHD_SYSTEM_STATS 1
-void get_system_stats(struct shash *);
+#include <stdbool.h>
+
+void system_stats_enable(bool enable);
+struct smap *system_stats_run(void);
+void system_stats_wait(void);
#endif /* vswitchd/system-stats.h */
{"name": "Open_vSwitch",
- "version": "6.9.3",
- "cksum": "2110020336 16754",
+ "version": "6.10.0",
+ "cksum": "3699312094 16958",
"tables": {
"Open_vSwitch": {
"columns": {
"type": {
"key": "string", "min": 0, "max": "unlimited"},
"ephemeral": true},
+ "cfm_remote_opstate": {
+ "type": {"key": {"type": "string",
+ "enum": ["set", ["up", "down"]]},
+ "min": 0, "max": 1},
+ "ephemeral": true},
"cfm_health": {
"type": {"key": {"type": "integer",
"minInteger": 0,
any defined controllers forever.</dd>
</dl>
</p>
- <p>If this value is unset, the default is implementation-specific.</p>
+ <p>
+ The default is <code>standalone</code> if the value is unset, but
+ future versions of Open vSwitch may change the default.
+ </p>
+ <p>
+ The <code>standalone</code> mode can create forwarding loops on a
+ bridge that has more than one uplink port unless STP is enabled. To
+ avoid loops on such a bridge, configure <code>secure</code> mode or
+ enable STP (see <ref column="stp_enable"/>).
+ </p>
<p>When more than one controller is configured,
<ref column="fail_mode"/> is considered only when none of the
configured controllers can be contacted.</p>
and if Open vSwitch node does not run STP, then this option
should be enabled. Default is disabled, set to
<code>true</code> to enable.
+
+ The following destination MAC addresss will not be forwarded when this
+ option is enabled.
+ <dl>
+ <dt><code>01:80:c2:00:00:00</code></dt>
+ <dd>IEEE 802.1D Spanning Tree Protocol (STP).</dd>
+
+ <dt><code>01:80:c2:00:00:01</code></dt>
+ <dd>IEEE Pause frame.</dd>
+
+ <dt><code>01:80:c2:00:00:0<var>x</var></code></dt>
+ <dd>Other reserved protocols.</dd>
+
+ <dt><code>00:e0:2b:00:00:00</code></dt>
+ <dd>Extreme Discovery Protocol (EDP).</dd>
+
+ <dt>
+ <code>00:e0:2b:00:00:04</code> and <code>00:e0:2b:00:00:06</code>
+ </dt>
+ <dd>Ethernet Automatic Protection Switching (EAPS).</dd>
+
+ <dt><code>01:00:0c:cc:cc:cc</code></dt>
+ <dd>
+ Cisco Discovery Protocol (CDP), VLAN Trunking Protocol (VTP),
+ Dynamic Trunking Protocol (DTP), Port Aggregation Protocol (PAgP),
+ and others.
+ </dd>
+
+ <dt><code>01:00:0c:cc:cc:cd</code></dt>
+ <dd>Cisco Shared Spanning Tree Protocol PVSTP+.</dd>
+
+ <dt><code>01:00:0c:cd:cd:cd</code></dt>
+ <dd>Cisco STP Uplink Fast.</dd>
+
+ <dt><code>01:00:0c:00:00:00</code></dt>
+ <dd>Cisco Inter Switch Link.</dd>
+
+ <dt><code>01:00:0c:cc:cc:c<var>x</var></code></dt>
+ <dd>Cisco CFM.</dd>
+ </dl>
</column>
<column name="other_config" key="mac-aging-time"
<group title="Bonding Configuration">
<p>A port that has more than one interface is a ``bonded port.'' Bonding
- allows for load balancing and fail-over. Some kinds of bonding will
- work with any kind of upstream switch:</p>
+ allows for load balancing and fail-over.</p>
+
+ <p>
+ The following types of bonding will work with any kind of upstream
+ switch. On the upstream switch, do not configure the interfaces as a
+ bond:
+ </p>
<dl>
<dt><code>balance-slb</code></dt>
<dt><code>active-backup</code></dt>
<dd>
Assigns all flows to one slave, failing over to a backup slave when
- the active slave is disabled.
+ the active slave is disabled. This is the only bonding mode in which
+ interfaces may be plugged into different upstream switches.
</dd>
</dl>
<dt><code>stable</code></dt>
<dd>
+ <p>Deprecated and slated for removal in February 2013.</p>
<p>Attempts to always assign a given flow to the same slave
consistently. In an effort to maintain stability, no load
balancing is done. Uses a similar hashing strategy to
<column name="bond_updelay">
<p>
- The number of milliseconds for which carrier must stay up on an
+ The number of milliseconds for which the link must stay up on an
interface before the interface is considered to be up. Specify
<code>0</code> to enable the interface immediately.
</p>
</column>
<column name="bond_downdelay">
- The number of milliseconds for which carrier must stay down on an
+ The number of milliseconds for which the link must stay down on an
interface before the interface is considered to be down. Specify
<code>0</code> to disable the interface immediately.
</column>
</column>
</group>
- <group title="SLB Configuration">
+ <group title="Rebalancing Configuration">
<p>
These settings control behavior when a bond is in
- <code>balance-slb</code> mode, regardless of whether the bond was
- intentionally configured in SLB mode or it fell back to SLB mode
- because LACP negotiation failed.
+ <code>balance-slb</code> or <code>balance-tcp</code> mode.
</p>
<column name="other_config" key="bond-rebalance-interval"
successive attempts to rebalance the bond, that is, to move flows
from one interface on the bond to another in an attempt to keep usage
of each interface roughly equal. If zero, load balancing is disabled
- on the bond (carrier status changes still cause flows to move). If
+ on the bond (link failure still cause flows to move). If
less than 1000ms, the rebalance interval will be 1000ms.
</column>
</group>
</dd>
<dt><code>null</code></dt>
- <dd>An ignored interface.</dd>
+ <dd>An ignored interface. Deprecated and slated for removal in
+ February 2013.</dd>
</dl>
</column>
</group>
<column name="options" key="tos">
Optional. The value of the ToS bits to be set on the encapsulating
- packet. It may also be the word <code>inherit</code>, in which case
+ packet. ToS is interpreted as DSCP and ECN bits, ECN part must be
+ zero. It may also be the word <code>inherit</code>, in which case
the ToS will be copied from the inner packet if it is IPv4 or IPv6
(otherwise it will be 0). The ECN fields are always inherited.
Default is 0.
frame having an invalid interval.
</column>
- <column name="cfm_fault_status" key="sequence">
- Indicates a CFM fault was triggered because the CFM module received
- a CCM frame with a sequence number that it was not expecting.
+ <column name="cfm_remote_opstate">
+ <p>When in extended mode, indicates the operational state of the
+ remote endpoint as either <code>up</code> or <code>down</code>. See
+ <ref column="other_config" key="cfm_opstate"/>.
+ </p>
</column>
<column name="cfm_health">
<column name="other_config" key="cfm_interval"
type='{"type": "integer"}'>
- The interval, in milliseconds, between transmissions of CFM heartbeats.
- Three missed heartbeat receptions indicate a connectivity fault.
- Defaults to 1000.
+ <p>
+ The interval, in milliseconds, between transmissions of CFM
+ heartbeats. Three missed heartbeat receptions indicate a
+ connectivity fault.
+ </p>
+
+ <p>
+ In standard operation only intervals of 3, 10, 100, 1,000, 10,000,
+ 60,000, or 600,000 ms are supported. Other values will be rounded
+ down to the nearest value on the list. Extended mode (see <ref
+ column="other_config" key="cfm_extended"/>) supports any interval up
+ to 65,535 ms. In either mode, the default is 1000 ms.
+ </p>
+
+ <p>We do not recommend using intervals less than 100 ms.</p>
</column>
<column name="other_config" key="cfm_extended"
<p>
The eviction process only considers flows that have an idle timeout or
a hard timeout. That is, eviction never deletes permanent flows.
- (Permanent flows do count against <ref column="flow_limit"/>.
+ (Permanent flows do count against <ref column="flow_limit"/>.)
</p>
<p>
sent out an implicit VLAN port, the frame will not be tagged. This
type of mirroring is sometimes called RSPAN.</p>
<p>
- The following destination MAC addresses will not be mirrored to a
- VLAN to avoid confusing switches that interpret the protocols that
- they represent:
+ See the documentation for
+ <ref column="other_config" key="forward-bpdu"/> in the
+ <ref table="Interface"/> table for a list of destination MAC
+ addresses which will not be mirrored to a VLAN to avoid confusing
+ switches that interpret the protocols that they represent.
</p>
- <dl>
- <dt><code>01:80:c2:00:00:00</code></dt>
- <dd>IEEE 802.1D Spanning Tree Protocol (STP).</dd>
-
- <dt><code>01:80:c2:00:00:01</code></dt>
- <dd>IEEE Pause frame.</dd>
-
- <dt><code>01:80:c2:00:00:0<var>x</var></code></dt>
- <dd>Other reserved protocols.</dd>
-
- <dt><code>01:00:0c:cc:cc:cc</code></dt>
- <dd>
- Cisco Discovery Protocol (CDP), VLAN Trunking Protocol (VTP),
- Dynamic Trunking Protocol (DTP), Port Aggregation Protocol (PAgP),
- and others.
- </dd>
-
- <dt><code>01:00:0c:cc:cc:cd</code></dt>
- <dd>Cisco Shared Spanning Tree Protocol PVSTP+.</dd>
-
- <dt><code>01:00:0c:cd:cd:cd</code></dt>
- <dd>Cisco STP Uplink Fast.</dd>
-
- <dt><code>01:00:0c:00:00:00</code></dt>
- <dd>Cisco Inter Switch Link.</dd>
- </dl>
<p><em>Please note:</em> Mirroring to a VLAN can disrupt a network that
contains unmanaged switches. Consider an unmanaged physical switch
with two ports: port 1, connected to an end host, and port 2,
Service (QoS) on IP networks.
The DSCP value specified here is used when establishing the connection
- between the controller and the Open vSwitch. The connection must be
- reset for the new DSCP values to take effect. If no value is
- specified, a default value of 48 is chosen. Valid DSCP values must be
- in the range 0 to 63.
+ between the controller and the Open vSwitch. If no value is specified,
+ a default value of 48 is chosen. Valid DSCP values must be in the
+ range 0 to 63.
</column>
</group>
Service (QoS) on IP networks.
The DSCP value specified here is used when establishing the connection
- between the manager and the Open vSwitch. The connection must be
- reset for the new DSCP values to take effect. If no value is
- specified, a default value of 48 is chosen. Valid DSCP values must be
- in the range 0 to 63.
+ between the manager and the Open vSwitch. If no value is specified, a
+ default value of 48 is chosen. Valid DSCP values must be in the range
+ 0 to 63.
</column>
</group>
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* Copyright (c) 2009, 2010 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
-/* Copyright (c) 2009 Nicira Networks
+/* Copyright (c) 2009 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* ovs-bugtool-lcap-show
* ovs-bugtool-ovsdb-dump
* ovs-bugtool-tc-class-show
+ * ovs-bugtool-bond-show
system-configuration/openvswitch.xml
Script to dump version information for all Open vSwitch daemons.
----------------------------------------------------------------------
-Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+Copyright (C) 2009, 2010, 2011 Nicira, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# chkconfig: 2345 09 91
# description: Manage Open vSwitch kernel modules and user-space daemons
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
touch /var/run/openvswitch.booted
set "$@" --delete-bridges
fi
+ set "$@" $OVS_CTL_OPTS
"$@"
start_ovs_xapi_sync
# chkconfig: 2345 95 01
# description: Update Open vSwitch configuration from XAPI database at boot
-# Copyright (C) 2009, 2010 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
missingok
postrotate
# Tell Open vSwitch daemons to reopen their log files
- if [ -e /var/run/openvswitch/ovs-vswitchd.pid ]; then
- /usr/bin/ovs-appctl -t ovs-vswitchd vlog/reopen
- fi
- if [ -e /var/run/openvswitch/ovsdb-server.pid ]; then
- /usr/bin/ovs-appctl -t ovsdb-server vlog/reopen
- fi
+ for pidfile in `cd /var/run/openvswitch && echo *.pid`; do
+ ovs-appctl -t "${pidfile%%.pid}" vlog/reopen
+ done
endscript
}
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# ovs-vswitchd configuration that are managed in the xapi database when
# integrated with Citrix management tools.
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
"--", "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 = ""
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",
# Spec file for Open vSwitch.
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
Summary: Open vSwitch daemon/database/utilities
Group: System Environment/Daemons
URL: http://www.openvswitch.org/
-Vendor: Nicira Networks, Inc.
+Vendor: Nicira, Inc.
Version: %{openvswitch_version}
License: ASL 2.0
$RPM_BUILD_ROOT/usr/bin/ovs-benchmark \
$RPM_BUILD_ROOT/usr/sbin/ovs-bugtool \
$RPM_BUILD_ROOT/usr/bin/ovs-controller \
+ $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
$RPM_BUILD_ROOT/usr/bin/ovs-pki \
$RPM_BUILD_ROOT/usr/bin/ovs-test \
- $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \
$RPM_BUILD_ROOT/usr/share/man/man1/ovs-benchmark.1 \
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-bugtool.8 \
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
- $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8
+ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \
+ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 \
+ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8
install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch
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
/etc/logrotate.d/openvswitch
/etc/profile.d/openvswitch.sh
/usr/share/openvswitch/python/
+/usr/share/openvswitch/scripts/ovs-check-dead-ifs
/usr/share/openvswitch/scripts/ovs-xapi-sync
/usr/share/openvswitch/scripts/interface-reconfigure
/usr/share/openvswitch/scripts/InterfaceReconfigure.py
# Copyright (c) 2008,2009,2011 Citrix Systems, Inc.
-# Copyright (c) 2009,2010,2011 Nicira Networks.
+# Copyright (c) 2009,2010,2011,2012 Nicira, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
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 = ""
-#!/usr/bin/python
+#!/usr/bin/env python
#
# Copyright (c) 2008,2009 Citrix Systems, Inc.
#
# Copyright (c) 2007-2011 Citrix Systems Inc.
-# Copyright (c) 2009,2010,2011 Nicira Networks.
+# Copyright (c) 2009,2010,2011,2012 Nicira, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@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))
#!/usr/bin/python
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira Networks
+# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
import ovs.daemon
import ovs.db.idl
import ovs.unixctl
+import ovs.unixctl.server
vlog = ovs.vlog.Vlog("ovs-xapi-sync")
session = None
" XAPI session could not be initialized" % br_name)
return None
- for n in session.xenapi.network.get_all():
- rec = session.xenapi.network.get_record(n)
- if rec['bridge'] == br_name:
- return rec
+ recs = session.xenapi.network.get_all_records_where('field "bridge"="%s"' % br_name)
+ if len(recs) > 0:
+ return recs.values()[0]
return None
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)
### Configuration options for openvswitch
-# Copyright (C) 2009, 2010, 2011 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
# FORCE_COREFILES: If 'yes' then core files will be enabled.
# FORCE_COREFILES=yes
# 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=