Krishna Kondaka kkondaka@vmware.com
Kyle Mestery kmestery@cisco.com
Leo Alterman lalterman@nicira.com
+Lorand Jakab lojakab@cisco.com
Luca Giraudo lgiraudo@nicira.com
Martin Casado casado@nicira.com
Mehak Mahajan mmahajan@nicira.com
Raju Subramanian rsubramanian@nicira.com
Ravi Kerur Ravi.Kerur@telekom.com
Reid Price reid@nicira.com
+Rich Lane rlane@bigswitch.com
Rob Hoes rob.hoes@citrix.com
Romain Lenglet romain.lenglet@berabera.info
Sajjad Lateef slateef@nicira.com
Bryan Osoro bosoro@nicira.com
Cedric Hobbs cedric@nicira.com
Dave Walker DaveWalker@ubuntu.com
+David Palma palma@onesource.pt
Derek Cormier derek.cormier@lab.ntt.co.jp
Duffie Cooley dcooley@nicira.com
DK Moon dkmoon@nicira.com
A: The kernel module in upstream Linux 3.3 and later does not include
tunnel virtual ports, that is, interfaces with type "gre",
- "ipsec_gre", "gre64", "ipsec_gre64", "vxlan", or "capwap". It is
+ "ipsec_gre", "gre64", "ipsec_gre64", "vxlan", or "lisp". 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.
alternative, Open vSwitch supports mirroring to a GRE tunnel (see
above).
+Q: Why are there so many different ways to dump flows?
+
+A: Open vSwitch uses different kinds of flows for different purposes:
+
+ - OpenFlow flows are the most important kind of flow. OpenFlow
+ controllers use these flows to define a switch's policy.
+ OpenFlow flows support wildcards, priorities, and multiple
+ tables.
+
+ When in-band control is in use, Open vSwitch sets up a few
+ "hidden" flows, with priority higher than a controller or the
+ user can configure, that are not visible via OpenFlow. (See
+ the "Controller" section of the FAQ for more information
+ about hidden flows.)
+
+ - The Open vSwitch software switch implementation uses a second
+ kind of flow internally. These flows, called "exact-match"
+ or "datapath" or "kernel" flows, do not support wildcards or
+ priorities and comprise only a single table, which makes them
+ suitable for caching. OpenFlow flows and exact-match flows
+ also support different actions and number ports differently.
+
+ Exact-match flows are an implementation detail that is
+ subject to change in future versions of Open vSwitch. Even
+ with the current version of Open vSwitch, hardware switch
+ implementations do not necessarily use exact-match flows.
+
+ Each of the commands for dumping flows has a different purpose:
+
+ - "ovs-ofctl dump-flows <br>" dumps OpenFlow flows, excluding
+ hidden flows. This is the most commonly useful form of flow
+ dump. (Unlike the other commands, this should work with any
+ OpenFlow switch, not just Open vSwitch.)
+
+ - "ovs-appctl bridge/dump-flows <br>" dumps OpenFlow flows,
+ including hidden flows. This is occasionally useful for
+ troubleshooting suspected issues with in-band control.
+
+ - "ovs-dpctl dump-flows [dp]" dumps the exact-match flow table
+ entries for a Linux kernel-based datapath. In Open vSwitch
+ 1.10 and later, ovs-vswitchd merges multiple switches into a
+ single datapath, so it will show all the flows on all your
+ kernel-based switches. This command can occasionally be
+ useful for debugging.
+
+ - "ovs-appctl dpif/dump-flows <br>", new in Open vSwitch 1.10,
+ dumps exact-match flows for only the specified bridge,
+ regardless of the type.
+
Configuration Problems
----------------------
point, so the same problems will show up with the Linux bridge or
any other way to do bridging.
+Q: I can't seem to add my PPP interface to an Open vSwitch bridge.
+
+A: PPP most commonly carries IP packets, but Open vSwitch works only
+ with Ethernet frames. The correct way to interface PPP to an
+ Ethernet network is usually to use routing instead of switching.
+
Q: Is there any documentation on the database tables and fields?
A: Yes. ovs-vswitchd.conf.db(5) is a comprehensive reference.
ovs-vsctl -- --all destroy QoS -- --all destroy Queue
+ If you do want to keep some QoS or Queue records, or the Open
+ vSwitch you are using is older than version 1.8 (which added the
+ --all option), then you will have to destroy QoS and Queue records
+ individually.
+
Q: I configured Quality of Service (QoS) in my OpenFlow network by
adding records to the QoS and Queue table, but the results aren't
what I expect.
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.
+ controller. In situations where this is not suitable, you can
+ implement VLAN handling yourself, e.g.:
+
+ - If a packet comes in on an access port, and the flow table
+ needs to send it out on a trunk port, then the flow can add
+ the appropriate VLAN tag with the "mod_vlan_vid" action.
+
+ - If a packet comes in on a trunk port, and the flow table
+ needs to send it out on an access port, then the flow can
+ strip the VLAN tag with the "strip_vlan" action.
Q: I configured ports on a bridge as access ports with different VLAN
tags, like this:
controller is not configured, this happens implicitly to
every packet.)
- - The "autopath" Nicira extension action. However, "autopath"
- is deprecated and scheduled for removal in February 2013.
-
- Mirrors configured for output to a bonded port.
It would make a lot of sense for Open vSwitch to present a bond as
implementation of such a feature, please bring it up on the Open
vSwitch development mailing list at dev@openvswitch.org.
+Q: I have a sophisticated network setup involving Open vSwitch, VMs or
+ multiple hosts, and other components. The behavior isn't what I
+ expect. Help!
+
+A: To debug network behavior problems, trace the path of a packet,
+ hop-by-hop, from its origin in one host to a remote host. If
+ that's correct, then trace the path of the response packet back to
+ the origin.
+
+ Usually a simple ICMP echo request and reply ("ping") packet is
+ good enough. Start by initiating an ongoing "ping" from the origin
+ host to a remote host. If you are tracking down a connectivity
+ problem, the "ping" will not display any successful output, but
+ packets are still being sent. (In this case the packets being sent
+ are likely ARP rather than ICMP.)
+
+ Tools available for tracing include the following:
+
+ - "tcpdump" and "wireshark" for observing hops across network
+ devices, such as Open vSwitch internal devices and physical
+ wires.
+
+ - "ovs-appctl dpif/dump-flows <br>" in Open vSwitch 1.10 and
+ later or "ovs-dpctl dump-flows <br>" in earlier versions.
+ These tools allow one to observe the actions being taken on
+ packets in ongoing flows.
+
+ See ovs-vswitchd(8) for "ovs-appctl dpif/dump-flows"
+ documentation, ovs-dpctl(8) for "ovs-dpctl dump-flows"
+ documentation, and "Why are there so many different ways to
+ dump flows?" above for some background.
+
+ - "ovs-appctl ofproto/trace" to observe the logic behind how
+ ovs-vswitchd treats packets. See ovs-vswitchd(8) for
+ documentation. You can out more details about a given flow
+ that "ovs-dpctl dump-flows" displays, by cutting and pasting
+ a flow from the output into an "ovs-appctl ofproto/trace"
+ command.
+
+ - SPAN, RSPAN, and ERSPAN features of physical switches, to
+ observe what goes on at these physical hops.
+
+ Starting at the origin of a given packet, observe the packet at
+ each hop in turn. For example, in one plausible scenario, you
+ might:
+
+ 1. "tcpdump" the "eth" interface through which an ARP egresses
+ a VM, from inside the VM.
+
+ 2. "tcpdump" the "vif" or "tap" interface through which the ARP
+ ingresses the host machine.
+
+ 3. Use "ovs-dpctl dump-flows" to spot the ARP flow and observe
+ the host interface through which the ARP egresses the
+ physical machine. You may need to use "ovs-dpctl show" to
+ interpret the port numbers. If the output seems surprising,
+ you can use "ovs-appctl ofproto/trace" to observe details of
+ how ovs-vswitchd determined the actions in the "ovs-dpctl
+ dump-flows" output.
+
+ 4. "tcpdump" the "eth" interface through which the ARP egresses
+ the physical machine.
+
+ 5. "tcpdump" the "eth" interface through which the ARP
+ ingresses the physical machine, at the remote host that
+ receives the ARP.
+
+ 6. Use "ovs-dpctl dump-flows" to spot the ARP flow on the
+ remote host that receives the ARP and observe the VM "vif"
+ or "tap" interface to which the flow is directed. Again,
+ "ovs-dpctl show" and "ovs-appctl ofproto/trace" might help.
+
+ 7. "tcpdump" the "vif" or "tap" interface to which the ARP is
+ directed.
+
+ 8. "tcpdump" the "eth" interface through which the ARP
+ ingresses a VM, from inside the VM.
+
+ It is likely that during one of these steps you will figure out the
+ problem. If not, then follow the ARP reply back to the origin, in
+ reverse.
+
Contact
-------
OPENFLOW-1.1+ \
PORTING \
README-gcov \
+ README-lisp \
REPORTING-BUGS \
SubmittingPatches \
WHY-OVS \
-post-v1.9.0
---------------------
+post-v1.10.0
+---------------------
+ - Stable bond mode has been removed.
+ - The autopath action has been removed.
+ - CAPWAP tunneling support removed.
+ - New support for the data encapsulation format of the LISP tunnel
+ protocol (RFC 6830). An external control plane or manual flow
+ setup is required for EID-to-RLOC mapping.
+
+
+v1.10.0 - xx xxx xxxx
+---------------------
- Bridge compatibility support has been removed. Any uses that
rely on ovs-brcompatd will have to stick with Open vSwitch 1.9.x
or adapt to native Open vSwitch support (e.g. use ovs-vsctl instead
of brctl).
- The maximum size of the MAC learning table is now configurable.
- - New support for the VXLAN tunnel protocol (see the IETF draft here:
- http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02).
- With the Linux datapath, packets for new flows are now queued
separately on a per-port basis, so it should no longer be
possible for a large number of new flows arriving on one port to
prevent new flows from being processed on other ports.
- Many "ovs-vsctl" database commands now accept an --if-exists option.
Please refer to the ovs-vsctl manpage for details.
- - New "vlog/disable-rate-limit" and "vlog/enable-rate-limit" commands
- available through ovs-appctl allow control over logging rate limits.
- - The OpenFlow "dp_desc" may now be configured by setting the value of
- other-config:dp-desc in the Bridge table.
- - Path MTU discovery is no longer supported.
+ - OpenFlow:
+ - Experimental support for newer versions of OpenFlow. See
+ the "What versions of OpenFlow does Open vSwitch support?"
+ question in the FAQ for more details.
+ - The OpenFlow "dp_desc" may now be configured by setting the
+ value of other-config:dp-desc in the Bridge table.
+ - It is possible to request the OpenFlow port number with the
+ "ofport_request" column in the Interface table.
+ - Tunneling:
+ - New support for the VXLAN tunnel protocol (see the IETF draft here:
+ http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-03).
+ - Tunneling requires the version of the kernel module paired with
+ Open vSwitch 1.9.0 or later.
+ - Inheritance of the Don't Fragment bit in IP tunnels (df_inherit)
+ is no longer supported.
+ - Path MTU discovery is no longer supported.
+ - ovs-dpctl:
+ - The "dump-flows" and "del-flows" no longer require an argument
+ if only one datapath exists.
+ - ovs-appctl:
+ - New "vlog/disable-rate-limit" and "vlog/enable-rate-limit"
+ commands available allow control over logging rate limits.
+ - New "dpif/dump-dps", "dpif/show", and "dpif/dump-flows" command
+ that mimic the equivalent ovs-dpctl commands.
+ - The ofproto library is now responsible for assigning OpenFlow port
+ numbers. An ofproto implementation should assign them when
+ port_construct() is called.
+ - All dpif-based bridges of a particular type share a common
+ datapath called "ovs-<type>", e.g. "ovs-system". The ovs-dpctl
+ commands will now return information on that shared datapath. To
+ get the equivalent bridge-specific information, use the new
+ "ovs-appctl dpif/*" commands.
- Backward-incompatible changes:
- Earlier Open vSwitch versions treated ANY as a wildcard in flow
syntax. OpenFlow 1.1 adds a port named ANY, which introduces a
conflict. ANY was rarely used in flow syntax, so we chose to
retire that meaning of ANY in favor of the OpenFlow 1.1 meaning.
- - Inheritance of the Don't Fragment bit in IP tunnels (df_inherit) is
- no longer supported.
- - Patch ports are implemented in userspace.
- - Tunneling requires the version of the kernel module paired with Open
- vSwitch 1.9.0 or later.
+ - Patch ports no longer require kernel support, so they now work
+ with FreeBSD and the kernel module built into Linux 3.3 and later.
v1.9.0 - xx xxx xxxx
--------------------
- - The tunneling code no longer assumes input and output keys are symmetric.
- If they are not, PMTUD needs to be disabled for tunneling to work. Note
- this only applies to flow-based keys.
- Datapath:
- Support for ipv6 set action.
- SKB mark matching and setting.
- Allow bitwise masking for SHA and THA fields in ARP, SLL and TLL
fields in IPv6 neighbor discovery messages, and IPv6 flow label.
- Adds support for writing to the metadata field for a flow.
- - It is possible to request the OpenFlow port number with the
- "ofport_request" column in the Interface table.
+ - Tunneling:
+ - The tunneling code no longer assumes input and output keys are
+ symmetric. If they are not, PMTUD needs to be disabled for
+ tunneling to work. Note this only applies to flow-based keys.
+ - New support for a nonstandard form of GRE that supports a 64-bit key.
+ - Tunnel Path MTU Discovery default value was set to 'disabled'.
+ This feature is deprecated and will be removed soon.
+ - Tunnel header caching removed.
- ovs-ofctl:
- Commands and actions that accept port numbers now also accept keywords
that represent those ports (such as LOCAL, NONE, and ALL). This is
- ovs-dpctl:
- Support requesting the port number with the "port_no" option in
the "add-if" command.
- - The "dump-flows" and "del-flows" no longer require an argument
- if only one datapath exists.
- - ovs-appctl:
- - New "dpif/dump-dps", "dpif/show", and "dpif/dump-flows" command
- that mimic the equivalent ovs-dpctl commands.
- ovs-pki: The "online PKI" features have been removed, along with
the ovs-pki-cgi program that facilitated it, because of some
alarmist insecurity claims. We do not believe that these claims
are true, but because we do not know of any users for this
feature it seems better on balance to remove it. (The ovs-pki-cgi
program was not included in distribution packaging.)
- - Tunnel Path MTU Discovery default value was set to 'disabled'. This
- feature is deprecated and will be removed soon.
- ovsdb-server now enforces the immutability of immutable columns. This
was not enforced in earlier versions due to an oversight.
- - New support for a nonstandard form of GRE that supports a 64-bit key.
- - The ofproto library is now responsible for assigning OpenFlow port
- numbers. An ofproto implementation should assign them when
- port_construct() is called.
- - All dpif-based bridges of a particular type share a common
- datapath called "ovs-<type>", e.g. "ovs-system". The ovs-dpctl
- commands will now return information on that shared datapath. To
- get the equivalent bridge-specific information, use the new
- "ovs-appctl dpif/*" commands.
- - Tunnel header caching removed.
- The following features are now deprecated. They will be removed no
earlier than February 2013. Please email dev@openvswitch.org with
concerns.
specified. Check that OVS implements the new behavior, fix it
if not.
- * On OF1.1+ flow_mods, DELETE now ignores buffer_id.
-
- * OFPST_PORT and OFPST_QUEUE stats. These differ little from
- OF1.0 to OF1.1 (just the size of the port number field) but do
- require abstraction (Done?).
-
* OFPT_TABLE_MOD stats. This is new in OF1.1, so we need to
implement it. It should be implemented so that the default OVS
behavior does not change.
* Document how OVS does packet buffering.
* MPLS. Simon Horman maintains a patch series that adds this
- feature. It needs review and possible revision before it is
- merged.
+ feature. This is partially merged.
* SCTP. Joe Stringer maintains a patch series that adds this
feature. It has received review comments that need to be
OpenFlow 1.2 support requires OpenFlow 1.1 as a prerequisite, plus the
following additional work. (This is based on the change log at the
-end of the OF1.2 spec. I didn’t compare the specs carefully yet.)
+end of the OF1.2 spec. I didn't compare the specs carefully yet.)
* Use new OpenFlow extensible error infrastructure, on OF1.2+
only, instead of the OVS-specific extension used until now.
* Update DESIGN to describe OF1.2 behavior also.
- * Implement OFPT_ROLE_REQUEST. Patch submitted by Jarno
- Rajahalme, currently under revision.
-
* Add ability to turn off packet buffering with OFPCML_NO_BUFFER.
OpenFlow 1.3
OpenFlow 1.3 support requires OpenFlow 1.2 as a prerequisite, plus the
following additional work. (This is based on the change log at the
end of the OF1.3 spec, reusing most of the section titles directly. I
-didn’t compare the specs carefully yet.)
+didn't compare the specs carefully yet.)
* Add support for multipart requests.
and design requirements. Might be politically difficult to add
directly to the kernel module, since its functionality overlaps
with tc. Ideally, therefore, we could implement these somehow
- with tc, but I haven’t investigated whether that makes sense.
+ with tc, but I haven't investigated whether that makes sense.
- * Per-connection event filtering. OF1.3 adopted Open vSwitch’s
+ * Per-connection event filtering. OF1.3 adopted Open vSwitch's
existing design for this feature so implementation should be
easy.
* Auxiliary connections. These are optional, so a minimal
implementation would not need them. An implementation in
- generic code might be a week’s worth of work. The value of an
+ generic code might be a week's worth of work. The value of an
implementation in generic code is questionable, though, since
much of the benefit of axuiliary connections is supposed to be
to take advantage of hardware support. (We could make the
kernel module somehow send packets across the auxiliary
- connections directly, for some kind of “hardware” support, if we
+ connections directly, for some kind of "hardware" support, if we
judged it useful enough.)
* MPLS BoS matching. (Included in Simon's MPLS series?)
- * Provider Backbone Bridge tagging. I don’t plan to implement
- this (but we’d accept an implementation).
+ * Provider Backbone Bridge tagging. I don't plan to implement
+ this (but we'd accept an implementation).
- * Rework tag order. I’m not sure whether we need to do anything
+ * Rework tag order. I'm not sure whether we need to do anything
for this.
* Duration for stats.
tree).
* The patch submission guidelines (see SubmittingPatches). I
- recommend using “git send-email”, which automatically follows a
+ recommend using "git send-email", which automatically follows a
lot of those guidelines.
Bug Reporting
* NIC bonding with or without LACP on upstream switch
* NetFlow, sFlow(R), and mirroring for increased visibility
* QoS (Quality of Service) configuration, plus policing
- * GRE, GRE over IPSEC, CAPWAP, and VXLAN tunneling
+ * GRE, GRE over IPSEC, VXLAN, and LISP tunneling
* 802.1ag connectivity fault management
* OpenFlow 1.0 plus numerous extensions
* Transactional configuration database with C and Python bindings
--- /dev/null
+Using LISP tunneling
+====================
+
+LISP is a layer 3 tunneling mechanism, meaning that encapsulated packets do
+not carry Ethernet headers, and ARP requests shouldn't be sent over the
+tunnel. Because of this, there are some additional steps required for setting
+up LISP tunnels in Open vSwitch, until support for L3 tunnels will improve.
+
+This guide assumes a point-to-point tunnel between two VMs connected to OVS
+bridges on different hypervisors connected via IPv4. Of course, more than one
+VM may be connected to any of the hypervisors, using the same LISP tunnel, and
+a hypervisor may be connected to several hypervisors over different LISP
+tunnels.
+
+There are several scenarios:
+
+ 1) the VMs have IP addresses in the same subnet and the hypervisors are also
+ in a single subnet (although one different from the VM's);
+ 2) the VMs have IP addresses in the same subnet but the hypervisors are
+ separated by a router;
+ 3) the VMs are in different subnets.
+
+In cases 1) and 3) ARP resolution can work as normal: ARP traffic is
+configured not to go through the LISP tunnel. For case 1) ARP is able to
+reach the other VM, if both OVS instances default to MAC address learning.
+Case 3) requires the hypervisor be configured as the default router for the
+VMs.
+
+In case 2) the VMs expect ARP replies from each other, but this is not
+possible over a layer 3 tunnel. One solution is to have static MAC address
+entries preconfigured on the VMs (e.g., `arp -f /etc/ethers` on startup on
+Unix based VMs), or have the hypervisor do proxy ARP.
+
+On the receiving side, the packet arrives without the original MAC header.
+The LISP tunneling code attaches a header with harcoded source and destination
+MAC addres 02:00:00:00:00:00. This address has all bits set to 0, except the
+locally administered bit, in order to avoid potential collisions with existing
+allocations. In order for packets to reach their intended destination, the
+destination MAC address needs to be rewritten. This can be done using the
+flow table.
+
+See below for an example setup, and the associated flow rules to enable LISP
+tunneling.
+
+ +---+ +---+
+ |VM1| |VM2|
+ +---+ +---+
+ | |
+ +--[tap0]--+ +--[tap0]---+
+ | | | |
+ [lisp0] OVS1 [eth0]-----------------[eth0] OVS2 [lisp0]
+ | | | |
+ +----------+ +-----------+
+
+On each hypervisor, interfaces tap0, eth0, and lisp0 are added to a single
+bridge instance, and become numbered 1, 2, and 3 respectively:
+
+ ovs-vsctl add-br br0
+ ovs-vsctl add-port br0 tap0
+ ovs-vsctl add-port br0 eth0
+ ovs-vsctl add-port br0 lisp0 -- set Interface lisp0 type=lisp options:remote_ip=<OVSx_IP>
+
+Flows on br0 are configured as follows:
+
+ priority=3,dl_dst=02:00:00:00:00:00,action=mod_dl_dst:<VMx_MAC>,output:1
+ priority=2,in_port=1,dl_type=0x0806,action=NORMAL
+ priority=1,in_port=1,dl_type=0x0800,vlan_tci=0,nw_src=<EID_prefix>,action=output:3
+ priority=0,action=NORMAL
# limitations under the License.
AC_PREREQ(2.64)
-AC_INIT(openvswitch, 1.9.90, ovs-bugs@openvswitch.org)
+AC_INIT(openvswitch, 1.10.90, ovs-bugs@openvswitch.org)
AC_CONFIG_SRCDIR([datapath/datapath.c])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([build-aux])
+++ /dev/null
-
-References:
-* http://www.rfc-editor.org/rfc/rfc5415.txt
-
-
-The CAPWAP header layout is summarized as follows:
-
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- |CAPWAP Preamble| HLEN | RID | WBID |T|F|L|W|M|K|Flags|
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Fragment ID | Frag Offset |Rsvd |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | (optional) Radio MAC Address |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | (optional) Wireless Specific Information |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Payload .... |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-The spec defines an optional Wireless Specific Information field which can be
-used to pass arbitrary data in the encapsulation layer:
-
- Wireless Specific Information: This optional field may be used to carry
- per-packet information. This field is only present if the
- 'W' bit is set. The WBID field in the CAPWAP Header is used to
- identify the format of the WSI optional field. The HLEN field assumes
- 4-byte alignment, and this field MUST be padded with zeroes (0x00) if it
- is not 4-byte aligned.
-
- The Wireless-Specific Information field uses the following format:
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Length | Data...
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- Length: The 8-bit field contains the length of the data field,
- with a maximum size of 255.
-
- Data: Wireless-specific information, defined by the wireless-
- specific binding specified in the CAPWAP Header's WBID field.
-
-
- WBID: A 5-bit field that is the wireless binding identifier. The
- identifier will indicate the type of wireless packet associated
- with the radio. The following values are defined:
-
- 0 - Reserved
- 1 - IEEE 802.11
- 2 - Reserved
- 3 - EPCGlobal [EPCGlobal]
-
- When Open vSwitch uses this field, it writes the value:
- 30 - Open vSwitch data
-
-
-Open vSwitch can make use of this field to pass additional packet routing
-information. When needed, it sets the 'W' bit to indicates the WSI field is
-added, and fills the field as follows:
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | WSI_LEN |K| Flags | Reserved |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | (optional) 64bit Tunnel Key |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- K - flag bit to identify presence of a 64bit tunnel key.
-
-
-Adding WSI fields: Fields must be written and read in consitent order. New
-fields may be added, but the existing fields always come first.
tunnel.c \
vlan.c \
vport.c \
- vport-capwap.c \
vport-gre.c \
vport-internal_dev.c \
+ vport-lisp.c \
vport-netdev.c \
vport-vxlan.c
tunnel.h \
vlan.h \
vport.h \
- vport-capwap.h \
vport-internal_dev.h \
vport-netdev.h
openvswitch_extras = \
- README \
- CAPWAP.txt
+ README
dist_sources = $(foreach module,$(dist_modules),$($(module)_sources))
dist_headers = $(foreach module,$(dist_modules),$($(module)_headers))
if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
skb->csum = csum_sub(skb->csum, csum_partial(skb->data
- + ETH_HLEN, VLAN_HLEN, 0));
+ + (2 * ETH_ALEN), VLAN_HLEN, 0));
vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);
*current_tci = vhdr->h_vlan_TCI;
if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
skb->csum = csum_add(skb->csum, csum_partial(skb->data
- + ETH_HLEN, VLAN_HLEN, 0));
+ + (2 * ETH_ALEN), VLAN_HLEN, 0));
}
__vlan_hwaccel_put_tag(skb, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
struct sk_buff *segs, *nskb;
int err;
- segs = skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM);
+ segs = __skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM, false);
if (IS_ERR(segs))
return PTR_ERR(segs);
len = sizeof(struct ovs_header);
len += nla_total_size(skb->len);
len += nla_total_size(FLOW_BUFSIZE);
- if (upcall_info->cmd == OVS_PACKET_CMD_ACTION)
- len += nla_total_size(8);
+ if (upcall_info->userdata)
+ len += NLA_ALIGN(upcall_info->userdata->nla_len);
user_skb = genlmsg_new(len, GFP_ATOMIC);
if (!user_skb) {
nla_nest_end(user_skb, nla);
if (upcall_info->userdata)
- nla_put_u64(user_skb, OVS_PACKET_ATTR_USERDATA,
- nla_get_u64(upcall_info->userdata));
+ __nla_put(user_skb, OVS_PACKET_ATTR_USERDATA,
+ nla_len(upcall_info->userdata),
+ nla_data(upcall_info->userdata));
nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len);
skb_copy_and_csum_dev(skb, nla_data(nla));
+ genlmsg_end(user_skb, upcall);
err = genlmsg_unicast(net, user_skb, upcall_info->portid);
out:
int next_offset = offsetof(struct sw_flow_actions, actions) +
(*sfa)->actions_len;
- if (req_size <= (ksize(*sfa) - next_offset))
+ if (req_size <= ((*sfa)->buf_size - next_offset))
goto out;
- new_acts_size = ksize(*sfa) * 2;
+ new_acts_size = (*sfa)->buf_size * 2;
if (new_acts_size > MAX_ACTIONS_BUFSIZE) {
if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size)
memcpy(acts->actions, (*sfa)->actions, (*sfa)->actions_len);
acts->actions_len = (*sfa)->actions_len;
- kfree(*sfa);
+ ovs_flow_actions_free(*sfa);
*sfa = acts;
out:
{
static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] = {
[OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 },
- [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_U64 },
+ [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC },
};
struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1];
int error;
return 0;
err_kfree:
- kfree(acts);
+ ovs_flow_actions_free(acts);
error:
return error;
}
if (IS_ERR(vport))
goto exit_unlock;
+ err = 0;
if (a[OVS_VPORT_ATTR_STATS])
ovs_vport_set_stats(vport, nla_data(a[OVS_VPORT_ATTR_STATS]));
if (IS_ERR(reply))
goto exit_unlock;
+ err = 0;
ovs_dp_detach_port(vport);
genl_notify(reply, genl_info_net(info), info->snd_portid,
* struct dp_upcall - metadata to include with a packet to send to userspace
* @cmd: One of %OVS_PACKET_CMD_*.
* @key: Becomes %OVS_PACKET_ATTR_KEY. Must be nonnull.
- * @userdata: If nonnull, its u64 value is extracted and passed to userspace as
+ * @userdata: If nonnull, its variable-length value is passed to userspace as
* %OVS_PACKET_ATTR_USERDATA.
* @portid: Netlink PID to which packet should be sent. If @portid is 0 then no
* packet is sent and the packet is accounted in the datapath's @n_lost
*/
struct ovs_net {
struct list_head dps;
- struct vport_net vport_net;
};
extern int ovs_net_id;
if (size > MAX_ACTIONS_BUFSIZE)
return ERR_PTR(-EINVAL);
- sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);
+ size += sizeof(*sfa);
+ if (size <= MAX_ACTIONS_BUFSIZE_KMALLOC)
+ sfa = kmalloc(size, GFP_KERNEL);
+ else
+ sfa = vmalloc(size);
+
if (!sfa)
return ERR_PTR(-ENOMEM);
sfa->actions_len = 0;
+ sfa->buf_size = size;
+
return sfa;
}
+void ovs_flow_actions_free(struct sw_flow_actions *sfa)
+{
+ if (sfa->buf_size <= MAX_ACTIONS_BUFSIZE_KMALLOC)
+ kfree(sfa);
+ else
+ vfree(sfa);
+}
+
struct sw_flow *ovs_flow_alloc(void)
{
struct sw_flow *flow;
{
struct sw_flow_actions *sf_acts = container_of(rcu,
struct sw_flow_actions, rcu);
- kfree(sf_acts);
+ ovs_flow_actions_free(sf_acts);
}
/* Schedules 'sf_acts' to be freed after the next RCU grace period.
return htons(ETH_P_802_2);
__skb_pull(skb, sizeof(struct llc_snap_hdr));
- return llc->ethertype;
+
+ if (ntohs(llc->ethertype) >= 1536)
+ return llc->ethertype;
+
+ return htons(ETH_P_802_2);
}
static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
struct sw_flow_actions {
struct rcu_head rcu;
u32 actions_len;
+ int buf_size;
struct nlattr actions[];
};
void ovs_flow_free(struct sw_flow *);
struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len);
+void ovs_flow_actions_free(struct sw_flow_actions *sfa);
void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len,
const struct nlattr *attr);
-#define MAX_ACTIONS_BUFSIZE (16 * 1024)
+#define MAX_ACTIONS_BUFSIZE (32 * 1024)
+#define MAX_ACTIONS_BUFSIZE_KMALLOC PAGE_SIZE
#define TBL_MIN_BUCKETS 1024
struct flow_table {
/tmp
/tunnel.c
/vlan.c
-/vport-capwap.c
/vport-generic.c
/vport-gre.c
/vport-internal_dev.c
+/vport-lisp.c
/vport-netdev.c
/vport-patch.c
/vport-vxlan.c
}
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
+typedef u32 netdev_features_t;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
+static inline struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
+ netdev_features_t features,
+ bool tx_path)
+{
+ return skb_gso_segment(skb, features);
+}
+#endif
+
#endif
#include <net/xfrm.h>
#include "checksum.h"
+#include "compat.h"
#include "datapath.h"
#include "tunnel.h"
#include "vlan.h"
static struct rtable *find_route(struct net *net,
__be32 *saddr, __be32 daddr, u8 ipproto,
- u8 tos)
+ u8 tos, u32 skb_mark)
{
struct rtable *rt;
/* Tunnel configuration keeps DSCP part of TOS bits, But Linux
struct flowi fl = { .nl_u = { .ip4_u = {
.daddr = daddr,
.saddr = *saddr,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ .fwmark = skb_mark,
+#endif
.tos = RT_TOS(tos) } },
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ .mark = skb_mark,
+#endif
.proto = ipproto };
if (unlikely(ip_route_output_key(net, &rt, &fl)))
struct flowi4 fl = { .daddr = daddr,
.saddr = *saddr,
.flowi4_tos = RT_TOS(tos),
+ .flowi4_mark = skb_mark,
.flowi4_proto = ipproto };
rt = ip_route_output_key(net, &fl);
if (skb_is_gso(skb)) {
struct sk_buff *nskb;
- nskb = skb_gso_segment(skb, 0);
+ nskb = __skb_gso_segment(skb, 0, false);
if (IS_ERR(nskb)) {
kfree_skb(skb);
err = PTR_ERR(nskb);
return sent_len;
}
+/* Compute source UDP port for outgoing packet.
+ * Currently we use the flow hash.
+ */
+u16 ovs_tnl_get_src_port(struct sk_buff *skb)
+{
+ int low;
+ int high;
+ unsigned int range;
+ u32 hash = OVS_CB(skb)->flow->hash;
+
+ inet_get_local_port_range(&low, &high);
+ range = (high - low) + 1;
+ return (((u64) hash * range) >> 32) + low;
+}
+
int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
{
struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
__be16 frag_off;
__be32 daddr;
__be32 saddr;
+ u32 skb_mark;
u8 ttl;
u8 tos;
}
/* Route lookup */
+ skb_mark = skb_get_mark(skb);
rt = find_route(port_key_get_net(&mutable->key), &saddr, daddr,
- tnl_vport->tnl_ops->ipproto, tos);
+ tnl_vport->tnl_ops->ipproto, tos, skb_mark);
if (IS_ERR(rt))
goto error_free;
rt = find_route(port_key_get_net(&mutable->key),
&saddr, mutable->key.daddr,
- tnl_ops->ipproto, mutable->tos);
+ tnl_ops->ipproto, mutable->tos, 0);
if (IS_ERR(rt))
return -EADDRNOTAVAIL;
dev = rt_dst(rt).dev;
struct vport *vport;
struct tnl_vport *tnl_vport;
struct tnl_mutable_config *mutable;
- int initial_frag_id;
int err;
vport = ovs_vport_alloc(sizeof(struct tnl_vport), vport_ops, parms);
goto error_free_vport;
}
- get_random_bytes(&initial_frag_id, sizeof(int));
- atomic_set(&tnl_vport->frag_id, initial_frag_id);
-
err = tnl_set_config(ovs_dp_get_net(parms->dp), parms->options, tnl_ops,
NULL, mutable);
if (err)
*/
#define TNL_T_PROTO_GRE 0
#define TNL_T_PROTO_GRE64 1
-#define TNL_T_PROTO_CAPWAP 2
#define TNL_T_PROTO_VXLAN 3
+#define TNL_T_PROTO_LISP 4
/* These flags are only needed when calling tnl_find_port(). */
#define TNL_T_KEY_EXACT (1 << 10)
/* All public tunnel flags. */
#define TNL_F_PUBLIC (TNL_F_CSUM | TNL_F_TOS_INHERIT | TNL_F_TTL_INHERIT | \
- TNL_F_DF_DEFAULT | TNL_F_IPSEC)
+ TNL_F_DF_DEFAULT)
/**
* struct port_lookup_key - Tunnel port key, used as hash table key.
const struct tnl_ops *tnl_ops;
struct tnl_mutable_config __rcu *mutable;
-
- /*
- * ID of last fragment sent (for tunnel protocols with direct support
- * fragmentation). If the protocol relies on IP fragmentation then
- * this is not needed.
- */
- atomic_t frag_id;
};
struct vport *ovs_tnl_create(const struct vport_parms *, const struct vport_ops *,
const char *ovs_tnl_get_name(const struct vport *vport);
int ovs_tnl_send(struct vport *vport, struct sk_buff *skb);
void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb);
+u16 ovs_tnl_get_src_port(struct sk_buff *skb);
struct vport *ovs_tnl_find_port(struct net *net, __be32 saddr, __be32 daddr,
__be64 key, int tunnel_type,
+++ /dev/null
-/*
- * 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
- * kernel, by Linus Torvalds and others.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/version.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
-
-#include <linux/if.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/list.h>
-#include <linux/net.h>
-#include <net/net_namespace.h>
-
-#include <net/icmp.h>
-#include <net/inet_frag.h>
-#include <net/ip.h>
-#include <net/protocol.h>
-#include <net/udp.h>
-
-#include "datapath.h"
-#include "tunnel.h"
-#include "vport.h"
-
-#define CAPWAP_SRC_PORT 58881
-#define CAPWAP_DST_PORT 58882
-
-#define CAPWAP_FRAG_TIMEOUT (30 * HZ)
-#define CAPWAP_FRAG_MAX_MEM (256 * 1024)
-#define CAPWAP_FRAG_PRUNE_MEM (192 * 1024)
-#define CAPWAP_FRAG_SECRET_INTERVAL (10 * 60 * HZ)
-
-/*
- * The CAPWAP header is a mess, with all kinds of odd size bit fields that
- * cross byte boundaries, which are difficult to represent correctly in
- * various byte orderings. Luckily we only care about a few permutations, so
- * statically create them and we can do very fast parsing by checking all 12
- * fields in one go.
- */
-#define CAPWAP_PREAMBLE_MASK __cpu_to_be32(0xFF000000)
-#define CAPWAP_HLEN_SHIFT 17
-#define CAPWAP_HLEN_MASK __cpu_to_be32(0x00F80000)
-#define CAPWAP_RID_MASK __cpu_to_be32(0x0007C000)
-#define CAPWAP_WBID_MASK __cpu_to_be32(0x00003E00)
-#define CAPWAP_F_MASK __cpu_to_be32(0x000001FF)
-
-#define CAPWAP_F_FRAG __cpu_to_be32(0x00000080)
-#define CAPWAP_F_LASTFRAG __cpu_to_be32(0x00000040)
-#define CAPWAP_F_WSI __cpu_to_be32(0x00000020)
-#define CAPWAP_F_RMAC __cpu_to_be32(0x00000010)
-
-#define CAPWAP_RMAC_LEN 4
-
-/* Standard CAPWAP looks for a WBID value of 2.
- * When we insert WSI field, use WBID value of 30, which has been
- * proposed for all "experimental" usage - users with no reserved WBID value
- * of their own.
-*/
-#define CAPWAP_WBID_30 __cpu_to_be32(0x00003C00)
-#define CAPWAP_WBID_2 __cpu_to_be32(0x00000200)
-
-#define FRAG_HDR (CAPWAP_F_FRAG)
-#define FRAG_LAST_HDR (FRAG_HDR | CAPWAP_F_LASTFRAG)
-
-/* Keyed packet, WBID 30, and length long enough to include WSI key */
-#define CAPWAP_KEYED (CAPWAP_WBID_30 | CAPWAP_F_WSI | htonl(20 << CAPWAP_HLEN_SHIFT))
-/* A backward-compatible packet, WBID 2 and length of 2 words (no WSI fields) */
-#define CAPWAP_NO_WSI (CAPWAP_WBID_2 | htonl(8 << CAPWAP_HLEN_SHIFT))
-
-/* Mask for all parts of header that must be 0. */
-#define CAPWAP_ZERO_MASK (CAPWAP_PREAMBLE_MASK | \
- (CAPWAP_F_MASK ^ (CAPWAP_F_WSI | CAPWAP_F_FRAG | CAPWAP_F_LASTFRAG | CAPWAP_F_RMAC)))
-
-struct capwaphdr {
- __be32 begin;
- __be16 frag_id;
- /* low 3 bits of frag_off are reserved */
- __be16 frag_off;
-};
-
-/*
- * We use the WSI field to hold additional tunnel data.
- * The first eight bits store the size of the wsi data in bytes.
- */
-struct capwaphdr_wsi {
- u8 wsi_len;
- u8 flags;
- __be16 reserved_padding;
-};
-
-struct capwaphdr_wsi_key {
- __be64 key;
-};
-
-/* Flag indicating a 64bit key is stored in WSI data field */
-#define CAPWAP_WSI_F_KEY64 0x80
-
-static struct capwaphdr *capwap_hdr(const struct sk_buff *skb)
-{
- return (struct capwaphdr *)(udp_hdr(skb) + 1);
-}
-
-/*
- * The fragment offset is actually the high 13 bits of the last 16 bit field,
- * so we would normally need to right shift 3 places. However, it stores the
- * offset in 8 byte chunks, which would involve a 3 place left shift. So we
- * just mask off the last 3 bits and be done with it.
- */
-#define FRAG_OFF_MASK (~0x7U)
-
-/*
- * The minimum header length. The header may be longer if the optional
- * WSI field is used.
- */
-#define CAPWAP_MIN_HLEN (sizeof(struct udphdr) + sizeof(struct capwaphdr))
-
-struct frag_match {
- __be32 saddr;
- __be32 daddr;
- __be16 id;
-};
-
-struct frag_queue {
- struct inet_frag_queue ifq;
- struct frag_match match;
-};
-
-struct frag_skb_cb {
- u16 offset;
-};
-#define FRAG_CB(skb) ((struct frag_skb_cb *)(skb)->cb)
-
-static struct sk_buff *fragment(struct sk_buff *, const struct vport *,
- struct dst_entry *dst, unsigned int hlen);
-static struct sk_buff *defrag(struct sk_buff *, bool frag_last);
-
-static void capwap_frag_init(struct inet_frag_queue *, void *match);
-static unsigned int capwap_frag_hash(struct inet_frag_queue *);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
-static int capwap_frag_match(struct inet_frag_queue *, void *match);
-#else
-static bool capwap_frag_match(struct inet_frag_queue *, void *match);
-#endif
-static void capwap_frag_expire(unsigned long ifq);
-
-static struct inet_frags frag_state = {
- .constructor = capwap_frag_init,
- .qsize = sizeof(struct frag_queue),
- .hashfn = capwap_frag_hash,
- .match = capwap_frag_match,
- .frag_expire = capwap_frag_expire,
- .secret_interval = CAPWAP_FRAG_SECRET_INTERVAL,
-};
-
-static int capwap_hdr_len(const struct tnl_mutable_config *mutable,
- const struct ovs_key_ipv4_tunnel *tun_key)
-{
- int size = CAPWAP_MIN_HLEN;
- u32 flags;
- __be64 out_key;
-
- tnl_get_param(mutable, tun_key, &flags, &out_key);
-
- /* CAPWAP has no checksums. */
- if (flags & TNL_F_CSUM)
- return -EINVAL;
-
- /* if keys are specified, then add WSI field */
- if (out_key || (flags & TNL_F_OUT_KEY_ACTION)) {
- size += sizeof(struct capwaphdr_wsi) +
- sizeof(struct capwaphdr_wsi_key);
- }
-
- return size;
-}
-
-static struct sk_buff *capwap_build_header(const struct vport *vport,
- const struct tnl_mutable_config *mutable,
- struct dst_entry *dst,
- struct sk_buff *skb,
- int tunnel_hlen)
-{
- struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
- struct udphdr *udph = udp_hdr(skb);
- struct capwaphdr *cwh = (struct capwaphdr *)(udph + 1);
- u32 flags;
- __be64 out_key;
-
- tnl_get_param(mutable, tun_key, &flags, &out_key);
-
- udph->source = htons(CAPWAP_SRC_PORT);
- udph->dest = htons(CAPWAP_DST_PORT);
- udph->check = 0;
-
- cwh->frag_id = 0;
- cwh->frag_off = 0;
-
- if (out_key || flags & TNL_F_OUT_KEY_ACTION) {
- /* first field in WSI is key */
- struct capwaphdr_wsi *wsi = (struct capwaphdr_wsi *)(cwh + 1);
-
- cwh->begin = CAPWAP_KEYED;
-
- /* -1 for wsi_len byte, not included in length as per spec */
- wsi->wsi_len = sizeof(struct capwaphdr_wsi) - 1
- + sizeof(struct capwaphdr_wsi_key);
- wsi->flags = CAPWAP_WSI_F_KEY64;
- wsi->reserved_padding = 0;
-
- if (out_key) {
- struct capwaphdr_wsi_key *opt = (struct capwaphdr_wsi_key *)(wsi + 1);
- opt->key = out_key;
- }
- } else {
- /* make packet readable by old capwap code */
- cwh->begin = CAPWAP_NO_WSI;
- }
- udph->len = htons(skb->len - skb_transport_offset(skb));
-
- if (unlikely(skb->len - skb_network_offset(skb) > dst_mtu(dst))) {
- unsigned int hlen = skb_transport_offset(skb) + capwap_hdr_len(mutable, tun_key);
- skb = fragment(skb, vport, dst, hlen);
- }
-
- return skb;
-}
-
-static int process_capwap_wsi(struct sk_buff *skb, __be64 *key, bool *key_present)
-{
- struct capwaphdr *cwh = capwap_hdr(skb);
- struct capwaphdr_wsi *wsi;
- int hdr_len;
- int rmac_len = 0;
- int wsi_len;
-
- if (((cwh->begin & CAPWAP_WBID_MASK) != CAPWAP_WBID_30))
- return 0;
-
- if (cwh->begin & CAPWAP_F_RMAC)
- rmac_len = CAPWAP_RMAC_LEN;
-
- hdr_len = ntohl(cwh->begin & CAPWAP_HLEN_MASK) >> CAPWAP_HLEN_SHIFT;
-
- if (unlikely(sizeof(struct capwaphdr) + rmac_len + sizeof(struct capwaphdr_wsi) > hdr_len))
- return -EINVAL;
-
- /* read wsi header to find out how big it really is */
- wsi = (struct capwaphdr_wsi *)((u8 *)(cwh + 1) + rmac_len);
- /* +1 for length byte not included in wsi_len */
- wsi_len = 1 + wsi->wsi_len;
-
- if (unlikely(sizeof(struct capwaphdr) + rmac_len + wsi_len != hdr_len))
- return -EINVAL;
-
- wsi_len -= sizeof(struct capwaphdr_wsi);
-
- if (wsi->flags & CAPWAP_WSI_F_KEY64) {
- struct capwaphdr_wsi_key *opt;
-
- if (unlikely(wsi_len < sizeof(struct capwaphdr_wsi_key)))
- return -EINVAL;
-
- opt = (struct capwaphdr_wsi_key *)(wsi + 1);
- *key = opt->key;
- *key_present = true;
- } else {
- *key_present = false;
- }
-
- return 0;
-}
-
-static struct sk_buff *process_capwap_proto(struct sk_buff *skb, __be64 *key, bool *key_present)
-{
- struct capwaphdr *cwh = capwap_hdr(skb);
- int hdr_len = sizeof(struct udphdr);
-
- if (unlikely((cwh->begin & CAPWAP_ZERO_MASK) != 0))
- goto error;
-
- hdr_len += ntohl(cwh->begin & CAPWAP_HLEN_MASK) >> CAPWAP_HLEN_SHIFT;
- if (unlikely(hdr_len < CAPWAP_MIN_HLEN))
- goto error;
-
- if (unlikely(!pskb_may_pull(skb, hdr_len + ETH_HLEN)))
- goto error;
-
- cwh = capwap_hdr(skb);
- __skb_pull(skb, hdr_len);
- skb_postpull_rcsum(skb, skb_transport_header(skb), hdr_len + ETH_HLEN);
-
- if (cwh->begin & CAPWAP_F_FRAG) {
- skb = defrag(skb, (__force bool)(cwh->begin & CAPWAP_F_LASTFRAG));
- if (!skb)
- return NULL;
- cwh = capwap_hdr(skb);
- }
-
- if ((cwh->begin & CAPWAP_F_WSI) && process_capwap_wsi(skb, key, key_present))
- goto error;
-
- return skb;
-error:
- kfree_skb(skb);
- return NULL;
-}
-
-/* Called with rcu_read_lock and BH disabled. */
-static int capwap_rcv(struct sock *sk, struct sk_buff *skb)
-{
- struct vport *vport;
- const struct tnl_mutable_config *mutable;
- struct iphdr *iph;
- struct ovs_key_ipv4_tunnel tun_key;
- __be64 key = 0;
- bool key_present = false;
-
- if (unlikely(!pskb_may_pull(skb, CAPWAP_MIN_HLEN + ETH_HLEN)))
- goto error;
-
- skb = process_capwap_proto(skb, &key, &key_present);
- if (unlikely(!skb))
- goto out;
-
- iph = ip_hdr(skb);
- vport = ovs_tnl_find_port(sock_net(sk), iph->daddr, iph->saddr, key,
- TNL_T_PROTO_CAPWAP, &mutable);
- if (unlikely(!vport)) {
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
- goto error;
- }
-
- if (key_present && mutable->key.daddr &&
- !(mutable->flags & TNL_F_IN_KEY_MATCH)) {
- key_present = false;
- key = 0;
- }
-
- tnl_tun_key_init(&tun_key, iph, key, key_present ? OVS_TNL_F_KEY : 0);
- OVS_CB(skb)->tun_key = &tun_key;
-
- ovs_tnl_rcv(vport, skb);
- goto out;
-
-error:
- kfree_skb(skb);
-out:
- return 0;
-}
-
-static const struct tnl_ops capwap_tnl_ops = {
- .tunnel_type = TNL_T_PROTO_CAPWAP,
- .ipproto = IPPROTO_UDP,
- .hdr_len = capwap_hdr_len,
- .build_header = capwap_build_header,
-};
-
-static inline struct capwap_net *ovs_get_capwap_net(struct net *net)
-{
- struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
- return &ovs_net->vport_net.capwap;
-}
-
-/* Arbitrary value. Irrelevant as long as it's not 0 since we set the handler. */
-#define UDP_ENCAP_CAPWAP 10
-static int init_socket(struct net *net)
-{
- int err;
- struct capwap_net *capwap_net = ovs_get_capwap_net(net);
- struct sockaddr_in sin;
-
- if (capwap_net->n_tunnels) {
- capwap_net->n_tunnels++;
- return 0;
- }
-
- err = sock_create_kern(AF_INET, SOCK_DGRAM, 0,
- &capwap_net->capwap_rcv_socket);
- if (err)
- goto error;
-
- /* release net ref. */
- sk_change_net(capwap_net->capwap_rcv_socket->sk, net);
-
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = htons(CAPWAP_DST_PORT);
-
- err = kernel_bind(capwap_net->capwap_rcv_socket,
- (struct sockaddr *)&sin,
- sizeof(struct sockaddr_in));
- if (err)
- goto error_sock;
-
- udp_sk(capwap_net->capwap_rcv_socket->sk)->encap_type = UDP_ENCAP_CAPWAP;
- udp_sk(capwap_net->capwap_rcv_socket->sk)->encap_rcv = capwap_rcv;
-
- capwap_net->frag_state.timeout = CAPWAP_FRAG_TIMEOUT;
- capwap_net->frag_state.high_thresh = CAPWAP_FRAG_MAX_MEM;
- capwap_net->frag_state.low_thresh = CAPWAP_FRAG_PRUNE_MEM;
-
- inet_frags_init_net(&capwap_net->frag_state);
- udp_encap_enable();
- capwap_net->n_tunnels++;
- return 0;
-
-error_sock:
- sk_release_kernel(capwap_net->capwap_rcv_socket->sk);
-error:
- pr_warn("cannot register capwap protocol handler : %d\n", err);
- return err;
-}
-
-static void release_socket(struct net *net)
-{
- struct capwap_net *capwap_net = ovs_get_capwap_net(net);
-
- capwap_net->n_tunnels--;
- if (capwap_net->n_tunnels)
- return;
-
- inet_frags_exit_net(&capwap_net->frag_state, &frag_state);
- sk_release_kernel(capwap_net->capwap_rcv_socket->sk);
-}
-
-static struct vport *capwap_create(const struct vport_parms *parms)
-{
- struct vport *vport;
- int err;
-
- err = init_socket(ovs_dp_get_net(parms->dp));
- if (err)
- return ERR_PTR(err);
-
- vport = ovs_tnl_create(parms, &ovs_capwap_vport_ops, &capwap_tnl_ops);
- if (IS_ERR(vport))
- release_socket(ovs_dp_get_net(parms->dp));
-
- return vport;
-}
-
-static void capwap_destroy(struct vport *vport)
-{
- ovs_tnl_destroy(vport);
- release_socket(ovs_dp_get_net(vport->dp));
-}
-
-static int capwap_init(void)
-{
- inet_frags_init(&frag_state);
- return 0;
-}
-
-static void capwap_exit(void)
-{
- inet_frags_fini(&frag_state);
-}
-
-static void copy_skb_metadata(struct sk_buff *from, struct sk_buff *to)
-{
- to->pkt_type = from->pkt_type;
- to->priority = from->priority;
- to->protocol = from->protocol;
- skb_dst_set(to, dst_clone(skb_dst(from)));
- to->dev = from->dev;
- to->mark = from->mark;
-
- if (from->sk)
- skb_set_owner_w(to, from->sk);
-
-#ifdef CONFIG_NET_SCHED
- to->tc_index = from->tc_index;
-#endif
-#if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
- to->ipvs_property = from->ipvs_property;
-#endif
- skb_copy_secmark(to, from);
-}
-
-static struct sk_buff *fragment(struct sk_buff *skb, const struct vport *vport,
- struct dst_entry *dst, unsigned int hlen)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- unsigned int headroom;
- unsigned int max_frame_len = dst_mtu(dst) + skb_network_offset(skb);
- struct sk_buff *result = NULL, *list_cur = NULL;
- unsigned int remaining;
- unsigned int offset;
- __be16 frag_id;
-
- if (hlen + ~FRAG_OFF_MASK + 1 > max_frame_len) {
- if (net_ratelimit())
- pr_warn("capwap link mtu (%d) is less than minimum packet (%d)\n",
- dst_mtu(dst),
- hlen - skb_network_offset(skb) + ~FRAG_OFF_MASK + 1);
- goto error;
- }
-
- remaining = skb->len - hlen;
- offset = 0;
- frag_id = htons(atomic_inc_return(&tnl_vport->frag_id));
-
- headroom = dst->header_len + 16;
- if (!skb_network_offset(skb))
- headroom += LL_RESERVED_SPACE(dst->dev);
-
- while (remaining) {
- struct sk_buff *skb2;
- int frag_size;
- struct udphdr *udph;
- struct capwaphdr *cwh;
-
- frag_size = min(remaining, max_frame_len - hlen);
- if (remaining > frag_size)
- frag_size &= FRAG_OFF_MASK;
-
- skb2 = alloc_skb(headroom + hlen + frag_size, GFP_ATOMIC);
- if (!skb2)
- goto error;
-
- skb_reserve(skb2, headroom);
- __skb_put(skb2, hlen + frag_size);
-
- if (skb_network_offset(skb))
- skb_reset_mac_header(skb2);
- skb_set_network_header(skb2, skb_network_offset(skb));
- skb_set_transport_header(skb2, skb_transport_offset(skb));
-
- /* Copy (Ethernet)/IP/UDP/CAPWAP header. */
- copy_skb_metadata(skb, skb2);
- skb_copy_from_linear_data(skb, skb2->data, hlen);
-
- /* Copy this data chunk. */
- if (skb_copy_bits(skb, hlen + offset, skb2->data + hlen, frag_size))
- BUG();
-
- udph = udp_hdr(skb2);
- udph->len = htons(skb2->len - skb_transport_offset(skb2));
-
- cwh = capwap_hdr(skb2);
- if (remaining > frag_size)
- cwh->begin |= FRAG_HDR;
- else
- cwh->begin |= FRAG_LAST_HDR;
- cwh->frag_id = frag_id;
- cwh->frag_off = htons(offset);
-
- if (result) {
- list_cur->next = skb2;
- list_cur = skb2;
- } else
- result = list_cur = skb2;
-
- offset += frag_size;
- remaining -= frag_size;
- }
-
- consume_skb(skb);
- return result;
-
-error:
- ovs_tnl_free_linked_skbs(result);
- kfree_skb(skb);
- return NULL;
-}
-
-/* All of the following functions relate to fragmentation reassembly. */
-
-static struct frag_queue *ifq_cast(struct inet_frag_queue *ifq)
-{
- return container_of(ifq, struct frag_queue, ifq);
-}
-
-static u32 frag_hash(struct frag_match *match)
-{
- return jhash_3words((__force u16)match->id, (__force u32)match->saddr,
- (__force u32)match->daddr,
- frag_state.rnd) & (INETFRAGS_HASHSZ - 1);
-}
-
-static struct frag_queue *queue_find(struct netns_frags *ns_frag_state,
- struct frag_match *match)
-{
- struct inet_frag_queue *ifq;
-
- read_lock(&frag_state.lock);
-
- ifq = inet_frag_find(ns_frag_state, &frag_state, match, frag_hash(match));
- if (!ifq)
- return NULL;
-
- /* Unlock happens inside inet_frag_find(). */
-
- return ifq_cast(ifq);
-}
-
-static struct sk_buff *frag_reasm(struct frag_queue *fq, struct net_device *dev)
-{
- struct sk_buff *head = fq->ifq.fragments;
- struct sk_buff *frag;
-
- /* Succeed or fail, we're done with this queue. */
- inet_frag_kill(&fq->ifq, &frag_state);
-
- if (fq->ifq.len > 65535)
- return NULL;
-
- /* Can't have the head be a clone. */
- if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
- return NULL;
-
- /*
- * We're about to build frag list for this SKB. If it already has a
- * frag list, alloc a new SKB and put the existing frag list there.
- */
- if (skb_shinfo(head)->frag_list) {
- int i;
- int paged_len = 0;
-
- frag = alloc_skb(0, GFP_ATOMIC);
- if (!frag)
- return NULL;
-
- frag->next = head->next;
- head->next = frag;
- skb_shinfo(frag)->frag_list = skb_shinfo(head)->frag_list;
- skb_shinfo(head)->frag_list = NULL;
-
- for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
- paged_len += skb_shinfo(head)->frags[i].size;
- frag->len = frag->data_len = head->data_len - paged_len;
- head->data_len -= frag->len;
- head->len -= frag->len;
-
- frag->ip_summed = head->ip_summed;
- atomic_add(frag->truesize, &fq->ifq.net->mem);
- }
-
- skb_shinfo(head)->frag_list = head->next;
- atomic_sub(head->truesize, &fq->ifq.net->mem);
-
- /* Properly account for data in various packets. */
- for (frag = head->next; frag; frag = frag->next) {
- head->data_len += frag->len;
- head->len += frag->len;
-
- if (head->ip_summed != frag->ip_summed)
- head->ip_summed = CHECKSUM_NONE;
- else if (head->ip_summed == CHECKSUM_COMPLETE)
- head->csum = csum_add(head->csum, frag->csum);
-
- head->truesize += frag->truesize;
- atomic_sub(frag->truesize, &fq->ifq.net->mem);
- }
-
- head->next = NULL;
- head->dev = dev;
- head->tstamp = fq->ifq.stamp;
- fq->ifq.fragments = NULL;
-
- return head;
-}
-
-static struct sk_buff *frag_queue(struct frag_queue *fq, struct sk_buff *skb,
- u16 offset, bool frag_last)
-{
- struct sk_buff *prev, *next;
- struct net_device *dev;
- int end;
-
- if (fq->ifq.last_in & INET_FRAG_COMPLETE)
- goto error;
-
- if (!skb->len)
- goto error;
-
- end = offset + skb->len;
-
- if (frag_last) {
- /*
- * Last fragment, shouldn't already have data past our end or
- * have another last fragment.
- */
- if (end < fq->ifq.len || fq->ifq.last_in & INET_FRAG_LAST_IN)
- goto error;
-
- fq->ifq.last_in |= INET_FRAG_LAST_IN;
- fq->ifq.len = end;
- } else {
- /* Fragments should align to 8 byte chunks. */
- if (end & ~FRAG_OFF_MASK)
- goto error;
-
- if (end > fq->ifq.len) {
- /*
- * Shouldn't have data past the end, if we already
- * have one.
- */
- if (fq->ifq.last_in & INET_FRAG_LAST_IN)
- goto error;
-
- fq->ifq.len = end;
- }
- }
-
- /* Find where we fit in. */
- prev = NULL;
- for (next = fq->ifq.fragments; next != NULL; next = next->next) {
- if (FRAG_CB(next)->offset >= offset)
- break;
- prev = next;
- }
-
- /*
- * Overlapping fragments aren't allowed. We shouldn't start before
- * the end of the previous fragment.
- */
- if (prev && FRAG_CB(prev)->offset + prev->len > offset)
- goto error;
-
- /* We also shouldn't end after the beginning of the next fragment. */
- if (next && end > FRAG_CB(next)->offset)
- goto error;
-
- FRAG_CB(skb)->offset = offset;
-
- /* Link into list. */
- skb->next = next;
- if (prev)
- prev->next = skb;
- else
- fq->ifq.fragments = skb;
-
- dev = skb->dev;
- skb->dev = NULL;
-
- fq->ifq.stamp = skb->tstamp;
- fq->ifq.meat += skb->len;
- atomic_add(skb->truesize, &fq->ifq.net->mem);
- if (offset == 0)
- fq->ifq.last_in |= INET_FRAG_FIRST_IN;
-
- /* If we have all fragments do reassembly. */
- if (fq->ifq.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
- fq->ifq.meat == fq->ifq.len)
- return frag_reasm(fq, dev);
-
- write_lock(&frag_state.lock);
- list_move_tail(&fq->ifq.lru_list, &fq->ifq.net->lru_list);
- write_unlock(&frag_state.lock);
-
- return NULL;
-
-error:
- kfree_skb(skb);
- return NULL;
-}
-
-static struct sk_buff *defrag(struct sk_buff *skb, bool frag_last)
-{
- struct iphdr *iph = ip_hdr(skb);
- struct capwaphdr *cwh = capwap_hdr(skb);
- struct capwap_net *capwap_net = ovs_get_capwap_net(dev_net(skb->dev));
- struct netns_frags *ns_frag_state = &capwap_net->frag_state;
- struct frag_match match;
- u16 frag_off;
- struct frag_queue *fq;
-
- inet_frag_evictor(ns_frag_state, &frag_state, false);
-
- match.daddr = iph->daddr;
- match.saddr = iph->saddr;
- match.id = cwh->frag_id;
- frag_off = ntohs(cwh->frag_off) & FRAG_OFF_MASK;
-
- fq = queue_find(ns_frag_state, &match);
- if (fq) {
- spin_lock(&fq->ifq.lock);
- skb = frag_queue(fq, skb, frag_off, frag_last);
- spin_unlock(&fq->ifq.lock);
-
- inet_frag_put(&fq->ifq, &frag_state);
-
- return skb;
- }
-
- kfree_skb(skb);
- return NULL;
-}
-
-static void capwap_frag_init(struct inet_frag_queue *ifq, void *match_)
-{
- struct frag_match *match = match_;
-
- ifq_cast(ifq)->match = *match;
-}
-
-static unsigned int capwap_frag_hash(struct inet_frag_queue *ifq)
-{
- return frag_hash(&ifq_cast(ifq)->match);
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
-static int capwap_frag_match(struct inet_frag_queue *ifq, void *a_)
-#else
-static bool capwap_frag_match(struct inet_frag_queue *ifq, void *a_)
-#endif
-{
- struct frag_match *a = a_;
- struct frag_match *b = &ifq_cast(ifq)->match;
-
- return a->id == b->id && a->saddr == b->saddr && a->daddr == b->daddr;
-}
-
-/* Run when the timeout for a given queue expires. */
-static void capwap_frag_expire(unsigned long ifq)
-{
- struct frag_queue *fq;
-
- fq = ifq_cast((struct inet_frag_queue *)ifq);
-
- spin_lock(&fq->ifq.lock);
-
- if (!(fq->ifq.last_in & INET_FRAG_COMPLETE))
- inet_frag_kill(&fq->ifq, &frag_state);
-
- spin_unlock(&fq->ifq.lock);
- inet_frag_put(&fq->ifq, &frag_state);
-}
-
-const struct vport_ops ovs_capwap_vport_ops = {
- .type = OVS_VPORT_TYPE_CAPWAP,
- .flags = VPORT_F_TUN_ID,
- .init = capwap_init,
- .exit = capwap_exit,
- .create = capwap_create,
- .destroy = capwap_destroy,
- .get_name = ovs_tnl_get_name,
- .get_options = ovs_tnl_get_options,
- .set_options = ovs_tnl_set_options,
- .send = ovs_tnl_send,
-};
-#else
-#warning CAPWAP tunneling will not be available on kernels before 2.6.26
-#endif /* Linux kernel < 2.6.26 */
+++ /dev/null
-/*
- * 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
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#ifndef VPORT_CAPWAP_H
-#define VPORT_CAPWAP_H 1
-
-#include <linux/net.h>
-
-struct capwap_net {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
- struct socket *capwap_rcv_socket;
- struct netns_frags frag_state;
- int n_tunnels;
-#endif
-};
-
-#endif /* vport-capwap.h */
iph = ip_hdr(skb);
vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr, key,
tunnel_type, &mutable);
- if (unlikely(!vport)) {
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+ if (unlikely(!vport))
goto error;
- }
tnl_flags = gre_flags_to_tunnel_flags(mutable, gre_flags, &key);
tnl_tun_key_init(&tun_key, iph, key, tnl_flags);
--- /dev/null
+/*
+ * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 2013 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/udp.h>
+
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/udp.h>
+
+#include "datapath.h"
+#include "tunnel.h"
+#include "vport.h"
+
+
+/*
+ * LISP encapsulation header:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |N|L|E|V|I|flags| Nonce/Map-Version |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Instance ID/Locator Status Bits |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+/**
+ * struct lisphdr - LISP header
+ * @nonce_present: Flag indicating the presence of a 24 bit nonce value.
+ * @locator_status_bits_present: Flag indicating the presence of Locator Status
+ * Bits (LSB).
+ * @solicit_echo_nonce: Flag indicating the use of the echo noncing mechanism.
+ * @map_version_present: Flag indicating the use of mapping versioning.
+ * @instance_id_present: Flag indicating the presence of a 24 bit Instance ID.
+ * @reserved_flags: 3 bits reserved for future flags.
+ * @nonce: 24 bit nonce value.
+ * @map_version: 24 bit mapping version.
+ * @locator_status_bits: Locator Status Bits: 32 bits when instance_id_present
+ * is not set, 8 bits when it is.
+ * @instance_id: 24 bit Instance ID
+ */
+struct lisphdr {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ __u8 reserved_flags:3;
+ __u8 instance_id_present:1;
+ __u8 map_version_present:1;
+ __u8 solicit_echo_nonce:1;
+ __u8 locator_status_bits_present:1;
+ __u8 nonce_present:1;
+#else
+ __u8 nonce_present:1;
+ __u8 locator_status_bits_present:1;
+ __u8 solicit_echo_nonce:1;
+ __u8 map_version_present:1;
+ __u8 instance_id_present:1;
+ __u8 reserved_flags:3;
+#endif
+ union {
+ __u8 nonce[3];
+ __u8 map_version[3];
+ } u1;
+ union {
+ __be32 locator_status_bits;
+ struct {
+ __u8 instance_id[3];
+ __u8 locator_status_bits;
+ } word2;
+ } u2;
+};
+
+#define LISP_HLEN (sizeof(struct udphdr) + sizeof(struct lisphdr))
+
+static inline int lisp_hdr_len(const struct tnl_mutable_config *mutable,
+ const struct ovs_key_ipv4_tunnel *tun_key)
+{
+ return LISP_HLEN;
+}
+
+/**
+ * struct lisp_port - Keeps track of open UDP ports
+ * @list: list element.
+ * @port: The UDP port number in network byte order.
+ * @socket: The socket created for this port number.
+ * @count: How many ports are using this socket/port.
+ */
+struct lisp_port {
+ struct list_head list;
+ __be16 port;
+ struct socket *lisp_rcv_socket;
+ int count;
+};
+
+static LIST_HEAD(lisp_ports);
+
+static struct lisp_port *lisp_port_exists(struct net *net, __be16 port)
+{
+ struct lisp_port *lisp_port;
+
+ list_for_each_entry(lisp_port, &lisp_ports, list) {
+ if (lisp_port->port == port &&
+ net_eq(sock_net(lisp_port->lisp_rcv_socket->sk), net))
+ return lisp_port;
+ }
+
+ return NULL;
+}
+
+static inline struct lisphdr *lisp_hdr(const struct sk_buff *skb)
+{
+ return (struct lisphdr *)(udp_hdr(skb) + 1);
+}
+
+static int lisp_tnl_send(struct vport *vport, struct sk_buff *skb)
+{
+ int tnl_len;
+ int network_offset = skb_network_offset(skb);
+
+ /* We only encapsulate IPv4 and IPv6 packets */
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ case htons(ETH_P_IPV6):
+ /* Pop off "inner" Ethernet header */
+ skb_pull(skb, network_offset);
+ tnl_len = ovs_tnl_send(vport, skb);
+ return tnl_len > 0 ? tnl_len + network_offset : tnl_len;
+ default:
+ kfree_skb(skb);
+ return 0;
+ }
+}
+
+/* Convert 64 bit tunnel ID to 24 bit Instance ID. */
+static void tunnel_id_to_instance_id(__be64 tun_id, __u8 *iid)
+{
+
+#ifdef __BIG_ENDIAN
+ iid[0] = (__force __u8)(tun_id >> 16);
+ iid[1] = (__force __u8)(tun_id >> 8);
+ iid[2] = (__force __u8)tun_id;
+#else
+ iid[0] = (__force __u8)((__force u64)tun_id >> 40);
+ iid[1] = (__force __u8)((__force u64)tun_id >> 48);
+ iid[2] = (__force __u8)((__force u64)tun_id >> 56);
+#endif
+}
+
+/* Convert 24 bit Instance ID to 64 bit tunnel ID. */
+static __be64 instance_id_to_tunnel_id(__u8 *iid)
+{
+#ifdef __BIG_ENDIAN
+ return (iid[0] << 16) | (iid[1] << 8) | iid[2];
+#else
+ return (__force __be64)(((__force u64)iid[0] << 40) |
+ ((__force u64)iid[1] << 48) |
+ ((__force u64)iid[2] << 56));
+#endif
+}
+
+static struct sk_buff *lisp_build_header(const struct vport *vport,
+ const struct tnl_mutable_config *mutable,
+ struct dst_entry *dst,
+ struct sk_buff *skb,
+ int tunnel_hlen)
+{
+ struct udphdr *udph = udp_hdr(skb);
+ struct lisphdr *lisph = (struct lisphdr *)(udph + 1);
+ const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
+ __be64 out_key;
+ u32 flags;
+
+ tnl_get_param(mutable, tun_key, &flags, &out_key);
+
+ udph->dest = mutable->dst_port;
+ udph->source = htons(ovs_tnl_get_src_port(skb));
+ udph->check = 0;
+ udph->len = htons(skb->len - skb_transport_offset(skb));
+
+ lisph->nonce_present = 0; /* We don't support echo nonce algorithm */
+ lisph->locator_status_bits_present = 1; /* Set LSB */
+ lisph->solicit_echo_nonce = 0; /* No echo noncing */
+ lisph->map_version_present = 0; /* No mapping versioning, nonce instead */
+ lisph->instance_id_present = 1; /* Store the tun_id as Instance ID */
+ lisph->reserved_flags = 0; /* Reserved flags, set to 0 */
+
+ lisph->u1.nonce[0] = 0;
+ lisph->u1.nonce[1] = 0;
+ lisph->u1.nonce[2] = 0;
+
+ tunnel_id_to_instance_id(out_key, &lisph->u2.word2.instance_id[0]);
+ lisph->u2.word2.locator_status_bits = 1;
+
+ /*
+ * Allow our local IP stack to fragment the outer packet even if the
+ * DF bit is set as a last resort. We also need to force selection of
+ * an IP ID here because Linux will otherwise leave it at 0 if the
+ * packet originally had DF set.
+ */
+ skb->local_df = 1;
+ __ip_select_ident(ip_hdr(skb), dst, 0);
+
+ return skb;
+}
+
+/* Called with rcu_read_lock and BH disabled. */
+static int lisp_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ struct vport *vport;
+ struct lisphdr *lisph;
+ const struct tnl_mutable_config *mutable;
+ struct iphdr *iph, *inner_iph;
+ struct ovs_key_ipv4_tunnel tun_key;
+ __be64 key;
+ u32 tunnel_flags = 0;
+ struct ethhdr *ethh;
+ __be16 protocol;
+
+ if (unlikely(!pskb_may_pull(skb, LISP_HLEN)))
+ goto error;
+
+ lisph = lisp_hdr(skb);
+
+ skb_pull_rcsum(skb, LISP_HLEN);
+
+ if (lisph->instance_id_present != 1)
+ key = 0;
+ else
+ key = instance_id_to_tunnel_id(&lisph->u2.word2.instance_id[0]);
+
+ iph = ip_hdr(skb);
+ vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr,
+ key, TNL_T_PROTO_LISP, &mutable);
+ if (unlikely(!vport))
+ goto error;
+
+ if (mutable->flags & TNL_F_IN_KEY_MATCH || !mutable->key.daddr)
+ tunnel_flags = OVS_TNL_F_KEY;
+ else
+ key = 0;
+
+ /* Save outer tunnel values */
+ tnl_tun_key_init(&tun_key, iph, key, tunnel_flags);
+ OVS_CB(skb)->tun_key = &tun_key;
+
+ /* Drop non-IP inner packets */
+ inner_iph = (struct iphdr *)(lisph + 1);
+ switch (inner_iph->version) {
+ case 4:
+ protocol = htons(ETH_P_IP);
+ break;
+ case 6:
+ protocol = htons(ETH_P_IPV6);
+ break;
+ default:
+ goto error;
+ }
+
+ /* Add Ethernet header */
+ ethh = (struct ethhdr *)skb_push(skb, ETH_HLEN);
+ memset(ethh, 0, ETH_HLEN);
+ ethh->h_dest[0] = 0x02;
+ ethh->h_source[0] = 0x02;
+ ethh->h_proto = protocol;
+
+ ovs_tnl_rcv(vport, skb);
+ goto out;
+
+error:
+ kfree_skb(skb);
+out:
+ return 0;
+}
+
+/* Arbitrary value. Irrelevant as long as it's not 0 since we set the handler. */
+#define UDP_ENCAP_LISP 1
+static int lisp_socket_init(struct lisp_port *lisp_port, struct net *net)
+{
+ int err;
+ struct sockaddr_in sin;
+
+ err = sock_create_kern(AF_INET, SOCK_DGRAM, 0,
+ &lisp_port->lisp_rcv_socket);
+ if (err)
+ goto error;
+
+ /* release net ref. */
+ sk_change_net(lisp_port->lisp_rcv_socket->sk, net);
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = lisp_port->port;
+
+ err = kernel_bind(lisp_port->lisp_rcv_socket, (struct sockaddr *)&sin,
+ sizeof(struct sockaddr_in));
+ if (err)
+ goto error_sock;
+
+ udp_sk(lisp_port->lisp_rcv_socket->sk)->encap_type = UDP_ENCAP_LISP;
+ udp_sk(lisp_port->lisp_rcv_socket->sk)->encap_rcv = lisp_rcv;
+
+ udp_encap_enable();
+
+ return 0;
+
+error_sock:
+ sk_release_kernel(lisp_port->lisp_rcv_socket->sk);
+error:
+ pr_warn("cannot register lisp protocol handler: %d\n", err);
+ return err;
+}
+
+static void lisp_tunnel_release(struct lisp_port *lisp_port)
+{
+ lisp_port->count--;
+
+ if (lisp_port->count == 0) {
+ /* Release old socket */
+ sk_release_kernel(lisp_port->lisp_rcv_socket->sk);
+ list_del(&lisp_port->list);
+ kfree(lisp_port);
+ }
+}
+
+static int lisp_tunnel_setup(struct net *net, struct nlattr *options,
+ struct lisp_port **lport)
+{
+ struct nlattr *a;
+ int err;
+ u16 dst_port;
+ struct lisp_port *lisp_port = NULL;
+
+ *lport = NULL;
+
+ if (!options) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT);
+ if (a && nla_len(a) == sizeof(u16)) {
+ dst_port = nla_get_u16(a);
+ } else {
+ /* Require destination port from userspace. */
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Verify if we already have a socket created for this port */
+ lisp_port = lisp_port_exists(net, htons(dst_port));
+ if (lisp_port) {
+ lisp_port->count++;
+ err = 0;
+ *lport = lisp_port;
+ goto out;
+ }
+
+ /* Add a new socket for this port */
+ lisp_port = kzalloc(sizeof(struct lisp_port), GFP_KERNEL);
+ if (!lisp_port) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ lisp_port->port = htons(dst_port);
+ lisp_port->count = 1;
+ list_add_tail(&lisp_port->list, &lisp_ports);
+
+ err = lisp_socket_init(lisp_port, net);
+ if (err)
+ goto error;
+
+ *lport = lisp_port;
+ goto out;
+
+error:
+ list_del(&lisp_port->list);
+ kfree(lisp_port);
+out:
+ return err;
+}
+
+static int lisp_tnl_set_options(struct vport *vport, struct nlattr *options)
+{
+ int err;
+ struct net *net = ovs_dp_get_net(vport->dp);
+ struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+ struct tnl_mutable_config *config;
+ struct lisp_port *old_port = NULL;
+ struct lisp_port *lisp_port = NULL;
+
+ config = rtnl_dereference(tnl_vport->mutable);
+
+ old_port = lisp_port_exists(net, config->dst_port);
+
+ err = lisp_tunnel_setup(net, options, &lisp_port);
+ if (err)
+ goto out;
+
+ err = ovs_tnl_set_options(vport, options);
+
+ if (err)
+ lisp_tunnel_release(lisp_port);
+ else {
+ /* Release old socket */
+ lisp_tunnel_release(old_port);
+ }
+out:
+ return err;
+}
+
+static const struct tnl_ops ovs_lisp_tnl_ops = {
+ .tunnel_type = TNL_T_PROTO_LISP,
+ .ipproto = IPPROTO_UDP,
+ .hdr_len = lisp_hdr_len,
+ .build_header = lisp_build_header,
+};
+
+static void lisp_tnl_destroy(struct vport *vport)
+{
+ struct lisp_port *lisp_port;
+ struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+ struct tnl_mutable_config *config;
+
+ config = rtnl_dereference(tnl_vport->mutable);
+
+ lisp_port = lisp_port_exists(ovs_dp_get_net(vport->dp),
+ config->dst_port);
+
+ lisp_tunnel_release(lisp_port);
+
+ ovs_tnl_destroy(vport);
+}
+
+static struct vport *lisp_tnl_create(const struct vport_parms *parms)
+{
+ int err;
+ struct vport *vport;
+ struct lisp_port *lisp_port = NULL;
+
+ err = lisp_tunnel_setup(ovs_dp_get_net(parms->dp), parms->options,
+ &lisp_port);
+ if (err)
+ return ERR_PTR(err);
+
+ vport = ovs_tnl_create(parms, &ovs_lisp_vport_ops, &ovs_lisp_tnl_ops);
+
+ if (IS_ERR(vport))
+ lisp_tunnel_release(lisp_port);
+
+ return vport;
+}
+
+const struct vport_ops ovs_lisp_vport_ops = {
+ .type = OVS_VPORT_TYPE_LISP,
+ .flags = VPORT_F_TUN_ID,
+ .create = lisp_tnl_create,
+ .destroy = lisp_tnl_destroy,
+ .get_name = ovs_tnl_get_name,
+ .get_options = ovs_tnl_get_options,
+ .set_options = lisp_tnl_set_options,
+ .send = lisp_tnl_send,
+};
+#else
+#warning LISP tunneling will not be available on kernels before 2.6.26
+#endif /* Linux kernel < 2.6.26 */
return (struct vxlanhdr *)(udp_hdr(skb) + 1);
}
-/* Compute source port for outgoing packet.
- * Currently we use the flow hash.
- */
-static u16 get_src_port(struct sk_buff *skb)
-{
- int low;
- int high;
- unsigned int range;
- u32 hash = OVS_CB(skb)->flow->hash;
-
- inet_get_local_port_range(&low, &high);
- range = (high - low) + 1;
- return (((u64) hash * range) >> 32) + low;
-}
-
static struct sk_buff *vxlan_build_header(const struct vport *vport,
const struct tnl_mutable_config *mutable,
struct dst_entry *dst,
tnl_get_param(mutable, tun_key, &flags, &out_key);
udph->dest = mutable->dst_port;
- udph->source = htons(get_src_port(skb));
+ udph->source = htons(ovs_tnl_get_src_port(skb));
udph->check = 0;
udph->len = htons(skb->len - skb_transport_offset(skb));
iph = ip_hdr(skb);
vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr,
key, TNL_T_PROTO_VXLAN, &mutable);
- if (unlikely(!vport)) {
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+ if (unlikely(!vport))
goto error;
- }
if (mutable->flags & TNL_F_IN_KEY_MATCH || !mutable->key.daddr)
tunnel_flags = OVS_TNL_F_KEY;
&ovs_gre_vport_ops,
&ovs_gre64_vport_ops,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
- &ovs_capwap_vport_ops,
&ovs_vxlan_vport_ops,
+ &ovs_lisp_vport_ops,
#endif
};
#include <linux/spinlock.h>
#include <linux/u64_stats_sync.h>
-#include "vport-capwap.h"
-
struct vport;
struct vport_parms;
-struct vport_net {
- struct capwap_net capwap;
-};
-
/* The following definitions are for users of the vport subsytem: */
int ovs_vport_init(void);
extern const struct vport_ops ovs_internal_vport_ops;
extern const struct vport_ops ovs_gre_vport_ops;
extern const struct vport_ops ovs_gre64_vport_ops;
-extern const struct vport_ops ovs_capwap_vport_ops;
extern const struct vport_ops ovs_vxlan_vport_ops;
+extern const struct vport_ops ovs_lisp_vport_ops;
#endif /* vport.h */
-openvswitch (1.9.90-1) unstable; urgency=low
+openvswitch (1.10.90-1) unstable; urgency=low
[ Open vSwitch team ]
* New upstream version
- - Nothing yet! Try NEWS...
+ - Nothing yet! Try NEWS...
- -- Open vSwitch team <dev@openvswitch.org> Wed, 24 Oct 2012 16:12:57 -0700
+ -- Open vSwitch team <dev@openvswitch.org> Mon, 04 Feb 2013 21:52:34 -0700
+
+openvswitch (1.10.0-1) unstable; urgency=low
+ [ Open vSwitch team ]
+ * New upstream version
+ - Bridge compatibility support has been removed. Any uses that
+ rely on ovs-brcompatd will have to stick with Open vSwitch 1.9.x
+ or adapt to native Open vSwitch support (e.g. use ovs-vsctl instead
+ of brctl).
+ - The maximum size of the MAC learning table is now configurable.
+ - With the Linux datapath, packets for new flows are now queued
+ separately on a per-port basis, so it should no longer be
+ possible for a large number of new flows arriving on one port to
+ prevent new flows from being processed on other ports.
+ - Many "ovs-vsctl" database commands now accept an --if-exists option.
+ Please refer to the ovs-vsctl manpage for details.
+ - OpenFlow:
+ - Experimental support for newer versions of OpenFlow. See
+ the "What versions of OpenFlow does Open vSwitch support?"
+ question in the FAQ for more details.
+ - The OpenFlow "dp_desc" may now be configured by setting the
+ value of other-config:dp-desc in the Bridge table.
+ - It is possible to request the OpenFlow port number with the
+ "ofport_request" column in the Interface table.
+ - Tunneling:
+ - New support for the VXLAN tunnel protocol (see the IETF draft here:
+ http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-03).
+ - Tunneling requires the version of the kernel module paired with
+ Open vSwitch 1.9.0 or later.
+ - Inheritance of the Don't Fragment bit in IP tunnels (df_inherit)
+ is no longer supported.
+ - Path MTU discovery is no longer supported.
+ - ovs-dpctl:
+ - The "dump-flows" and "del-flows" no longer require an argument
+ if only one datapath exists.
+ - ovs-appctl:
+ - New "vlog/disable-rate-limit" and "vlog/enable-rate-limit"
+ commands available allow control over logging rate limits.
+ - New "dpif/dump-dps", "dpif/show", and "dpif/dump-flows" command
+ that mimic the equivalent ovs-dpctl commands.
+ - The ofproto library is now responsible for assigning OpenFlow port
+ numbers. An ofproto implementation should assign them when
+ port_construct() is called.
+ - All dpif-based bridges of a particular type share a common
+ datapath called "ovs-<type>", e.g. "ovs-system". The ovs-dpctl
+ commands will now return information on that shared datapath. To
+ get the equivalent bridge-specific information, use the new
+ "ovs-appctl dpif/*" commands.
+ - Backward-incompatible changes:
+ - Earlier Open vSwitch versions treated ANY as a wildcard in flow
+ syntax. OpenFlow 1.1 adds a port named ANY, which introduces a
+ conflict. ANY was rarely used in flow syntax, so we chose to
+ retire that meaning of ANY in favor of the OpenFlow 1.1 meaning.
+ - Patch ports no longer require kernel support, so they now work
+ with FreeBSD and the kernel module built into Linux 3.3 and later.
+
+ -- Open vSwitch team <dev@openvswitch.org> Mon, 04 Feb 2013 21:52:34 -0700
openvswitch (1.9.0-1) unstable; urgency=low
[ Open vSwitch team ]
* New upstream version
- - The tunneling code no longer assumes input and output keys are symmetric.
- If they are not, PMTUD needs to be disabled for tunneling to work. Note
- this only applies to flow-based keys.
- Datapath:
- Support for ipv6 set action.
- SKB mark matching and setting.
- Allow bitwise masking for SHA and THA fields in ARP, SLL and TLL
fields in IPv6 neighbor discovery messages, and IPv6 flow label.
- Adds support for writing to the metadata field for a flow.
+ - Tunneling:
+ - The tunneling code no longer assumes input and output keys are
+ symmetric. If they are not, PMTUD needs to be disabled for
+ tunneling to work. Note this only applies to flow-based keys.
+ - New support for a nonstandard form of GRE that supports a 64-bit key.
+ - Tunnel Path MTU Discovery default value was set to 'disabled'.
+ This feature is deprecated and will be removed soon.
+ - Tunnel header caching removed.
- ovs-ofctl:
- Commands and actions that accept port numbers now also accept keywords
that represent those ports (such as LOCAL, NONE, and ALL). This is
are true, but because we do not know of any users for this
feature it seems better on balance to remove it. (The ovs-pki-cgi
program was not included in distribution packaging.)
- - Tunnel Path MTU Discovery default value was set to 'disabled'. This
- feature is deprecated and will be removed soon.
- ovsdb-server now enforces the immutability of immutable columns. This
was not enforced in earlier versions due to an oversight.
- - New support for a nonstandard form of GRE that supports a 64-bit key.
- - Tunnel header caching removed.
- The following features are now deprecated. They will be removed no
earlier than February 2013. Please email dev@openvswitch.org with
concerns.
${shlibs:Depends}, ${misc:Depends},
openvswitch-common (= ${binary:Version}),
openvswitch-controller (= ${binary:Version}),
- openvswitch-ipsec (= ${binary:Version}),
openvswitch-switch (= ${binary:Version})
Description: Debug symbols for Open vSwitch packages
Open vSwitch is a production quality, multilayer, software-based, Ethernet
* for %OVS_PACKET_CMD_EXECUTE. It has nested %OVS_ACTION_ATTR_* attributes.
* @OVS_PACKET_ATTR_USERDATA: Present for an %OVS_PACKET_CMD_ACTION
* notification if the %OVS_ACTION_ATTR_USERSPACE action specified an
- * %OVS_USERSPACE_ATTR_USERDATA attribute.
+ * %OVS_USERSPACE_ATTR_USERDATA attribute, with the same length and content
+ * specified there.
*
* These attributes follow the &struct ovs_header within the Generic Netlink
* payload for %OVS_PACKET_* commands.
OVS_PACKET_ATTR_PACKET, /* Packet data. */
OVS_PACKET_ATTR_KEY, /* Nested OVS_KEY_ATTR_* attributes. */
OVS_PACKET_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */
- OVS_PACKET_ATTR_USERDATA, /* u64 OVS_ACTION_ATTR_USERSPACE arg. */
+ OVS_PACKET_ATTR_USERDATA, /* OVS_ACTION_ATTR_USERSPACE arg. */
__OVS_PACKET_ATTR_MAX
};
OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */
OVS_VPORT_TYPE_GRE, /* GRE tunnel. */
OVS_VPORT_TYPE_VXLAN, /* VXLAN tunnel */
- OVS_VPORT_TYPE_CAPWAP = 102, /* CAPWAP tunnel */
OVS_VPORT_TYPE_GRE64 = 104, /* GRE tunnel with 64-bit keys */
+ OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */
__OVS_VPORT_TYPE_MAX
};
#ifdef __KERNEL__
OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */
#endif
+
+ OVS_KEY_ATTR_MPLS = 62, /* struct ovs_key_mpls */
OVS_KEY_ATTR_TUN_ID = 63, /* be64 tunnel ID */
__OVS_KEY_ATTR_MAX
};
__u8 eth_dst[6];
};
+struct ovs_key_mpls {
+ __be32 mpls_top_lse;
+};
+
struct ovs_key_ipv4 {
__be32 ipv4_src;
__be32 ipv4_dst;
* enum ovs_userspace_attr - Attributes for %OVS_ACTION_ATTR_USERSPACE action.
* @OVS_USERSPACE_ATTR_PID: u32 Netlink PID to which the %OVS_PACKET_CMD_ACTION
* message should be sent. Required.
- * @OVS_USERSPACE_ATTR_USERDATA: If present, its u64 argument is copied to the
- * %OVS_PACKET_CMD_ACTION message as %OVS_PACKET_ATTR_USERDATA,
+ * @OVS_USERSPACE_ATTR_USERDATA: If present, its variable-length argument is
+ * copied to the %OVS_PACKET_CMD_ACTION message as %OVS_PACKET_ATTR_USERDATA.
*/
enum ovs_userspace_attr {
OVS_USERSPACE_ATTR_UNSPEC,
OVS_USERSPACE_ATTR_PID, /* u32 Netlink PID to receive upcalls. */
- OVS_USERSPACE_ATTR_USERDATA, /* u64 optional user-specified cookie. */
+ OVS_USERSPACE_ATTR_USERDATA, /* Optional user-specified cookie. */
__OVS_USERSPACE_ATTR_MAX
};
#define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)
+/**
+ * struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument.
+ * @mpls_lse: MPLS label stack entry to push.
+ * @mpls_ethertype: Ethertype to set in the encapsulating ethernet frame.
+ *
+ * The only values @mpls_ethertype should ever be given are %ETH_P_MPLS_UC and
+ * %ETH_P_MPLS_MC, indicating MPLS unicast or multicast. Other are rejected.
+ */
+struct ovs_action_push_mpls {
+ __be32 mpls_lse;
+ __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */
+};
+
/**
* struct ovs_action_push_vlan - %OVS_ACTION_ATTR_PUSH_VLAN action argument.
* @vlan_tpid: Tag protocol identifier (TPID) to push.
* @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
* @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested
* %OVS_USERSPACE_ATTR_* attributes.
- * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The
- * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its
- * value.
* @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the
* packet.
* @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet.
* @OVS_ACTION_ATTR_SAMPLE: Probabilitically executes actions, as specified in
* the nested %OVS_SAMPLE_ATTR_* attributes.
+ * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The
+ * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its
+ * value.
+ * @OVS_ACTION_ATTR_PUSH_MPLS: Push a new MPLS label stack entry onto the
+ * top of the packets MPLS label stack. Set the ethertype of the
+ * encapsulating frame to either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC to
+ * indicate the new packet contents.
+ * @OVS_ACTION_ATTR_POP_MPLS: Pop an MPLS label stack entry off of the
+ * packet's MPLS label stack. Set the encapsulating frame's ethertype to
+ * indicate the new packet contents This could potentially still be
+ * %ETH_P_MPLS_* if the resulting MPLS label stack is not empty. If there
+ * is no MPLS label stack, as determined by ethertype, no action is taken.
*
* Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all
* fields within a header are modifiable, e.g. the IPv4 protocol and fragment
OVS_ACTION_ATTR_PUSH_VLAN, /* struct ovs_action_push_vlan. */
OVS_ACTION_ATTR_POP_VLAN, /* No argument. */
OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */
+ OVS_ACTION_ATTR_PUSH_MPLS, /* struct ovs_action_push_mpls. */
+ OVS_ACTION_ATTR_POP_MPLS, /* __be16 ethertype. */
__OVS_ACTION_ATTR_MAX
};
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
NXAST_NOTE, /* struct nx_action_note */
NXAST_SET_TUNNEL64, /* struct nx_action_set_tunnel64 */
NXAST_MULTIPATH, /* struct nx_action_multipath */
- NXAST_AUTOPATH__DEPRECATED, /* struct nx_action_autopath */
+ NXAST_AUTOPATH__OBSOLETE, /* No longer used. */
NXAST_BUNDLE, /* struct nx_action_bundle */
NXAST_BUNDLE_LOAD, /* struct nx_action_bundle */
NXAST_RESUBMIT_TABLE, /* struct nx_action_resubmit */
NXAST_CONTROLLER, /* struct nx_action_controller */
NXAST_DEC_TTL_CNT_IDS, /* struct nx_action_cnt_ids */
NXAST_WRITE_METADATA, /* struct nx_action_write_metadata */
+ NXAST_PUSH_MPLS, /* struct nx_action_push_mpls */
+ NXAST_POP_MPLS, /* struct nx_action_pop_mpls */
};
/* Header for Nicira-defined actions. */
};
OFP_ASSERT(sizeof(struct nx_action_fin_timeout) == 16);
\f
-/* Action structure for NXAST_AUTOPATH.
- *
- * This action performs the following steps in sequence:
- *
- * 1. Hashes the flow using an implementation-defined hash function.
- *
- * The hashed fields' values are drawn from the current state of the
- * flow, including all modifications that have been made by actions up to
- * this point.
- *
- * 2. Selects an OpenFlow 'port'.
- *
- * 'port' is selected in an implementation-defined manner, taking into
- * account 'id' and the hash value calculated in step 1.
- *
- * Generally a switch will have been configured with a set of ports that
- * may be chosen given 'id'. The switch may take into account any number
- * of factors when choosing 'port' from its configured set. Factors may
- * include carrier, load, and the results of configuration protocols such
- * as LACP.
- *
- * 3. Stores 'port' in dst[ofs:ofs+n_bits].
- *
- * The format and semantics of 'dst' and 'ofs_nbits' are similar to those
- * for the NXAST_REG_LOAD action.
- *
- * The switch will reject actions in which ofs+n_bits is greater than the width
- * of 'dst', with error type OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT.
- */
-struct nx_action_autopath {
- ovs_be16 type; /* OFPAT_VENDOR. */
- ovs_be16 len; /* Length is 24. */
- ovs_be32 vendor; /* NX_VENDOR_ID. */
- ovs_be16 subtype; /* NXAST_AUTOPATH. */
-
- /* Where to store the result. */
- ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */
- ovs_be32 dst; /* Destination. */
-
- ovs_be32 id; /* Autopath ID. */
- ovs_be32 pad;
-};
-OFP_ASSERT(sizeof(struct nx_action_autopath) == 24);
-\f
/* Action structure for NXAST_BUNDLE and NXAST_BUNDLE_LOAD.
*
* The bundle actions choose a slave from a supplied list of options.
/* Tunnel ID.
*
- * For a packet received via a GRE or VXLAN tunnel including a (32-bit) key, the
- * key is stored in the low 32-bits and the high bits are zeroed. For other
- * packets, the value is 0.
+ * For a packet received via a GRE, VXLAN or LISP tunnel including a (32-bit)
+ * key, the key is stored in the low 32-bits and the high bits are zeroed. For
+ * other packets, the value is 0.
*
* All zero bits, for packets not received via a keyed tunnel.
*
};
OFP_ASSERT(sizeof(struct nx_action_write_metadata) == 32);
+/* Action structure for NXAST_PUSH_MPLS. */
+struct nx_action_push_mpls {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_PUSH_MPLS. */
+ ovs_be16 ethertype; /* Ethertype */
+ uint8_t pad[4];
+};
+OFP_ASSERT(sizeof(struct nx_action_push_mpls) == 16);
+
+/* Action structure for NXAST_POP_MPLS. */
+struct nx_action_pop_mpls {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_POP_MPLS. */
+ ovs_be16 ethertype; /* Ethertype */
+ uint8_t pad[4];
+};
+OFP_ASSERT(sizeof(struct nx_action_pop_mpls) == 16);
+
#endif /* openflow/nicira-ext.h */
OFPAT11_PUSH_VLAN, /* Push a new VLAN tag */
OFPAT11_POP_VLAN, /* Pop the outer VLAN tag */
- OFPAT11_PUSH_MPLS, /* Push a new MPLS tag */
- OFPAT11_POP_MPLS, /* Pop the outer MPLS tag */
+ OFPAT11_PUSH_MPLS, /* Push a new MPLS Label Stack Entry */
+ OFPAT11_POP_MPLS, /* Pop the outer MPLS Label Stack Entry */
OFPAT11_SET_QUEUE, /* Set queue id when outputting to a port */
OFPAT11_GROUP, /* Apply group. */
OFPAT11_SET_NW_TTL, /* IP TTL. */
* not IP. */
/* Bit 5 was previously used for path MTU discovery. " */
/* Bit 6 is reserved since it was previously used for Tunnel header caching. */
-#define TNL_F_IPSEC (1 << 7) /* Traffic is IPsec encrypted. */
+/* Bit 7 was previously used for IPsec tunnel ports. */
#endif /* openvswitch/tunnel.h */
lib_libopenvswitch_a_SOURCES = \
lib/aes128.c \
lib/aes128.h \
- lib/autopath.c \
- lib/autopath.h \
lib/backtrace.c \
lib/backtrace.h \
lib/bitmap.c \
+++ /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.
- */
-
-#include <config.h>
-
-#include "autopath.h"
-
-#include <inttypes.h>
-#include <stdlib.h>
-
-#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"
-#include "vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(autopath);
-
-void
-autopath_parse(struct ofpact_autopath *ap, const char *s_)
-{
- char *s;
- char *id_str, *dst, *save_ptr;
- uint16_t port;
-
- ofpact_init_AUTOPATH(ap);
-
- s = xstrdup(s_);
- save_ptr = NULL;
- id_str = strtok_r(s, ", ", &save_ptr);
- dst = strtok_r(NULL, ", ", &save_ptr);
-
- if (!dst) {
- ovs_fatal(0, "%s: not enough arguments to autopath action", s_);
- }
-
- if (!ofputil_port_from_string(id_str, &port)) {
- ovs_fatal(0, "%s: bad port number", s_);
- }
- ap->port = port;
-
- 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_, ap->dst.n_bits, 1u << ap->dst.n_bits);
- }
-
- free(s);
-}
-
-enum ofperr
-autopath_from_openflow(const struct nx_action_autopath *nap,
- struct ofpact_autopath *autopath)
-{
- 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);
-
- if (autopath->dst.n_bits < 16) {
- VLOG_WARN("at least 16 bit destination is required for autopath "
- "action.");
- return OFPERR_OFPBAC_BAD_ARGUMENT;
- }
-
- 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);
-}
+++ /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.
- */
-
-#ifndef AUTOPATH_H
-#define AUTOPATH_H 1
-
-#include <stdint.h>
-#include "ofp-errors.h"
-
-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_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 */
struct list bal_node; /* In bond_rebalance()'s 'bals' list. */
struct list entries; /* 'struct bond_entry's assigned here. */
uint64_t tx_bytes; /* Sum across 'tx_bytes' of entries. */
-
- /* BM_STABLE specific bonding info. */
- uint32_t stb_id; /* ID used for 'stb_slaves' ordering. */
};
/* A bond, that is, a set of network devices grouped to improve performance or
long long int next_rebalance; /* Next rebalancing time. */
bool send_learning_packets;
- /* BM_STABLE specific bonding info. */
- tag_type stb_tag; /* Tag associated with this bond. */
-
/* Legacy compatibility. */
long long int next_fake_iface_update; /* LLONG_MAX if disabled. */
*balance = BM_TCP;
} else if (!strcmp(s, bond_mode_to_string(BM_SLB))) {
*balance = BM_SLB;
- } else if (!strcmp(s, bond_mode_to_string(BM_STABLE))) {
- *balance = BM_STABLE;
} else if (!strcmp(s, bond_mode_to_string(BM_AB))) {
*balance = BM_AB;
} else {
return "balance-tcp";
case BM_SLB:
return "balance-slb";
- case BM_STABLE:
- return "stable";
case BM_AB:
return "active-backup";
}
bond = xzalloc(sizeof *bond);
hmap_init(&bond->slaves);
bond->no_slaves_tag = tag_create_random();
- bond->stb_tag = tag_create_random();
bond->next_fake_iface_update = LLONG_MAX;
bond_reconfigure(bond, s);
if (bond->balance != s->balance) {
bond->balance = s->balance;
revalidate = true;
-
- if (bond->balance == BM_STABLE) {
- VLOG_WARN_ONCE("Stable bond mode is deprecated and may be removed"
- " in February 2013. Please email"
- " dev@openvswitch.org with concerns.");
- }
}
if (bond->basis != s->basis) {
* bond. If 'slave_' already exists within 'bond' then this function
* reconfigures the existing slave.
*
- * 'stb_id' is used in BM_STABLE bonds to guarantee consistent slave choices
- * across restarts and distributed vswitch instances. It should be unique per
- * slave, and preferably consistent across restarts and reconfigurations.
- *
* 'netdev' must be the network device that 'slave_' represents. It is owned
* by the client, so the client must not close it before either unregistering
* 'slave_' or destroying 'bond'.
*/
void
-bond_slave_register(struct bond *bond, void *slave_, uint32_t stb_id,
- struct netdev *netdev)
+bond_slave_register(struct bond *bond, void *slave_, struct netdev *netdev)
{
struct bond_slave *slave = bond_slave_lookup(bond, slave_);
bond_enable_slave(slave, netdev_get_carrier(netdev), NULL);
}
- if (slave->stb_id != stb_id) {
- slave->stb_id = stb_id;
- bond->bond_revalidate = true;
- }
-
bond_slave_set_netdev__(slave, netdev);
free(slave->name);
}
if (bond->bond_revalidate) {
- bond->bond_revalidate = false;
+ struct bond_slave *slave;
+ bond->bond_revalidate = false;
bond_entry_reset(bond);
- if (bond->balance != BM_STABLE) {
- struct bond_slave *slave;
-
- HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
- tag_set_add(tags, slave->tag);
- }
- } else {
- tag_set_add(tags, bond->stb_tag);
+ HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
+ tag_set_add(tags, slave->tag);
}
tag_set_add(tags, bond->no_slaves_tag);
}
* exception is if we locked the learning table to avoid reflections on
* bond slaves. */
return BV_DROP_IF_MOVED;
-
- case BM_STABLE:
- return BV_ACCEPT;
}
NOT_REACHED();
{
struct bond_slave *slave = choose_output_slave(bond, flow, vlan, tags);
if (slave) {
- *tags |= bond->balance == BM_STABLE ? bond->stb_tag : slave->tag;
+ *tags |= slave->tag;
return slave->aux;
} else {
*tags |= bond->no_slaves_tag;
static void
bond_enable_slave(struct bond_slave *slave, bool enable, struct tag_set *tags)
{
- struct bond *bond = slave->bond;
slave->delay_expires = LLONG_MAX;
if (enable != slave->enabled) {
slave->enabled = enable;
if (!slave->enabled) {
- VLOG_WARN("interface %s: disabled", slave->name);
+ VLOG_INFO("interface %s: disabled", slave->name);
if (tags) {
tag_set_add(tags, slave->tag);
}
} else {
- VLOG_WARN("interface %s: enabled", slave->name);
+ VLOG_INFO("interface %s: enabled", slave->name);
slave->tag = tag_create_random();
}
-
- if (bond->balance == BM_STABLE) {
- bond->bond_revalidate = true;
- }
}
}
return &bond->hash[bond_hash(bond, flow, vlan) & BOND_MASK];
}
-/* This function uses Highest Random Weight hashing to choose an output slave.
- * This approach only reassigns a minimal number of flows when slaves are
- * enabled or disabled. Unfortunately, it has O(n) performance against the
- * number of slaves. There exist algorithms which are O(1), but have slightly
- * more complex implementations and require the use of memory. This may need
- * to be reimplemented if it becomes a performance bottleneck. */
-static struct bond_slave *
-choose_stb_slave(const struct bond *bond, uint32_t flow_hash)
-{
- struct bond_slave *best, *slave;
- uint32_t best_hash;
-
- best = NULL;
- best_hash = 0;
- HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
- if (slave->enabled) {
- uint32_t hash;
-
- hash = hash_2words(flow_hash, slave->stb_id);
- if (!best || hash > best_hash) {
- best = slave;
- best_hash = hash;
- }
- }
- }
-
- return best;
-}
-
static struct bond_slave *
choose_output_slave(const struct bond *bond, const struct flow *flow,
uint16_t vlan, tag_type *tags)
case BM_AB:
return bond->active_slave;
- case BM_STABLE:
- return choose_stb_slave(bond, bond_hash_tcp(flow, vlan, bond->basis));
-
case BM_TCP:
if (bond->lacp_status != LACP_NEGOTIATED) {
/* Must have LACP negotiations for TCP balanced bonds. */
}
/* Fall Through. */
case BM_SLB:
- if (!bond_is_balanced(bond)) {
- return choose_stb_slave(bond, bond_hash(bond, flow, vlan));
- }
e = lookup_bond_entry(bond, flow, vlan);
if (!e->slave || !e->slave->enabled) {
e->slave = CONTAINER_OF(hmap_random_node(&bond->slaves),
bond->send_learning_packets = true;
} else if (old_active_slave) {
- VLOG_WARN_RL(&rl, "bond %s: all interfaces disabled", bond->name);
+ VLOG_INFO_RL(&rl, "bond %s: all interfaces disabled", bond->name);
}
}
enum bond_mode {
BM_TCP, /* Transport Layer Load Balance. */
BM_SLB, /* Source Load Balance. */
- BM_STABLE, /* Stable. */
BM_AB /* Active Backup. */
};
void bond_destroy(struct bond *);
bool bond_reconfigure(struct bond *, const struct bond_settings *);
-void bond_slave_register(struct bond *, void *slave_,
- uint32_t stable_id, struct netdev *);
+void bond_slave_register(struct bond *, void *slave_, struct netdev *);
void bond_slave_set_netdev(struct bond *, void *slave_, struct netdev *);
void bond_slave_unregister(struct bond *, const void *slave);
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
static void destroy_table(struct classifier *, struct cls_table *);
+static void update_tables_after_insertion(struct classifier *,
+ struct cls_table *,
+ unsigned int new_priority);
+static void update_tables_after_removal(struct classifier *,
+ struct cls_table *,
+ unsigned int del_priority);
+
static struct cls_rule *find_match(const struct cls_table *,
const struct flow *);
static struct cls_rule *find_equal(struct cls_table *,
const struct miniflow *, uint32_t hash);
-static struct cls_rule *insert_rule(struct cls_table *, struct cls_rule *);
+static struct cls_rule *insert_rule(struct classifier *,
+ struct cls_table *, struct cls_rule *);
/* Iterates RULE over HEAD and all of the cls_rules on HEAD->list. */
#define FOR_EACH_RULE_IN_LIST(RULE, HEAD) \
{
cls->n_rules = 0;
hmap_init(&cls->tables);
+ list_init(&cls->tables_priority);
}
/* Destroys 'cls'. Rules within 'cls', if any, are not freed; this is the
table = insert_table(cls, &rule->match.mask);
}
- old_rule = insert_rule(table, rule);
+ old_rule = insert_rule(cls, table, rule);
if (!old_rule) {
table->n_table_rules++;
cls->n_rules++;
if (--table->n_table_rules == 0) {
destroy_table(cls, table);
+ } else {
+ update_tables_after_removal(cls, table, rule->priority);
}
-
cls->n_rules--;
}
struct cls_rule *best;
best = NULL;
- HMAP_FOR_EACH (table, hmap_node, &cls->tables) {
+ LIST_FOR_EACH (table, list_node, &cls->tables_priority) {
struct cls_rule *rule = find_match(table, flow);
- if (rule && (!best || rule->priority > best->priority)) {
+ if (rule) {
best = rule;
+ LIST_FOR_EACH_CONTINUE (table, list_node, &cls->tables_priority) {
+ if (table->max_priority <= best->priority) {
+ /* Tables in descending priority order,
+ * can not find anything better. */
+ return best;
+ }
+ rule = find_match(table, flow);
+ if (rule && rule->priority > best->priority) {
+ best = rule;
+ }
+ }
+ break;
}
}
return best;
return NULL;
}
+ /* Skip if there is no hope. */
+ if (target->priority > table->max_priority) {
+ return NULL;
+ }
+
head = find_equal(table, &target->match.flow,
miniflow_hash_in_minimask(&target->match.flow,
&target->match.mask, 0));
{
struct cls_table *table;
- HMAP_FOR_EACH (table, hmap_node, &cls->tables) {
+ /* Iterate tables in the descending max priority order. */
+ LIST_FOR_EACH (table, list_node, &cls->tables_priority) {
uint32_t storage[FLOW_U32S];
struct minimask mask;
struct cls_rule *head;
+ if (target->priority > table->max_priority) {
+ break; /* Can skip this and the rest of the tables. */
+ }
+
minimask_combine(&mask, &target->match.mask, &table->mask, storage);
HMAP_FOR_EACH (head, hmap_node, &table->rules) {
struct cls_rule *rule;
FOR_EACH_RULE_IN_LIST (rule, head) {
+ if (rule->priority < target->priority) {
+ break; /* Rules in descending priority order. */
+ }
if (rule->priority == target->priority
&& miniflow_equal_in_minimask(&target->match.flow,
&rule->match.flow, &mask)) {
hmap_init(&table->rules);
minimask_clone(&table->mask, mask);
hmap_insert(&cls->tables, &table->hmap_node, minimask_hash(mask, 0));
+ list_push_back(&cls->tables_priority, &table->list_node);
return table;
}
minimask_destroy(&table->mask);
hmap_remove(&cls->tables, &table->hmap_node);
hmap_destroy(&table->rules);
+ list_remove(&table->list_node);
free(table);
}
+/* This function performs the following updates for 'table' in 'cls' following
+ * the addition of a new rule with priority 'new_priority' to 'table':
+ *
+ * - Update 'table->max_priority' and 'table->max_count' if necessary.
+ *
+ * - Update 'table''s position in 'cls->tables_priority' if necessary.
+ *
+ * This function should only be called after adding a new rule, not after
+ * replacing a rule by an identical one or modifying a rule in-place. */
+static void
+update_tables_after_insertion(struct classifier *cls, struct cls_table *table,
+ unsigned int new_priority)
+{
+ if (new_priority == table->max_priority) {
+ ++table->max_count;
+ } else if (new_priority > table->max_priority) {
+ struct cls_table *iter;
+
+ table->max_priority = new_priority;
+ table->max_count = 1;
+
+ /* Possibly move 'table' earlier in the priority list. If we break out
+ * of the loop, then 'table' should be moved just after that 'iter'.
+ * If the loop terminates normally, then 'iter' will be the list head
+ * and we'll move table just after that (e.g. to the front of the
+ * list). */
+ iter = table;
+ LIST_FOR_EACH_REVERSE_CONTINUE (iter, list_node,
+ &cls->tables_priority) {
+ if (iter->max_priority >= table->max_priority) {
+ break;
+ }
+ }
+
+ /* Move 'table' just after 'iter' (unless it's already there). */
+ if (iter->list_node.next != &table->list_node) {
+ list_splice(iter->list_node.next,
+ &table->list_node, table->list_node.next);
+ }
+ }
+}
+
+/* This function performs the following updates for 'table' in 'cls' following
+ * the deletion of a rule with priority 'del_priority' from 'table':
+ *
+ * - Update 'table->max_priority' and 'table->max_count' if necessary.
+ *
+ * - Update 'table''s position in 'cls->tables_priority' if necessary.
+ *
+ * This function should only be called after removing a rule, not after
+ * replacing a rule by an identical one or modifying a rule in-place. */
+static void
+update_tables_after_removal(struct classifier *cls, struct cls_table *table,
+ unsigned int del_priority)
+{
+ struct cls_table *iter;
+
+ if (del_priority == table->max_priority && --table->max_count == 0) {
+ struct cls_rule *head;
+
+ table->max_priority = 0;
+ HMAP_FOR_EACH (head, hmap_node, &table->rules) {
+ if (head->priority > table->max_priority) {
+ table->max_priority = head->priority;
+ table->max_count = 1;
+ } else if (head->priority == table->max_priority) {
+ ++table->max_count;
+ }
+ }
+
+ /* Possibly move 'table' later in the priority list. If we break out
+ * of the loop, then 'table' should be moved just before that 'iter'.
+ * If the loop terminates normally, then 'iter' will be the list head
+ * and we'll move table just before that (e.g. to the back of the
+ * list). */
+ iter = table;
+ LIST_FOR_EACH_CONTINUE (iter, list_node, &cls->tables_priority) {
+ if (iter->max_priority <= table->max_priority) {
+ break;
+ }
+ }
+
+ /* Move 'table' just before 'iter' (unless it's already there). */
+ if (iter->list_node.prev != &table->list_node) {
+ list_splice(&iter->list_node,
+ &table->list_node, table->list_node.next);
+ }
+ }
+}
+
static struct cls_rule *
find_match(const struct cls_table *table, const struct flow *flow)
{
}
static struct cls_rule *
-insert_rule(struct cls_table *table, struct cls_rule *new)
+insert_rule(struct classifier *cls,
+ struct cls_table *table, struct cls_rule *new)
{
struct cls_rule *head;
+ struct cls_rule *old = NULL;
new->hmap_node.hash = miniflow_hash_in_minimask(&new->match.flow,
&new->match.mask, 0);
if (!head) {
hmap_insert(&table->rules, &new->hmap_node, new->hmap_node.hash);
list_init(&new->list);
- return NULL;
+ goto out;
} else {
/* Scan the list for the insertion point that will keep the list in
* order of decreasing priority. */
if (new->priority == rule->priority) {
list_replace(&new->list, &rule->list);
- return rule;
+ old = rule;
+ goto out;
} else {
list_insert(&rule->list, &new->list);
- return NULL;
+ goto out;
}
}
}
/* Insert 'new' at the end of the list. */
list_push_back(&head->list, &new->list);
- return NULL;
}
+
+ out:
+ if (!old) {
+ update_tables_after_insertion(cls, table, new->priority);
+ }
+ return old;
}
static struct cls_rule *
struct classifier {
int n_rules; /* Total number of rules. */
struct hmap tables; /* Contains "struct cls_table"s. */
+ struct list tables_priority; /* Tables in descending priority order */
};
/* A set of rules that all have the same fields wildcarded. */
struct cls_table {
struct hmap_node hmap_node; /* Within struct classifier 'tables' hmap. */
+ struct list list_node; /* Within classifier 'tables_priority_list' */
struct hmap rules; /* Contains "struct cls_rule"s. */
struct minimask mask; /* Wildcards for fields. */
int n_table_rules; /* Number of rules, including duplicates. */
+ unsigned int max_priority; /* Max priority of any rule in the table. */
+ unsigned int max_count; /* Count of max_priority rules. */
};
/* Returns true if 'table' is a "catch-all" table that will match every
case OVS_VPORT_TYPE_GRE64:
return "gre64";
- case OVS_VPORT_TYPE_CAPWAP:
- return "capwap";
-
case OVS_VPORT_TYPE_VXLAN:
return "vxlan";
+ case OVS_VPORT_TYPE_LISP:
+ return "lisp";
+
case OVS_VPORT_TYPE_UNSPEC:
case __OVS_VPORT_TYPE_MAX:
break;
return OVS_VPORT_TYPE_GRE64;
} else if (strstr(type, "gre")) {
return OVS_VPORT_TYPE_GRE;
- } else if (!strcmp(type, "capwap")) {
- return OVS_VPORT_TYPE_CAPWAP;
} else if (!strcmp(type, "vxlan")) {
return OVS_VPORT_TYPE_VXLAN;
+ } else if (!strcmp(type, "lisp")) {
+ return OVS_VPORT_TYPE_LISP;
} else {
return OVS_VPORT_TYPE_UNSPEC;
}
uint32_t *port_nop)
{
struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ const struct netdev_tunnel_config *tnl_cfg;
const char *name = netdev_vport_get_dpif_port(netdev);
const char *type = netdev_get_type(netdev);
struct dpif_linux_vport request, reply;
struct nl_sock *sock = NULL;
uint32_t upcall_pid;
struct ofpbuf *buf;
+ uint64_t options_stub[64 / 8];
+ struct ofpbuf options;
int error;
if (dpif->epoll_fd >= 0) {
netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false);
}
+ tnl_cfg = netdev_get_tunnel_config(netdev);
+ if (tnl_cfg && tnl_cfg->dst_port != 0) {
+ ofpbuf_use_stack(&options, options_stub, sizeof options_stub);
+ nl_msg_put_u16(&options, OVS_TUNNEL_ATTR_DST_PORT,
+ ntohs(tnl_cfg->dst_port));
+ request.options = options.data;
+ request.options_len = options.size;
+ }
+
request.port_no = *port_nop;
upcall_pid = sock ? nl_sock_pid(sock) : 0;
request.upcall_pid = &upcall_pid;
[OVS_PACKET_ATTR_KEY] = { .type = NL_A_NESTED },
/* OVS_PACKET_CMD_ACTION only. */
- [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_U64, .optional = true },
+ [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_UNSPEC, .optional = true },
};
struct ovs_header *ovs_header;
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])
- : 0);
+ upcall->userdata = a[OVS_PACKET_ATTR_USERDATA];
*dp_ifindex = ovs_header->dp_ifindex;
return 0;
bool create, struct dpif **);
static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *,
int queue_no, const struct flow *,
- uint64_t arg);
+ const struct nlattr *userdata);
static void dp_netdev_execute_actions(struct dp_netdev *,
struct ofpbuf *, struct flow *,
const struct nlattr *actions,
dp->n_hit++;
} else {
dp->n_missed++;
- dp_netdev_output_userspace(dp, packet, DPIF_UC_MISS, &key, 0);
+ dp_netdev_output_userspace(dp, packet, DPIF_UC_MISS, &key, NULL);
}
}
static int
dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
- int queue_no, const struct flow *flow, uint64_t arg)
+ int queue_no, const struct flow *flow,
+ const struct nlattr *userdata)
{
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;
-
- if (q->head - q->tail >= MAX_QUEUE_LEN) {
- dp->n_lost++;
- return ENOBUFS;
- }
+ if (q->head - q->tail < MAX_QUEUE_LEN) {
+ struct dp_netdev_upcall *u = &q->upcalls[q->head++ & QUEUE_MASK];
+ struct dpif_upcall *upcall = &u->upcall;
+ struct ofpbuf *buf = &u->buf;
+ size_t buf_size;
+
+ upcall->type = queue_no;
+
+ /* Allocate buffer big enough for everything. */
+ buf_size = ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size;
+ if (userdata) {
+ buf_size += NLA_ALIGN(userdata->nla_len);
+ }
+ ofpbuf_init(buf, buf_size);
- u = &q->upcalls[q->head++ & QUEUE_MASK];
+ /* Put ODP flow. */
+ odp_flow_key_from_flow(buf, flow, flow->in_port);
+ upcall->key = buf->data;
+ upcall->key_len = buf->size;
- buf = &u->buf;
- ofpbuf_init(buf, ODPUTIL_FLOW_KEY_BYTES + 2 + packet->size);
- odp_flow_key_from_flow(buf, flow, flow->in_port);
- key_len = buf->size;
- ofpbuf_pull(buf, key_len);
- ofpbuf_reserve(buf, 2);
- ofpbuf_put(buf, packet->data, packet->size);
+ /* Put userdata. */
+ if (userdata) {
+ upcall->userdata = ofpbuf_put(buf, userdata,
+ NLA_ALIGN(userdata->nla_len));
+ }
- upcall = &u->upcall;
- upcall->type = queue_no;
- upcall->packet = buf;
- upcall->key = buf->base;
- upcall->key_len = key_len;
- upcall->userdata = arg;
+ /* Put packet.
+ *
+ * We adjust 'data' and 'size' in 'buf' so that only the packet itself
+ * is visible in 'upcall->packet'. The ODP flow and (if present)
+ * userdata become part of the headroom. */
+ ofpbuf_put_zeros(buf, 2);
+ buf->data = ofpbuf_put(buf, packet->data, packet->size);
+ buf->size = packet->size;
+ upcall->packet = buf;
- return 0;
+ return 0;
+ } else {
+ dp->n_lost++;
+ return ENOBUFS;
+ }
}
static void
struct ofpbuf *packet, struct flow *key,
const struct nlattr *a)
{
- const struct nlattr *userdata_attr;
- uint64_t userdata;
+ const struct nlattr *userdata;
- userdata_attr = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
- userdata = userdata_attr ? nl_attr_get_u64(userdata_attr) : 0;
+ userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION, key, userdata);
}
packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst);
break;
+ case OVS_KEY_ATTR_MPLS:
+ set_mpls_lse(packet, nl_attr_get_be32(a));
+ break;
+
case OVS_KEY_ATTR_UNSPEC:
case OVS_KEY_ATTR_ENCAP:
case OVS_KEY_ATTR_ETHERTYPE:
eth_pop_vlan(packet);
break;
+ case OVS_ACTION_ATTR_PUSH_MPLS: {
+ const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
+ push_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse);
+ break;
+ }
+
+ case OVS_ACTION_ATTR_POP_MPLS:
+ pop_mpls(packet, nl_attr_get_be16(a));
+ break;
+
case OVS_ACTION_ATTR_SET:
execute_set_action(packet, nl_attr_get(a));
break;
/* A packet passed up from the datapath to userspace.
*
- * If 'key' or 'actions' is nonnull, then it points into data owned by
- * 'packet', so their memory cannot be freed separately. (This is hardly a
- * great way to do things but it works out OK for the dpif providers and
- * clients that exist so far.)
+ * If 'key', 'actions', or 'userdata' is nonnull, then it points into data
+ * owned by 'packet', so their memory cannot be freed separately. (This is
+ * hardly a great way to do things but it works out OK for the dpif providers
+ * and clients that exist so far.)
*/
struct dpif_upcall {
/* All types. */
size_t key_len; /* Length of 'key' in bytes. */
/* DPIF_UC_ACTION only. */
- uint64_t userdata; /* Argument to OVS_ACTION_ATTR_USERSPACE. */
+ struct nlattr *userdata; /* Argument to OVS_ACTION_ATTR_USERSPACE. */
};
int dpif_recv_set(struct dpif *, bool enable);
return ofpbuf_try_pull(packet, sizeof(struct icmp6_hdr));
}
+static void
+parse_mpls(struct ofpbuf *b, struct flow *flow)
+{
+ struct mpls_hdr *mh;
+
+ while ((mh = ofpbuf_try_pull(b, sizeof *mh))) {
+ if (flow->mpls_depth++ == 0) {
+ flow->mpls_lse = mh->mpls_lse;
+ }
+ if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
+ break;
+ }
+ }
+}
+
static void
parse_vlan(struct ofpbuf *b, struct flow *flow)
{
}
ofpbuf_pull(b, sizeof *llc);
- return llc->snap.snap_type;
+
+ if (ntohs(llc->snap.snap_type) >= ETH_TYPE_MIN) {
+ return llc->snap.snap_type;
+ }
+
+ return htons(FLOW_DL_TYPE_NONE);
}
static int
*
* - packet->l2 to the start of the Ethernet header.
*
+ * - packet->l2_5 to the start of the MPLS shim header.
+ *
* - packet->l3 to just past the Ethernet header, or just past the
* vlan_header if one is present, to the first byte of the payload of the
* Ethernet frame.
flow->skb_priority = skb_priority;
flow->skb_mark = skb_mark;
- packet->l2 = b.data;
- packet->l3 = NULL;
- packet->l4 = NULL;
- packet->l7 = NULL;
+ packet->l2 = b.data;
+ packet->l2_5 = NULL;
+ packet->l3 = NULL;
+ packet->l4 = NULL;
+ packet->l7 = NULL;
if (b.size < sizeof *eth) {
return;
}
flow->dl_type = parse_ethertype(&b);
+ /* Parse mpls, copy l3 ttl. */
+ if (eth_type_mpls(flow->dl_type)) {
+ packet->l2_5 = b.data;
+ parse_mpls(&b, flow);
+ }
+
packet->l3 = b.data;
flow_extract_l3_onwards(packet, flow, flow->dl_type);
}
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
fmd->tun_id = flow->tunnel.tun_id;
fmd->metadata = flow->metadata;
flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
}
+/* Sets the MPLS Label that 'flow' matches to 'label', which is interpreted
+ * as an OpenFlow 1.1 "mpls_label" value. */
+void
+flow_set_mpls_label(struct flow *flow, ovs_be32 label)
+{
+ set_mpls_lse_label(&flow->mpls_lse, label);
+}
+
+/* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the
+ * range 0...7. */
+void
+flow_set_mpls_tc(struct flow *flow, uint8_t tc)
+{
+ set_mpls_lse_tc(&flow->mpls_lse, tc);
+}
+
+/* Sets the MPLS BOS bit that 'flow' matches to which should be 0 or 1. */
+void
+flow_set_mpls_bos(struct flow *flow, uint8_t bos)
+{
+ set_mpls_lse_bos(&flow->mpls_lse, bos);
+}
+
/* Puts into 'b' a packet that flow_extract() would parse as having the given
* 'flow'.
*
void
flow_compose(struct ofpbuf *b, const struct flow *flow)
{
+ ovs_be16 inner_dl_type;
+
+ inner_dl_type = flow_innermost_dl_type(flow);
eth_compose(b, flow->dl_dst, flow->dl_src, ntohs(flow->dl_type), 0);
if (flow->dl_type == htons(FLOW_DL_TYPE_NONE)) {
struct eth_header *eth = b->l2;
eth_push_vlan(b, flow->vlan_tci);
}
- if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ if (inner_dl_type == htons(ETH_TYPE_IP)) {
struct ip_header *ip;
b->l3 = ip = ofpbuf_put_zeros(b, sizeof *ip);
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)) {
+ } else if (inner_dl_type == htons(ETH_TYPE_IPV6)) {
/* XXX */
- } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
- flow->dl_type == htons(ETH_TYPE_RARP)) {
+ } else if (inner_dl_type == htons(ETH_TYPE_ARP) ||
+ inner_dl_type == htons(ETH_TYPE_RARP)) {
struct arp_eth_header *arp;
b->l3 = arp = ofpbuf_put_zeros(b, sizeof *arp);
memcpy(arp->ar_tha, flow->arp_tha, ETH_ADDR_LEN);
}
}
+
+ if (eth_type_mpls(flow->dl_type)) {
+ b->l2_5 = b->l3;
+ push_mpls(b, flow->dl_type, flow->mpls_lse);
+ }
}
\f
/* Compressed flow. */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* 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 18
+#define FLOW_WC_SEQ 19
#define FLOW_N_REGS 8
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
unless in DPIF code, in which case it
is the datapath port number. */
uint32_t skb_mark; /* Packet mark. */
+ ovs_be32 mpls_lse; /* MPLS label stack entry. */
+ uint16_t mpls_depth; /* Depth of MPLS stack. */
ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
ovs_be16 dl_type; /* Ethernet frame type. */
+ ovs_be16 encap_dl_type; /* MPLS encapsulated Ethernet frame type */
ovs_be16 tp_src; /* TCP/UDP source port. */
ovs_be16 tp_dst; /* TCP/UDP destination port. */
uint8_t dl_src[6]; /* Ethernet source address. */
#define FLOW_U32S (sizeof(struct flow) / 4)
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 152 &&
- FLOW_WC_SEQ == 18);
+BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 160 &&
+ FLOW_WC_SEQ == 19);
/* Represents the metadata fields of struct flow. */
struct flow_metadata {
const struct flow_tnl *, uint16_t in_port, struct flow *);
void flow_extract_l3_onwards(struct ofpbuf *, struct flow *,
ovs_be16 dl_type);
+
+/* Returns the innermost dl_type.
+ * If there's an outer and an inner type then the inner type is returned.
+ * Otherwise, if there is only one type then it is returned. */
+static inline ovs_be16
+flow_innermost_dl_type(const struct flow *flow)
+{
+ return (flow->encap_dl_type == htons(0)
+ ? flow->dl_type
+ : flow->encap_dl_type);
+}
+
void flow_zero_wildcards(struct flow *, const struct flow_wildcards *);
void flow_get_metadata(const struct flow *, struct flow_metadata *);
void flow_set_vlan_vid(struct flow *, ovs_be16 vid);
void flow_set_vlan_pcp(struct flow *, uint8_t pcp);
+void flow_set_mpls_label(struct flow *flow, ovs_be32 label);
+void flow_set_mpls_ttl(struct flow *flow, uint8_t ttl);
+void flow_set_mpls_tc(struct flow *flow, uint8_t tc);
+void flow_set_mpls_bos(struct flow *flow, uint8_t stack);
+
void flow_compose(struct ofpbuf *, const struct flow *);
static inline int
for (ASSIGN_CONTAINER(ITER, (LIST)->next, MEMBER); \
&(ITER)->MEMBER != (LIST); \
ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER))
+#define LIST_FOR_EACH_CONTINUE(ITER, MEMBER, LIST) \
+ for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER); \
+ &(ITER)->MEMBER != (LIST); \
+ ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER))
#define LIST_FOR_EACH_REVERSE(ITER, MEMBER, LIST) \
for (ASSIGN_CONTAINER(ITER, (LIST)->prev, MEMBER); \
&(ITER)->MEMBER != (LIST); \
ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER))
+#define LIST_FOR_EACH_REVERSE_CONTINUE(ITER, MEMBER, LIST) \
+ for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER); \
+ &(ITER)->MEMBER != (LIST); \
+ ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER))
#define LIST_FOR_EACH_SAFE(ITER, NEXT, MEMBER, LIST) \
for (ASSIGN_CONTAINER(ITER, (LIST)->next, MEMBER); \
(&(ITER)->MEMBER != (LIST) \
match->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK);
}
+/* Modifies 'match' so that the MPLS label is wildcarded. */
+void
+match_set_any_mpls_label(struct match *match)
+{
+ match->wc.masks.mpls_lse &= ~htonl(MPLS_LABEL_MASK);
+ flow_set_mpls_label(&match->flow, htonl(0));
+}
+
+/* Modifies 'match' so that it matches only packets with an MPLS header whose
+ * label equals the low 20 bits of 'mpls_label'. */
+void
+match_set_mpls_label(struct match *match, ovs_be32 mpls_label)
+{
+ match->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK);
+ flow_set_mpls_label(&match->flow, mpls_label);
+}
+
+/* Modifies 'match' so that the MPLS TC is wildcarded. */
+void
+match_set_any_mpls_tc(struct match *match)
+{
+ match->wc.masks.mpls_lse &= ~htonl(MPLS_TC_MASK);
+ flow_set_mpls_tc(&match->flow, 0);
+}
+
+/* Modifies 'match' so that it matches only packets with an MPLS header whose
+ * Traffic Class equals the low 3 bits of 'mpls_tc'. */
+void
+match_set_mpls_tc(struct match *match, uint8_t mpls_tc)
+{
+ match->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK);
+ flow_set_mpls_tc(&match->flow, mpls_tc);
+}
+
+/* Modifies 'match' so that the MPLS stack flag is wildcarded. */
+void
+match_set_any_mpls_bos(struct match *match)
+{
+ match->wc.masks.mpls_lse &= ~htonl(MPLS_BOS_MASK);
+ flow_set_mpls_bos(&match->flow, 0);
+}
+
+/* Modifies 'match' so that it matches only packets with an MPLS header whose
+ * Stack Flag equals the lower bit of 'mpls_bos' */
+void
+match_set_mpls_bos(struct match *match, uint8_t mpls_bos)
+{
+ match->wc.masks.mpls_lse |= htonl(MPLS_BOS_MASK);
+ flow_set_mpls_bos(&match->flow, mpls_bos);
+}
+
void
match_set_tp_src(struct match *match, ovs_be16 tp_src)
{
}
}
+static void
+flow_format_mpls(const struct flow *flow, struct ds *s)
+{
+ if (flow->dl_type == htons(ETH_TYPE_MPLS)) {
+ ds_put_cstr(s, "mpls");
+ } else if (flow->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ ds_put_cstr(s, "mplsm");
+ } else {
+ return;
+ }
+
+ ds_put_format(s, "(label:%"PRIu32",tc:%d,ttl:%d,bos:%d),",
+ mpls_lse_to_label(flow->mpls_lse),
+ mpls_lse_to_tc(flow->mpls_lse),
+ mpls_lse_to_ttl(flow->mpls_lse),
+ mpls_lse_to_bos(flow->mpls_lse));
+}
+
/* Appends a string representation of 'match' to 's'. If 'priority' is
* different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
void
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%u,", priority);
ds_put_cstr(s, "arp,");
} else if (f->dl_type == htons(ETH_TYPE_RARP)) {
ds_put_cstr(s, "rarp,");
+ } else if (f->mpls_depth) {
+ flow_format_mpls(f, s);
} else {
skip_type = false;
}
if (wc->masks.nw_ttl) {
ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl);
}
+ if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+ ds_put_format(s, "mpls_label=%"PRIu32",",
+ mpls_lse_to_label(f->mpls_lse));
+ }
+ if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) {
+ ds_put_format(s, "mpls_tc=%"PRIu8",",
+ mpls_lse_to_tc(f->mpls_lse));
+ }
+ if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
+ ds_put_format(s, "mpls_bos=%"PRIu8",",
+ mpls_lse_to_bos(f->mpls_lse));
+ }
switch (wc->masks.nw_frag) {
case FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER:
ds_put_format(s, "nw_frag=%s,",
void match_set_vlan_vid_masked(struct match *, ovs_be16 vid, ovs_be16 mask);
void match_set_any_pcp(struct match *);
void match_set_dl_vlan_pcp(struct match *, uint8_t);
+void match_set_any_mpls_label(struct match *);
+void match_set_mpls_label(struct match *, ovs_be32);
+void match_set_any_mpls_tc(struct match *);
+void match_set_mpls_tc(struct match *, uint8_t);
+void match_set_any_mpls_bos(struct match *);
+void match_set_mpls_bos(struct match *, uint8_t);
void match_set_tp_src(struct match *, ovs_be16);
void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask);
void match_set_tp_dst(struct match *, ovs_be16);
OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP",
},
+ /* ## ---- ## */
+ /* ## L2.5 ## */
+ /* ## ---- ## */
+ {
+ MFF_MPLS_LABEL, "mpls_label", NULL,
+ 4, 20,
+ MFM_NONE,
+ MFS_DECIMAL,
+ MFP_MPLS,
+ true,
+ OXM_OF_MPLS_LABEL, "OXM_OF_MPLS_LABEL",
+ OXM_OF_MPLS_LABEL, "OXM_OF_MPLS_LABEL",
+ }, {
+ MFF_MPLS_TC, "mpls_tc", NULL,
+ 1, 3,
+ MFM_NONE,
+ MFS_DECIMAL,
+ MFP_MPLS,
+ true,
+ OXM_OF_MPLS_TC, "OXM_OF_MPLS_TC",
+ OXM_OF_MPLS_TC, "OXM_OF_MPLS_TC",
+ }, {
+ MFF_MPLS_BOS, "mpls_bos", NULL,
+ 1, 1,
+ MFM_NONE,
+ MFS_DECIMAL,
+ MFP_MPLS,
+ false,
+ OXM_OF_MPLS_BOS, "OXM_OF_MPLS_BOS",
+ OXM_OF_MPLS_BOS, "OXM_OF_MPLS_BOS",
+ },
+
/* ## -- ## */
/* ## L3 ## */
/* ## -- ## */
case MFF_VLAN_PCP:
return !(wc->masks.vlan_tci & htons(VLAN_PCP_MASK));
+ case MFF_MPLS_LABEL:
+ return !(wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK));
+ case MFF_MPLS_TC:
+ return !(wc->masks.mpls_lse & htonl(MPLS_TC_MASK));
+ case MFF_MPLS_BOS:
+ return !(wc->masks.mpls_lse & htonl(MPLS_BOS_MASK));
+
case MFF_IPV4_SRC:
return !wc->masks.nw_src;
case MFF_IPV4_DST:
return flow->dl_type == htons(ETH_TYPE_IPV6);
case MFP_VLAN_VID:
return (flow->vlan_tci & htons(VLAN_CFI)) != 0;
+ case MFP_MPLS:
+ return eth_type_mpls(flow->dl_type);
case MFP_IP_ANY:
return is_ip_any(flow);
case MFF_IPV6_LABEL:
return !(value->be32 & ~htonl(IPV6_LABEL_MASK));
+ case MFF_MPLS_LABEL:
+ return !(value->be32 & ~htonl(MPLS_LABEL_MASK >> MPLS_LABEL_SHIFT));
+
+ case MFF_MPLS_TC:
+ return !(value->u8 & ~(MPLS_TC_MASK >> MPLS_TC_SHIFT));
+
+ case MFF_MPLS_BOS:
+ return !(value->u8 & ~(MPLS_BOS_MASK >> MPLS_BOS_SHIFT));
+
case MFF_N_IDS:
default:
NOT_REACHED();
value->u8 = vlan_tci_to_pcp(flow->vlan_tci);
break;
+ case MFF_MPLS_LABEL:
+ value->be32 = htonl(mpls_lse_to_label(flow->mpls_lse));
+ break;
+
+ case MFF_MPLS_TC:
+ value->u8 = mpls_lse_to_tc(flow->mpls_lse);
+ break;
+
+ case MFF_MPLS_BOS:
+ value->u8 = mpls_lse_to_bos(flow->mpls_lse);
+ break;
+
case MFF_IPV4_SRC:
value->be32 = flow->nw_src;
break;
match_set_dl_vlan_pcp(match, value->u8);
break;
+ case MFF_MPLS_LABEL:
+ match_set_mpls_label(match, value->be32);
+ break;
+
+ case MFF_MPLS_TC:
+ match_set_mpls_tc(match, value->u8);
+ break;
+
+ case MFF_MPLS_BOS:
+ match_set_mpls_bos(match, value->u8);
+ break;
+
case MFF_IPV4_SRC:
match_set_nw_src(match, value->be32);
break;
flow_set_vlan_pcp(flow, value->u8);
break;
+ case MFF_MPLS_LABEL:
+ flow_set_mpls_label(flow, value->be32);
+ break;
+
+ case MFF_MPLS_TC:
+ flow_set_mpls_tc(flow, value->u8);
+ break;
+
+ case MFF_MPLS_BOS:
+ flow_set_mpls_bos(flow, value->u8);
+ break;
+
case MFF_IPV4_SRC:
flow->nw_src = value->be32;
break;
case MFF_METADATA:
match_set_metadata_masked(match, htonll(0), htonll(0));
+ break;
case MFF_IN_PORT:
match->flow.in_port = 0;
match_set_any_pcp(match);
break;
+ case MFF_MPLS_LABEL:
+ match_set_any_mpls_label(match);
+ break;
+
+ case MFF_MPLS_TC:
+ match_set_any_mpls_tc(match);
+ break;
+
+ case MFF_MPLS_BOS:
+ match_set_any_mpls_bos(match);
+ break;
+
case MFF_IPV4_SRC:
case MFF_ARP_SPA:
match_set_nw_src_masked(match, htonl(0), htonl(0));
case MFF_DL_VLAN:
case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
+ case MFF_MPLS_LABEL:
+ case MFF_MPLS_TC:
+ case MFF_MPLS_BOS:
case MFF_IP_PROTO:
case MFF_IP_TTL:
case MFF_IP_DSCP:
value->u8 &= 0x07;
break;
+ case MFF_MPLS_LABEL:
+ value->be32 &= htonl(MPLS_LABEL_MASK >> MPLS_LABEL_SHIFT);
+ break;
+
+ case MFF_MPLS_TC:
+ value->u8 &= MPLS_TC_MASK >> MPLS_TC_SHIFT;
+ break;
+
+ case MFF_MPLS_BOS:
+ value->u8 &= MPLS_BOS_MASK >> MPLS_BOS_SHIFT;
+ break;
+
case MFF_N_IDS:
default:
NOT_REACHED();
}
static void
-mf_format_frag_string(const uint8_t *valuep, const uint8_t *maskp,
- struct ds *s)
+mf_format_frag_string(uint8_t value, uint8_t mask, struct ds *s)
{
const struct frag_handling *h;
- uint8_t value = *valuep;
- uint8_t mask = *maskp;
- value &= mask;
mask &= FLOW_NW_FRAG_MASK;
+ value &= mask;
for (h = all_frags; h < &all_frags[ARRAY_SIZE(all_frags)]; h++) {
if (value == h->value && mask == h->mask) {
break;
case MFS_FRAG:
- mf_format_frag_string(&value->u8, &mask->u8, s);
+ mf_format_frag_string(value->u8, mask ? mask->u8 : UINT8_MAX, s);
break;
case MFS_TNL_FLAGS:
MFF_DL_VLAN_PCP, /* u8 (OpenFlow 1.0 compatibility) */
MFF_VLAN_PCP, /* be16 (OpenFlow 1.2 compatibility) */
+ /* L2.5 */
+ MFF_MPLS_LABEL, /* be32 */
+ MFF_MPLS_TC, /* u8 */
+ MFF_MPLS_BOS, /* u8 */
+
/* L3. */
MFF_IPV4_SRC, /* be32 */
MFF_IPV4_DST, /* be32 */
MFP_IPV6,
MFP_IP_ANY,
+ /* L2.5 requirements. */
+ MFP_MPLS,
+
/* L2+L3 requirements. */
MFP_TCP, /* On IPv4 or IPv6. */
MFP_UDP, /* On IPv4 or IPv6. */
* - "dl_vlan_pcp" is 1 byte but only 3 bits.
* - "is_frag" is 1 byte but only 2 bits.
* - "ipv6_label" is 4 bytes but only 20 bits.
+ * - "mpls_label" is 4 bytes but only 20 bits.
+ * - "mpls_tc" is 1 byte but only 3 bits.
+ * - "mpls_bos" is 1 byte but only 1 bit.
*/
unsigned int n_bytes; /* Width of the field in bytes. */
unsigned int n_bits; /* Number of significant bits in field. */
return 0;
}
+static int
+netdev_dummy_send(struct netdev *netdev, const void *buffer OVS_UNUSED,
+ size_t size)
+{
+ struct netdev_dev_dummy *dev =
+ netdev_dev_dummy_cast(netdev_get_dev(netdev));
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += size;
+
+ return 0;
+}
+
static int
netdev_dummy_set_etheraddr(struct netdev *netdev,
const uint8_t mac[ETH_ADDR_LEN])
netdev_dummy_recv_wait,
netdev_dummy_drain,
- NULL, /* send */
+ netdev_dummy_send, /* send */
NULL, /* send_wait */
netdev_dummy_set_etheraddr,
return;
}
+ dummy_dev->stats.rx_packets++;
+ dummy_dev->stats.rx_bytes += packet->size;
+
n_listeners = 0;
LIST_FOR_EACH (dev, node, &dummy_dev->devs) {
if (dev->listening) {
if (sock == INT_MIN) {
sock = socket(AF_PACKET, SOCK_RAW, 0);
if (sock >= 0) {
- set_nonblocking(sock);
+ int error = set_nonblocking(sock);
+ if (error) {
+ close(sock);
+ sock = -error;
+ }
} else {
sock = -errno;
VLOG_ERR("failed to create packet socket: %s", strerror(errno));
/* Default to the OTV port, per the VXLAN IETF draft. */
#define VXLAN_DST_PORT 8472
+#define LISP_DST_PORT 4341
+
#define DEFAULT_TTL 64
struct netdev_dev_vport {
static int netdev_vport_create(const struct netdev_class *, const char *,
struct netdev_dev **);
static int get_patch_config(struct netdev_dev *, struct smap *args);
+static int get_tunnel_config(struct netdev_dev *, struct smap *args);
static void netdev_vport_poll_notify(struct netdev_dev_vport *);
static bool
return class->get_config == get_patch_config;
}
+static bool
+netdev_vport_needs_dst_port(const struct netdev_dev *dev)
+{
+ const struct netdev_class *class = netdev_dev_get_class(dev);
+ const char *type = netdev_dev_get_type(dev);
+
+ return (class->get_config == get_tunnel_config &&
+ (!strcmp("vxlan", type) || !strcmp("lisp", type)));
+}
+
const char *
netdev_vport_get_dpif_port(const struct netdev *netdev)
{
const struct netdev_class *class = netdev_dev_get_class(dev);
const char *dpif_port;
- dpif_port = (is_vport_class(class)
- ? vport_class_cast(class)->dpif_port
- : NULL);
+ if (netdev_vport_needs_dst_port(dev)) {
+ const struct netdev_dev_vport *vport = netdev_vport_get_dev(netdev);
+ const char *type = netdev_dev_get_type(dev);
+ static char dpif_port_combined[IFNAMSIZ];
+
+ /*
+ * Note: IFNAMSIZ is 16 bytes long. The maximum length of a VXLAN
+ * or LISP port name below is 15 or 14 bytes respectively. Still,
+ * assert here on the size of strlen(type) in case that changes
+ * in the future.
+ */
+ ovs_assert(strlen(type) + 10 < IFNAMSIZ);
+ snprintf(dpif_port_combined, IFNAMSIZ, "%s_sys_%d", type,
+ ntohs(vport->tnl_cfg.dst_port));
+ return dpif_port_combined;
+ } else {
+ dpif_port = (is_vport_class(class)
+ ? vport_class_cast(class)->dpif_port
+ : NULL);
+ }
+
return dpif_port ? dpif_port : netdev_get_name(netdev);
}
ipsec_mech_set = false;
memset(&tnl_cfg, 0, sizeof tnl_cfg);
- if (!strcmp(type, "capwap")) {
- VLOG_WARN_ONCE("CAPWAP tunnel support is deprecated.");
- }
-
- needs_dst_port = !strcmp(type, "vxlan");
+ needs_dst_port = netdev_vport_needs_dst_port(dev_);
tnl_cfg.ipsec = strstr(type, "ipsec");
tnl_cfg.dont_fragment = true;
}
/* Add a default destination port for VXLAN if none specified. */
- if (needs_dst_port && !tnl_cfg.dst_port) {
+ if (!strcmp(type, "vxlan") && !tnl_cfg.dst_port) {
tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
}
+ /* Add a default destination port for LISP if none specified. */
+ if (!strcmp(type, "lisp") && !tnl_cfg.dst_port) {
+ tnl_cfg.dst_port = htons(LISP_DST_PORT);
+ }
+
if (tnl_cfg.ipsec) {
static pid_t pid = 0;
if (pid <= 0) {
TUNNEL_CLASS("ipsec_gre", "gre_system"),
TUNNEL_CLASS("gre64", "gre64_system"),
TUNNEL_CLASS("ipsec_gre64", "gre64_system"),
- TUNNEL_CLASS("capwap", "capwap_system"),
- TUNNEL_CLASS("vxlan", "vxlan_system")
+ TUNNEL_CLASS("vxlan", "vxlan_system"),
+ TUNNEL_CLASS("lisp", "lisp_system")
};
int i;
rcvbuf = 1024 * 1024;
if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUFFORCE,
&rcvbuf, sizeof rcvbuf)) {
- VLOG_WARN_RL(&rl, "setting %d-byte socket receive buffer failed (%s)",
- rcvbuf, strerror(errno));
+ /* Only root can use SO_RCVBUFFORCE. Everyone else gets EPERM.
+ * Warn only if the failure is therefore unexpected. */
+ if (errno != EPERM || !getuid()) {
+ VLOG_WARN_RL(&rl, "setting %d-byte socket receive buffer failed "
+ "(%s)", rcvbuf, strerror(errno));
+ }
}
retval = get_socket_rcvbuf(sock->fd);
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
/* Metadata. */
if (match->wc.masks.in_port) {
match->wc.masks.vlan_tci);
}
+ /* MPLS. */
+ if (eth_type_mpls(flow->dl_type)) {
+ if (match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK)) {
+ nxm_put_8(b, OXM_OF_MPLS_TC, mpls_lse_to_tc(flow->mpls_lse));
+ }
+
+ if (match->wc.masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
+ nxm_put_8(b, OXM_OF_MPLS_BOS, mpls_lse_to_bos(flow->mpls_lse));
+ }
+
+ if (match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+ nxm_put_32(b, OXM_OF_MPLS_LABEL,
+ htonl(mpls_lse_to_label(flow->mpls_lse)));
+ }
+ }
+
/* L3. */
if (flow->dl_type == htons(ETH_TYPE_IP)) {
/* IP. */
case OVS_ACTION_ATTR_USERSPACE: return -2;
case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
case OVS_ACTION_ATTR_POP_VLAN: return 0;
+ case OVS_ACTION_ATTR_PUSH_MPLS: return sizeof(struct ovs_action_push_mpls);
+ case OVS_ACTION_ATTR_POP_MPLS: return sizeof(ovs_be16);
case OVS_ACTION_ATTR_SET: return -2;
case OVS_ACTION_ATTR_SAMPLE: return -2;
case OVS_KEY_ATTR_ICMPV6: return "icmpv6";
case OVS_KEY_ATTR_ARP: return "arp";
case OVS_KEY_ATTR_ND: return "nd";
+ case OVS_KEY_ATTR_MPLS: return "mpls";
case __OVS_KEY_ATTR_MAX:
default:
{
static const struct nl_policy ovs_userspace_policy[] = {
[OVS_USERSPACE_ATTR_PID] = { .type = NL_A_U32 },
- [OVS_USERSPACE_ATTR_USERDATA] = { .type = NL_A_U64, .optional = true },
+ [OVS_USERSPACE_ATTR_USERDATA] = { .type = NL_A_UNSPEC,
+ .optional = true },
};
struct nlattr *a[ARRAY_SIZE(ovs_userspace_policy)];
+ const struct nlattr *userdata_attr;
if (!nl_parse_nested(attr, ovs_userspace_policy, a, ARRAY_SIZE(a))) {
ds_put_cstr(ds, "userspace(error)");
ds_put_format(ds, "userspace(pid=%"PRIu32,
nl_attr_get_u32(a[OVS_USERSPACE_ATTR_PID]));
- if (a[OVS_USERSPACE_ATTR_USERDATA]) {
+ userdata_attr = a[OVS_USERSPACE_ATTR_USERDATA];
+ if (userdata_attr && nl_attr_get_size(userdata_attr) == sizeof(uint64_t)) {
uint64_t userdata = nl_attr_get_u64(a[OVS_USERSPACE_ATTR_USERDATA]);
union user_action_cookie cookie;
ds_put_format(ds, ",userdata=0x%"PRIx64, userdata);
break;
}
+ } else if (userdata_attr) {
+ const uint8_t *userdata = nl_attr_get(userdata_attr);
+ size_t len = nl_attr_get_size(userdata_attr);
+ size_t i;
+
+ ds_put_format(ds, ",userdata(");
+ for (i = 0; i < len; i++) {
+ ds_put_format(ds, "%02x", userdata[i]);
+ }
+ ds_put_char(ds, ')');
}
ds_put_char(ds, ')');
}
}
+static void
+format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse)
+{
+ ds_put_format(ds, "label=%"PRIu32",tc=%d,ttl=%d,bos=%d",
+ mpls_lse_to_label(mpls_lse),
+ mpls_lse_to_tc(mpls_lse),
+ mpls_lse_to_ttl(mpls_lse),
+ mpls_lse_to_bos(mpls_lse));
+}
+
static void
format_odp_action(struct ds *ds, const struct nlattr *a)
{
case OVS_ACTION_ATTR_POP_VLAN:
ds_put_cstr(ds, "pop_vlan");
break;
+ case OVS_ACTION_ATTR_PUSH_MPLS: {
+ const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
+ ds_put_cstr(ds, "push_mpls(");
+ format_mpls_lse(ds, mpls->mpls_lse);
+ ds_put_format(ds, ",eth_type=0x%"PRIx16")", ntohs(mpls->mpls_ethertype));
+ break;
+ }
+ case OVS_ACTION_ATTR_POP_MPLS: {
+ ovs_be16 ethertype = nl_attr_get_be16(a);
+ ds_put_format(ds, "pop_mpls(eth_type=0x%"PRIx16")", ntohs(ethertype));
+ break;
+ }
case OVS_ACTION_ATTR_SAMPLE:
format_odp_sample_action(ds, a);
break;
int n = -1;
if (sscanf(s, "userspace(pid=%lli)%n", &pid, &n) > 0 && n > 0) {
- odp_put_userspace_action(pid, NULL, actions);
+ odp_put_userspace_action(pid, NULL, 0, actions);
return n;
} else if (sscanf(s, "userspace(pid=%lli,sFlow(vid=%i,"
"pcp=%i,output=%lli))%n",
cookie.type = USER_ACTION_COOKIE_SFLOW;
cookie.sflow.vlan_tci = htons(tci);
cookie.sflow.output = output;
- odp_put_userspace_action(pid, &cookie, actions);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie, actions);
return n;
} else if (sscanf(s, "userspace(pid=%lli,slow_path%n", &pid, &n) > 0
&& n > 0) {
}
n++;
- odp_put_userspace_action(pid, &cookie, actions);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie, actions);
return n;
} else if (sscanf(s, "userspace(pid=%lli,userdata="
"%31[x0123456789abcdefABCDEF])%n", &pid, userdata_s,
&n) > 0 && n > 0) {
- union user_action_cookie cookie;
uint64_t userdata;
userdata = strtoull(userdata_s, NULL, 0);
- memcpy(&cookie, &userdata, sizeof cookie);
- odp_put_userspace_action(pid, &cookie, actions);
+ odp_put_userspace_action(pid, &userdata, sizeof(userdata),
+ actions);
return n;
+ } else if (sscanf(s, "userspace(pid=%lli,userdata(%n", &pid, &n) > 0
+ && n > 0) {
+ struct ofpbuf buf;
+ char *end;
+
+ ofpbuf_init(&buf, 16);
+ end = ofpbuf_put_hex(&buf, &s[n], NULL);
+ if (end[0] == ')' && end[1] == ')') {
+ odp_put_userspace_action(pid, buf.data, buf.size, actions);
+ ofpbuf_uninit(&buf);
+ return (end + 2) - s;
+ }
}
}
case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16);
case OVS_KEY_ATTR_ETHERTYPE: return 2;
+ case OVS_KEY_ATTR_MPLS: return sizeof(struct ovs_key_mpls);
case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4);
case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp);
ds_put_char(ds, ')');
break;
+ case OVS_KEY_ATTR_MPLS: {
+ const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
+ ds_put_char(ds, '(');
+ format_mpls_lse(ds, mpls_key->mpls_top_lse);
+ ds_put_char(ds, ')');
+ break;
+ }
+
case OVS_KEY_ATTR_ETHERTYPE:
ds_put_format(ds, "(0x%04"PRIx16")",
ntohs(nl_attr_get_be16(a)));
return true;
}
+static ovs_be32
+mpls_lse_from_components(int mpls_label, int mpls_tc, int mpls_ttl, int mpls_bos)
+{
+ return (htonl((mpls_label << MPLS_LABEL_SHIFT) |
+ (mpls_tc << MPLS_TC_SHIFT) |
+ (mpls_ttl << MPLS_TTL_SHIFT) |
+ (mpls_bos << MPLS_BOS_SHIFT)));
+}
+
static int
parse_odp_key_attr(const char *s, const struct simap *port_names,
struct ofpbuf *key)
}
}
+ {
+ int label, tc, ttl, bos;
+ int n = -1;
+
+ if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n",
+ &label, &tc, &ttl, &bos, &n) > 0 &&
+ n > 0) {
+ struct ovs_key_mpls *mpls;
+
+ mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls);
+ mpls->mpls_top_lse = mpls_lse_from_components(label, tc, ttl, bos);
+ return n;
+ }
+ }
+
{
ovs_be32 ipv4_src;
ovs_be32 ipv4_dst;
memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
}
+ if (flow->mpls_depth) {
+ struct ovs_key_mpls *mpls_key;
+
+ mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls_key);
+ mpls_key->mpls_top_lse = flow->mpls_lse;
+ }
+
if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
if (flow->nw_proto == IPPROTO_TCP) {
struct ovs_key_tcp *tcp_key;
}
static enum odp_key_fitness
-parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
- uint64_t present_attrs, int out_of_range_attr,
- uint64_t expected_attrs, struct flow *flow,
- const struct nlattr *key, size_t key_len)
+parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
+ uint64_t present_attrs, int out_of_range_attr,
+ uint64_t expected_attrs, struct flow *flow,
+ const struct nlattr *key, size_t key_len)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ ovs_be16 dl_type;
- if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ /* Parse MPLS label stack entry */
+ if (eth_type_mpls(flow->dl_type)) {
+ /* Calculate fitness of outer attributes. */
+ expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
+
+ /* Get the MPLS LSE value. */
+ if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
+ return ODP_FIT_TOO_LITTLE;
+ }
+ flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
+ flow->mpls_depth++;
+
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
+ flow->encap_dl_type = htons(ETH_TYPE_IP);
+ } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
+ flow->encap_dl_type = htons(ETH_TYPE_IPV6);
+ } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
+ flow->encap_dl_type = htons(ETH_TYPE_ARP);
+ }
+ }
+
+ dl_type = flow_innermost_dl_type(flow);
+
+ if (dl_type == htons(ETH_TYPE_IP)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
const struct ovs_key_ipv4 *ipv4_key;
return ODP_FIT_ERROR;
}
}
- } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ } else if (dl_type == htons(ETH_TYPE_IPV6)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
const struct ovs_key_ipv6 *ipv6_key;
return ODP_FIT_ERROR;
}
}
- } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
- flow->dl_type == htons(ETH_TYPE_RARP)) {
+ } else if (dl_type == htons(ETH_TYPE_ARP) ||
+ dl_type == htons(ETH_TYPE_RARP)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
const struct ovs_key_arp *arp_key;
}
if (flow->nw_proto == IPPROTO_TCP
- && is_ip_any(flow)
+ && (dl_type == htons(ETH_TYPE_IP) ||
+ dl_type == htons(ETH_TYPE_IPV6))
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
flow->tp_dst = tcp_key->tcp_dst;
}
} else if (flow->nw_proto == IPPROTO_UDP
- && is_ip_any(flow)
+ && (dl_type == htons(ETH_TYPE_IP) ||
+ dl_type == htons(ETH_TYPE_IPV6))
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
flow->tp_dst = udp_key->udp_dst;
}
} else if (flow->nw_proto == IPPROTO_ICMP
- && flow->dl_type == htons(ETH_TYPE_IP)
+ && dl_type == htons(ETH_TYPE_IP)
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) {
flow->tp_dst = htons(icmp_key->icmp_code);
}
} else if (flow->nw_proto == IPPROTO_ICMPV6
- && flow->dl_type == htons(ETH_TYPE_IPV6)
+ && dl_type == htons(ETH_TYPE_IPV6)
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) {
if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow)) {
return ODP_FIT_ERROR;
}
- encap_fitness = parse_l3_onward(attrs, present_attrs, out_of_range_attr,
- expected_attrs, flow, key, key_len);
+ encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len);
/* The overall fitness is the worse of the outer and inner attributes. */
return MAX(fitness, encap_fitness);
return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
expected_attrs, flow, key, key_len);
}
- return parse_l3_onward(attrs, present_attrs, out_of_range_attr,
- expected_attrs, flow, key, key_len);
+ return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len);
}
/* Returns 'fitness' as a string, for use in debug messages. */
}
/* Appends an OVS_ACTION_ATTR_USERSPACE action to 'odp_actions' that specifies
- * Netlink PID 'pid'. If 'cookie' is nonnull, adds a userdata attribute whose
- * contents contains 'cookie' and returns the offset within 'odp_actions' of
- * the start of the cookie. (If 'cookie' is null, then the return value is not
- * meaningful.) */
+ * Netlink PID 'pid'. If 'userdata' is nonnull, adds a userdata attribute
+ * whose contents are the 'userdata_size' bytes at 'userdata' and returns the
+ * offset within 'odp_actions' of the start of the cookie. (If 'userdata' is
+ * null, then the return value is not meaningful.) */
size_t
-odp_put_userspace_action(uint32_t pid, const union user_action_cookie *cookie,
+odp_put_userspace_action(uint32_t pid,
+ const void *userdata, size_t userdata_size,
struct ofpbuf *odp_actions)
{
+ size_t userdata_ofs;
size_t offset;
offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_USERSPACE);
nl_msg_put_u32(odp_actions, OVS_USERSPACE_ATTR_PID, pid);
- if (cookie) {
+ if (userdata) {
+ userdata_ofs = odp_actions->size + NLA_HDRLEN;
nl_msg_put_unspec(odp_actions, OVS_USERSPACE_ATTR_USERDATA,
- cookie, sizeof *cookie);
+ userdata, userdata_size);
+ } else {
+ userdata_ofs = 0;
}
nl_msg_end_nested(odp_actions, offset);
- return cookie ? odp_actions->size - NLA_ALIGN(sizeof *cookie) : 0;
+ return userdata_ofs;
}
void
nl_msg_end_nested(odp_actions, offset);
}
+void
+odp_put_skb_mark_action(const uint32_t skb_mark,
+ struct ofpbuf *odp_actions)
+{
+ commit_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK, &skb_mark,
+ sizeof(skb_mark));
+}
+
/* If any of the flow key data that ODP actions can modify are different in
* 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
* 'odp_actions' that change the flow tunneling information in key from
base->vlan_tci = flow->vlan_tci;
}
+static void
+commit_mpls_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ if (flow->mpls_lse == base->mpls_lse &&
+ flow->mpls_depth == base->mpls_depth) {
+ return;
+ }
+
+ if (flow->mpls_depth < base->mpls_depth) {
+ if (base->mpls_depth - flow->mpls_depth > 1) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
+ VLOG_WARN_RL(&rl, "Multiple mpls_pop actions reduced to "
+ " a single mpls_pop action");
+ }
+
+ nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, flow->dl_type);
+ } else if (flow->mpls_depth > base->mpls_depth) {
+ struct ovs_action_push_mpls *mpls;
+
+ if (flow->mpls_depth - base->mpls_depth > 1) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
+ VLOG_WARN_RL(&rl, "Multiple mpls_push actions reduced to "
+ " a single mpls_push action");
+ }
+
+ mpls = nl_msg_put_unspec_uninit(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
+ sizeof *mpls);
+ memset(mpls, 0, sizeof *mpls);
+ mpls->mpls_ethertype = flow->dl_type;
+ mpls->mpls_lse = flow->mpls_lse;
+ } else {
+ struct ovs_key_mpls mpls_key;
+
+ mpls_key.mpls_top_lse = flow->mpls_lse;
+ commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
+ &mpls_key, sizeof(mpls_key));
+ }
+
+ base->dl_type = flow->dl_type;
+ base->mpls_lse = flow->mpls_lse;
+ base->mpls_depth = flow->mpls_depth;
+}
+
static void
commit_set_ipv4_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
commit_set_nw_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
{
+ ovs_be16 dl_type = flow_innermost_dl_type(flow);
+
/* Check if flow really have an IP header. */
if (!flow->nw_proto) {
return;
}
- if (base->dl_type == htons(ETH_TYPE_IP)) {
+ if (dl_type == htons(ETH_TYPE_IP)) {
commit_set_ipv4_action(flow, base, odp_actions);
- } else if (base->dl_type == htons(ETH_TYPE_IPV6)) {
+ } else if (dl_type == htons(ETH_TYPE_IPV6)) {
commit_set_ipv6_action(flow, base, odp_actions);
}
}
}
base->skb_mark = flow->skb_mark;
- commit_set_action(odp_actions, OVS_KEY_ATTR_SKB_MARK,
- &base->skb_mark, sizeof(base->skb_mark));
+ odp_put_skb_mark_action(base->skb_mark, odp_actions);
}
/* If any of the flow key data that ODP actions can modify are different in
* 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
{
commit_set_ether_addr_action(flow, base, odp_actions);
commit_vlan_action(flow, base, odp_actions);
+ commit_mpls_action(flow, base, odp_actions);
commit_set_nw_action(flow, base, odp_actions);
commit_set_port_action(flow, base, odp_actions);
commit_set_priority_action(flow, base, odp_actions);
BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 8);
size_t odp_put_userspace_action(uint32_t pid,
- const union user_action_cookie *,
+ const void *userdata, size_t userdata_size,
struct ofpbuf *odp_actions);
void odp_put_tunnel_action(const struct flow_tnl *tunnel,
struct ofpbuf *odp_actions);
+void odp_put_skb_mark_action(const uint32_t skb_mark,
+ struct ofpbuf *odp_actions);
/* Reasons why a subfacet might not be fast-pathable. */
enum slow_path_reason {
#include <config.h>
#include "ofp-actions.h"
-#include "autopath.h"
#include "bundle.h"
#include "byte-order.h"
#include "compiler.h"
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);
case OFPUTIL_NXAST_CONTROLLER:
controller_from_openflow((const struct nx_action_controller *) a, out);
break;
+
+ case OFPUTIL_NXAST_PUSH_MPLS: {
+ struct nx_action_push_mpls *nxapm = (struct nx_action_push_mpls *)a;
+ if (!eth_type_mpls(nxapm->ethertype)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_PUSH_MPLS(out)->ethertype = nxapm->ethertype;
+ break;
+ }
+
+ case OFPUTIL_NXAST_POP_MPLS: {
+ struct nx_action_pop_mpls *nxapm = (struct nx_action_pop_mpls *)a;
+ if (eth_type_mpls(nxapm->ethertype)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_POP_MPLS(out)->ethertype = nxapm->ethertype;
+ break;
+ }
}
return error;
return nxm_reg_load_from_openflow12_set_field(
(const struct ofp12_action_set_field *)a, out);
+ case OFPUTIL_OFPAT11_PUSH_MPLS: {
+ struct ofp11_action_push *oap = (struct ofp11_action_push *)a;
+ if (!eth_type_mpls(oap->ethertype)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_PUSH_MPLS(out)->ethertype = oap->ethertype;
+ break;
+ }
+
+ case OFPUTIL_OFPAT11_POP_MPLS: {
+ struct ofp11_action_pop_mpls *oapm = (struct ofp11_action_pop_mpls *)a;
+ if (eth_type_mpls(oapm->ethertype)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_POP_MPLS(out)->ethertype = oapm->ethertype;
+ break;
+ }
+
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
#include "ofp-util.def"
return ofpact_from_nxast(a, code, out);
}
\f
static enum ofperr
-ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports)
+ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports,
+ ovs_be16 *dl_type)
{
const struct ofpact_enqueue *enqueue;
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);
+ if (*dl_type != flow->dl_type) {
+ struct flow updated_flow = *flow;
+ updated_flow.dl_type = *dl_type;
+ return nxm_reg_load_check(ofpact_get_REG_LOAD(a), &updated_flow);
+ } else {
+ return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow);
+ }
case OFPACT_DEC_TTL:
case OFPACT_SET_TUNNEL:
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;
+ case OFPACT_PUSH_MPLS:
+ *dl_type = ofpact_get_PUSH_MPLS(a)->ethertype;
+ return 0;
+
+ case OFPACT_POP_MPLS:
+ *dl_type = ofpact_get_POP_MPLS(a)->ethertype;
+ return 0;
+
case OFPACT_CLEAR_ACTIONS:
case OFPACT_WRITE_METADATA:
case OFPACT_GOTO_TABLE:
const struct flow *flow, int max_ports)
{
const struct ofpact *a;
+ ovs_be16 dl_type = flow->dl_type;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
- enum ofperr error = ofpact_check__(a, flow, max_ports);
+ enum ofperr error = ofpact_check__(a, flow, max_ports, &dl_type);
if (error) {
return error;
}
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;
ofputil_put_NXAST_EXIT(out);
break;
+ case OFPACT_PUSH_MPLS:
+ ofputil_put_NXAST_PUSH_MPLS(out)->ethertype =
+ ofpact_get_PUSH_MPLS(a)->ethertype;
+ break;
+
+ case OFPACT_POP_MPLS:
+ ofputil_put_NXAST_POP_MPLS(out)->ethertype =
+ ofpact_get_POP_MPLS(a)->ethertype;
+ break;
+
case OFPACT_OUTPUT:
case OFPACT_ENQUEUE:
case OFPACT_SET_VLAN_VID:
case OFPACT_RESUBMIT:
case OFPACT_LEARN:
case OFPACT_MULTIPATH:
- case OFPACT_AUTOPATH:
case OFPACT_NOTE:
case OFPACT_EXIT:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_POP_MPLS:
ofpact_to_nxast(a, out);
break;
}
/* OpenFlow 1.1 uses OFPIT_WRITE_METADATA to express this action. */
break;
+ case OFPACT_PUSH_MPLS:
+ ofputil_put_OFPAT11_PUSH_MPLS(out)->ethertype =
+ ofpact_get_PUSH_MPLS(a)->ethertype;
+ break;
+
+ case OFPACT_POP_MPLS:
+ ofputil_put_OFPAT11_POP_MPLS(out)->ethertype =
+ ofpact_get_POP_MPLS(a)->ethertype;
+
+ break;
+
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
NOT_REACHED();
case OFPACT_RESUBMIT:
case OFPACT_LEARN:
case OFPACT_MULTIPATH:
- case OFPACT_AUTOPATH:
case OFPACT_NOTE:
case OFPACT_EXIT:
ofpact_to_nxast(a, out);
case OFPACT_RESUBMIT:
case OFPACT_LEARN:
case OFPACT_MULTIPATH:
- case OFPACT_AUTOPATH:
case OFPACT_NOTE:
case OFPACT_EXIT:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_POP_MPLS:
case OFPACT_CLEAR_ACTIONS:
case OFPACT_GOTO_TABLE:
default:
{
const struct ofpact_enqueue *enqueue;
const struct ofpact_resubmit *resubmit;
- const struct ofpact_autopath *autopath;
const struct ofpact_controller *controller;
const struct ofpact_metadata *metadata;
const struct ofpact_tunnel *tunnel;
multipath_format(ofpact_get_MULTIPATH(a), s);
break;
- case OFPACT_AUTOPATH:
- autopath = ofpact_get_AUTOPATH(a);
- ds_put_cstr(s, "autopath(");
- ofputil_format_port(autopath->port, s);
- ds_put_char(s, ',');
- mf_format_subfield(&autopath->dst, s);
- ds_put_char(s, ')');
- break;
-
case OFPACT_NOTE:
print_note(ofpact_get_NOTE(a), s);
break;
+ case OFPACT_PUSH_MPLS:
+ ds_put_format(s, "push_mpls:0x%04"PRIx16,
+ ntohs(ofpact_get_PUSH_MPLS(a)->ethertype));
+ break;
+
+ case OFPACT_POP_MPLS:
+ ds_put_format(s, "pop_mpls:0x%04"PRIx16,
+ ntohs(ofpact_get_POP_MPLS(a)->ethertype));
+ break;
+
case OFPACT_EXIT:
ds_put_cstr(s, "exit");
break;
/*
- * Copyright (c) 2012 Nicira, Inc.
+ * Copyright (c) 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
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) \
+ DEFINE_OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact) \
+ DEFINE_OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact) \
\
/* Metadata. */ \
DEFINE_OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact) \
\
/* Arithmetic. */ \
DEFINE_OFPACT(MULTIPATH, ofpact_multipath, ofpact) \
- DEFINE_OFPACT(AUTOPATH, ofpact_autopath, ofpact) \
\
/* Other. */ \
DEFINE_OFPACT(NOTE, ofpact_note, data) \
union mf_subvalue subvalue; /* Least-significant bits are used. */
};
+/* OFPACT_PUSH_VLAN/MPLS/PBB
+ *
+ * Used for NXAST_PUSH_MPLS, OFPAT11_PUSH_MPLS. */
+struct ofpact_push_mpls {
+ struct ofpact ofpact;
+ ovs_be16 ethertype;
+};
+
+/* OFPACT_POP_MPLS
+ *
+ * Used for NXAST_POP_MPLS, OFPAT11_POP_MPLS.. */
+struct ofpact_pop_mpls {
+ struct ofpact ofpact;
+ ovs_be16 ethertype;
+};
+
/* OFPACT_SET_TUNNEL.
*
* Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */
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. */
return ofpraw_pull(raw, &msg);
}
+/* Does the same job as ofpraw_decode(), except that it assert-fails if
+ * ofpraw_decode() 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
+ * ofpraw_decode() on the message and thus know that it's OK.) */
+enum ofpraw
+ofpraw_decode_assert(const struct ofp_header *oh)
+{
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = ofpraw_decode(&raw, oh);
+ ovs_assert(!error);
+ return raw;
+}
+
/* 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
}
/* 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
+ * ofpraw_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.) */
+ * ofpraw_decode() on the message and thus know that it's OK.) */
enum ofpraw
ofpraw_pull_assert(struct ofpbuf *msg)
{
next = ofpbuf_new(MAX(1024, hdrs_len + len));
ofpbuf_put(next, msg->data, hdrs_len);
+ next->l2 = next->data;
+ next->l3 = ofpbuf_tail(next);
list_push_back(replies, &next->list_node);
*ofpmp_flags__(msg->data) |= htons(OFPSF_REPLY_MORE);
/*
- * Copyright (c) 2012 Nicira, Inc.
+ * Copyright (c) 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Decoding messages into OFPRAW_* values. */
enum ofperr ofpraw_decode(enum ofpraw *, const struct ofp_header *);
+enum ofpraw ofpraw_decode_assert(const struct ofp_header *);
enum ofperr ofpraw_pull(enum ofpraw *, struct ofpbuf *);
enum ofpraw ofpraw_pull_assert(struct ofpbuf *);
#include <errno.h>
#include <stdlib.h>
-#include "autopath.h"
#include "bundle.h"
#include "byte-order.h"
#include "dynamic-string.h"
multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
break;
- case OFPUTIL_NXAST_AUTOPATH__DEPRECATED:
- autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg);
- break;
-
case OFPUTIL_NXAST_BUNDLE:
bundle_parse(arg, ofpacts);
break;
case OFPUTIL_NXAST_CONTROLLER:
parse_controller(ofpacts, arg);
break;
+
+ case OFPUTIL_OFPAT11_PUSH_MPLS:
+ case OFPUTIL_NXAST_PUSH_MPLS:
+ ofpact_put_PUSH_MPLS(ofpacts)->ethertype =
+ htons(str_to_u16(arg, "push_mpls"));
+ break;
+
+ case OFPUTIL_OFPAT11_POP_MPLS:
+ case OFPUTIL_NXAST_POP_MPLS:
+ ofpact_put_POP_MPLS(ofpacts)->ethertype =
+ htons(str_to_u16(arg, "pop_mpls"));
+ break;
}
}
{ "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
{ "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
{ "rarp", ETH_TYPE_RARP, 0},
-};
+ { "mpls", ETH_TYPE_MPLS, 0 },
+ { "mplsm", ETH_TYPE_MPLS_MCAST, 0 },
+ };
const struct protocol *p;
for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
parse_field(mf_from_name(name), value, &fm->match);
} else if (!strcmp(name, "duration")
|| !strcmp(name, "n_packets")
- || !strcmp(name, "n_bytes")) {
+ || !strcmp(name, "n_bytes")
+ || !strcmp(name, "idle_age")
+ || !strcmp(name, "hard_age")) {
/* Ignore these, so that users can feed the output of
* "ovs-ofctl dump-flows" back into commands that parse
* flows. */
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
ds_put_cstr(&f, "arp,");
} else if (om->dl_type == htons(ETH_TYPE_RARP)){
ds_put_cstr(&f, "rarp,");
+ } else if (om->dl_type == htons(ETH_TYPE_MPLS)) {
+ ds_put_cstr(&f, "mpls,");
+ } else if (om->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ ds_put_cstr(&f, "mplsm,");
} else {
skip_type = false;
}
}
ds_put_cstr(string, " role=");
- if (rr.request_current_role_only) {
- ds_put_cstr(string, "nochange");
- return;
- }
switch (rr.role) {
- case NX_ROLE_OTHER:
+ case OFPCR12_ROLE_NOCHANGE:
+ ds_put_cstr(string, "nochange");
+ break;
+ case OFPCR12_ROLE_EQUAL:
ds_put_cstr(string, "equal"); /* OF 1.2 wording */
break;
- case NX_ROLE_MASTER:
+ case OFPCR12_ROLE_MASTER:
ds_put_cstr(string, "master");
break;
- case NX_ROLE_SLAVE:
+ case OFPCR12_ROLE_SLAVE:
ds_put_cstr(string, "slave");
break;
default:
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <stdlib.h>
-#include "autopath.h"
#include "bundle.h"
#include "byte-order.h"
#include "classifier.h"
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
}
}
- if (match->flow.dl_type == htons(ETH_TYPE_MPLS) ||
- match->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ if (eth_type_mpls(match->flow.dl_type)) {
enum { OFPFW11_MPLS_ALL = OFPFW11_MPLS_LABEL | OFPFW11_MPLS_TC };
if ((wc & OFPFW11_MPLS_ALL) != OFPFW11_MPLS_ALL) {
{
const struct flow_wildcards *wc = &match->wc;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
/* tunnel params other than tun_id can't be sent in a flow_mod */
if (!tun_parms_fully_wildcarded(wc)) {
| OFPUTIL_P_OF13_OXM;
}
+ /* NXM and OF1.1+ support matching MPLS label */
+ if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OF1.1+ support matching MPLS TC */
+ if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
+ /* NXM and OF1.3+ support matching MPLS stack flag */
+ /* Allow for OF1.2 as there doesn't seem to be a
+ * particularly good reason not to */
+ if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
+ return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM
+ | OFPUTIL_P_OF13_OXM;
+ }
+
/* Other formats can express this rule. */
return OFPUTIL_P_ANY;
}
ofputil_decode_role_message(const struct ofp_header *oh,
struct ofputil_role_request *rr)
{
- const struct ofp12_role_request *orr = ofpmsg_body(oh);
- uint32_t role = ntohl(orr->role);
struct ofpbuf b;
enum ofpraw raw;
- memset(rr, 0, sizeof *rr);
-
ofpbuf_use_const(&b, oh, ntohs(oh->length));
raw = ofpraw_pull_assert(&b);
- if (raw == OFPRAW_OFPT12_ROLE_REQUEST
- || raw == OFPRAW_OFPT12_ROLE_REPLY) {
+ if (raw == OFPRAW_OFPT12_ROLE_REQUEST ||
+ raw == OFPRAW_OFPT12_ROLE_REPLY) {
+ const struct ofp12_role_request *orr = b.l3;
- if (raw == OFPRAW_OFPT12_ROLE_REQUEST) {
- if (role == OFPCR12_ROLE_NOCHANGE) {
- rr->request_current_role_only = true;
- return 0;
- }
- if (role == OFPCR12_ROLE_MASTER || role == OFPCR12_ROLE_SLAVE) {
- rr->generation_id = ntohll(orr->generation_id);
- rr->have_generation_id = true;
- }
+ if (orr->role != htonl(OFPCR12_ROLE_NOCHANGE) &&
+ orr->role != htonl(OFPCR12_ROLE_EQUAL) &&
+ orr->role != htonl(OFPCR12_ROLE_MASTER) &&
+ orr->role != htonl(OFPCR12_ROLE_SLAVE)) {
+ return OFPERR_OFPRRFC_BAD_ROLE;
}
- /* Map to enum nx_role */
- role -= 1; /* OFPCR12_ROLE_MASTER -> NX_ROLE_MASTER etc. */
- } else if (raw != OFPRAW_NXT_ROLE_REQUEST
- && raw != OFPRAW_NXT_ROLE_REPLY) {
- return OFPERR_OFPBRC_BAD_TYPE;
- }
+ rr->role = ntohl(orr->role);
+ if (raw == OFPRAW_OFPT12_ROLE_REQUEST
+ ? orr->role == htonl(OFPCR12_ROLE_NOCHANGE)
+ : orr->generation_id == htonll(UINT64_MAX)) {
+ rr->have_generation_id = false;
+ rr->generation_id = 0;
+ } else {
+ rr->have_generation_id = true;
+ rr->generation_id = ntohll(orr->generation_id);
+ }
+ } else if (raw == OFPRAW_NXT_ROLE_REQUEST ||
+ raw == OFPRAW_NXT_ROLE_REPLY) {
+ const struct nx_role_request *nrr = b.l3;
+
+ BUILD_ASSERT(NX_ROLE_OTHER + 1 == OFPCR12_ROLE_EQUAL);
+ BUILD_ASSERT(NX_ROLE_MASTER + 1 == OFPCR12_ROLE_MASTER);
+ BUILD_ASSERT(NX_ROLE_SLAVE + 1 == OFPCR12_ROLE_SLAVE);
+
+ if (nrr->role != htonl(NX_ROLE_OTHER) &&
+ nrr->role != htonl(NX_ROLE_MASTER) &&
+ nrr->role != htonl(NX_ROLE_SLAVE)) {
+ return OFPERR_OFPRRFC_BAD_ROLE;
+ }
- if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER
- && role != NX_ROLE_SLAVE) {
- return OFPERR_OFPRRFC_BAD_ROLE;
+ rr->role = ntohl(nrr->role) + 1;
+ rr->have_generation_id = false;
+ rr->generation_id = 0;
+ } else {
+ NOT_REACHED();
}
- rr->role = role;
return 0;
}
* buffer owned by the caller. */
struct ofpbuf *
ofputil_encode_role_reply(const struct ofp_header *request,
- enum nx_role role)
+ const struct ofputil_role_request *rr)
{
- struct ofp12_role_request *reply;
struct ofpbuf *buf;
- size_t reply_size;
-
- struct ofpbuf b;
enum ofpraw raw;
- ofpbuf_use_const(&b, request, ntohs(request->length));
- raw = ofpraw_pull_assert(&b);
+ raw = ofpraw_decode_assert(request);
if (raw == OFPRAW_OFPT12_ROLE_REQUEST) {
- reply_size = sizeof (struct ofp12_role_request);
- raw = OFPRAW_OFPT12_ROLE_REPLY;
- }
- else if (raw == OFPRAW_NXT_ROLE_REQUEST) {
- reply_size = sizeof (struct nx_role_request);
- raw = OFPRAW_NXT_ROLE_REPLY;
- } else {
- NOT_REACHED();
- }
+ struct ofp12_role_request *orr;
- buf = ofpraw_alloc_reply(raw, request, 0);
- reply = ofpbuf_put_zeros(buf, reply_size);
+ buf = ofpraw_alloc_reply(OFPRAW_OFPT12_ROLE_REPLY, request, 0);
+ orr = ofpbuf_put_zeros(buf, sizeof *orr);
- if (raw == OFPRAW_OFPT12_ROLE_REPLY) {
- /* Map to OpenFlow enum ofp12_controller_role */
- role += 1; /* NX_ROLE_MASTER -> OFPCR12_ROLE_MASTER etc. */
- /*
- * OpenFlow specification does not specify use of generation_id field
- * on reply messages. Intuitively, it would seem a good idea to return
- * the current value. However, the current value is undefined
- * initially, and there is no way to insert an undefined value in the
- * message. Therefore we leave the generation_id zeroed on reply
- * messages.
- *
- * A request for clarification has been filed with the Open Networking
- * Foundation as EXT-272.
- */
+ orr->role = htonl(rr->role);
+ orr->generation_id = htonll(rr->have_generation_id
+ ? rr->generation_id
+ : UINT64_MAX);
+ } else if (raw == OFPRAW_NXT_ROLE_REQUEST) {
+ struct nx_role_request *nrr;
+
+ BUILD_ASSERT(NX_ROLE_OTHER == OFPCR12_ROLE_EQUAL - 1);
+ BUILD_ASSERT(NX_ROLE_MASTER == OFPCR12_ROLE_MASTER - 1);
+ BUILD_ASSERT(NX_ROLE_SLAVE == OFPCR12_ROLE_SLAVE - 1);
+
+ buf = ofpraw_alloc_reply(OFPRAW_NXT_ROLE_REPLY, request, 0);
+ nrr = ofpbuf_put_zeros(buf, sizeof *nrr);
+ nrr->role = htonl(rr->role - 1);
+ } else {
+ NOT_REACHED();
}
- reply->role = htonl(role);
return buf;
}
*portp = port32;
return true;
} else if (port32 <= OFPP_LAST_RESV) {
- struct ds s;
+ struct ds msg;
- ds_init(&s);
- ofputil_format_port(port32, &s);
+ ds_init(&msg);
+ ofputil_format_port(port32, &msg);
VLOG_WARN_ONCE("referring to port %s as %u is deprecated for "
"compatibility with future versions of OpenFlow",
- ds_cstr(&s), port32);
- ds_destroy(&s);
+ ds_cstr(&msg), port32);
+ ds_destroy(&msg);
*portp = port32;
return true;
MAY_ARP_SHA = 1 << 4, /* arp_sha */
MAY_ARP_THA = 1 << 5, /* arp_tha */
MAY_IPV6 = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */
- MAY_ND_TARGET = 1 << 7 /* nd_target */
+ MAY_ND_TARGET = 1 << 7, /* nd_target */
+ MAY_MPLS = 1 << 8, /* mpls label and tc */
} may_match;
struct flow_wildcards wc;
} else if (match->flow.dl_type == htons(ETH_TYPE_ARP) ||
match->flow.dl_type == htons(ETH_TYPE_RARP)) {
may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
+ } else if (eth_type_mpls(match->flow.dl_type)) {
+ may_match = MAY_MPLS;
} else {
may_match = 0;
}
if (!(may_match & MAY_ND_TARGET)) {
wc.masks.nd_target = in6addr_any;
}
+ if (!(may_match & MAY_MPLS)) {
+ wc.masks.mpls_lse = htonl(0);
+ wc.masks.mpls_depth = 0;
+ }
/* Log any changes. */
if (!flow_wildcards_equal(&wc, &match->wc)) {
OFPAT11_ACTION(OFPAT11_SET_TP_DST, ofp_action_tp_port, 0, "mod_tp_dst")
OFPAT11_ACTION(OFPAT11_PUSH_VLAN, ofp11_action_push, 0, "push_vlan")
OFPAT11_ACTION(OFPAT11_POP_VLAN, ofp_action_header, 0, "pop_vlan")
+OFPAT11_ACTION(OFPAT11_PUSH_MPLS, ofp11_action_push, 0, "push_mpls")
+OFPAT11_ACTION(OFPAT11_POP_MPLS, ofp11_action_pop_mpls, 0, "pop_mpls")
OFPAT11_ACTION(OFPAT11_SET_QUEUE, ofp11_action_set_queue, 0, "set_queue")
//OFPAT11_ACTION(OFPAT11_SET_NW_TTL, ofp11_action_nw_ttl, 0, "set_nw_ttl")
OFPAT11_ACTION(OFPAT11_DEC_NW_TTL, ofp_action_header, 0, NULL)
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_DEC_TTL_CNT_IDS, nx_action_cnt_ids, 1, NULL)
NXAST_ACTION(NXAST_WRITE_METADATA, nx_action_write_metadata, 0,
"write_metadata")
+NXAST_ACTION(NXAST_PUSH_MPLS, nx_action_push_mpls, 0, "push_mpls")
+NXAST_ACTION(NXAST_POP_MPLS, nx_action_pop_mpls, 0, "pop_mpls")
#undef OFPAT10_ACTION
#undef OFPAT11_ACTION
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Abstract ofp_role_request and reply. */
struct ofputil_role_request {
- bool request_current_role_only; /* no role change */
+ enum ofp12_controller_role role;
bool have_generation_id;
- enum nx_role role;
uint64_t generation_id;
};
enum ofperr ofputil_decode_role_message(const struct ofp_header *,
struct ofputil_role_request *);
struct ofpbuf *ofputil_encode_role_reply(const struct ofp_header *,
- enum nx_role current_role);
+ const struct ofputil_role_request *);
/* Abstract table stats.
*
* OFPUTIL_NXAST_NOTE
* OFPUTIL_NXAST_SET_TUNNEL64
* OFPUTIL_NXAST_MULTIPATH
- * OFPUTIL_NXAST_AUTOPATH
* OFPUTIL_NXAST_BUNDLE
* OFPUTIL_NXAST_BUNDLE_LOAD
* OFPUTIL_NXAST_RESUBMIT_TABLE
/* Handy utility for parsing flows and actions. */
bool ofputil_parse_key_value(char **stringp, char **keyp, char **valuep);
-struct ofpbuf *ofputlil_dump_ports(enum ofp_version ofp_version, int16_t port);
-
struct ofputil_port_stats {
uint16_t port_no;
struct netdev_stats stats;
b->allocated = allocated;
b->source = source;
b->size = 0;
- b->l2 = b->l3 = b->l4 = b->l7 = NULL;
+ b->l2 = b->l2_5 = b->l3 = b->l4 = b->l7 = NULL;
list_poison(&b->list_node);
b->private_p = NULL;
}
if (buffer->l2) {
new_buffer->l2 = (char *) buffer->l2 + data_delta;
}
+ if (buffer->l2_5) {
+ new_buffer->l2_5 = (char *) buffer->l2_5 + data_delta;
+ }
if (buffer->l3) {
new_buffer->l3 = (char *) buffer->l3 + data_delta;
}
if (b->l2) {
b->l2 = (char *) b->l2 + data_delta;
}
+ if (b->l2_5) {
+ b->l2_5 = (char *) b->l2_5 + data_delta;
+ }
if (b->l3) {
b->l3 = (char *) b->l3 + data_delta;
}
size_t size; /* Number of bytes in use. */
void *l2; /* Link-level header. */
+ void *l2_5; /* MPLS label stack */
void *l3; /* Network-level header. */
void *l4; /* Transport-level header. */
void *l7; /* Application data. */
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON "
"UUID: %s", s);
free(s);
+ ovsdb_error_destroy(error);
return false;
}
/* Removes outermost VLAN header (if any is present) from 'packet'.
*
- * 'packet->l2' must initially point to 'packet''s Ethernet header. */
+ * 'packet->l2_5' should initially point to 'packet''s outer-most MPLS header
+ * or may be NULL if there are no MPLS headers. */
void
eth_pop_vlan(struct ofpbuf *packet)
{
}
}
+/* Return depth of mpls stack.
+ *
+ * 'packet->l2_5' should initially point to 'packet''s outer-most MPLS header
+ * or may be NULL if there are no MPLS headers. */
+uint16_t
+eth_mpls_depth(const struct ofpbuf *packet)
+{
+ struct mpls_hdr *mh = packet->l2_5;
+ uint16_t depth;
+
+ if (!mh) {
+ return 0;
+ }
+
+ depth = 0;
+ while (packet->size >= ((char *)mh - (char *)packet->data) + sizeof *mh) {
+ depth++;
+ if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
+ break;
+ }
+ mh++;
+ }
+
+ return depth;
+}
+
+/* Set ethertype of the packet. */
+void
+set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type)
+{
+ struct eth_header *eh = packet->data;
+
+ if (eh->eth_type == htons(ETH_TYPE_VLAN)) {
+ ovs_be16 *p;
+ p = (ovs_be16 *)((char *)(packet->l2_5 ? packet->l2_5 : packet->l3) - 2);
+ *p = eth_type;
+ } else {
+ eh->eth_type = eth_type;
+ }
+}
+
+static bool is_mpls(struct ofpbuf *packet)
+{
+ return packet->l2_5 != NULL;
+}
+
+/* Set time to live (TTL) of an MPLS label stack entry (LSE). */
+static void
+set_mpls_lse_ttl(ovs_be32 *lse, uint8_t ttl)
+{
+ *lse &= ~htonl(MPLS_TTL_MASK);
+ *lse |= htonl((ttl << MPLS_TTL_SHIFT) & MPLS_TTL_MASK);
+}
+
+/* Set traffic class (TC) of an MPLS label stack entry (LSE). */
+void
+set_mpls_lse_tc(ovs_be32 *lse, uint8_t tc)
+{
+ *lse &= ~htonl(MPLS_TC_MASK);
+ *lse |= htonl((tc << MPLS_TC_SHIFT) & MPLS_TC_MASK);
+}
+
+/* Set label of an MPLS label stack entry (LSE). */
+void
+set_mpls_lse_label(ovs_be32 *lse, ovs_be32 label)
+{
+ *lse &= ~htonl(MPLS_LABEL_MASK);
+ *lse |= htonl((ntohl(label) << MPLS_LABEL_SHIFT) & MPLS_LABEL_MASK);
+}
+
+/* Set bottom of stack (BoS) bit of an MPLS label stack entry (LSE). */
+void
+set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos)
+{
+ *lse &= ~htonl(MPLS_BOS_MASK);
+ *lse |= htonl((bos << MPLS_BOS_SHIFT) & MPLS_BOS_MASK);
+}
+
+/* Compose an MPLS label stack entry (LSE) from its components:
+ * label, traffic class (TC), time to live (TTL) and
+ * bottom of stack (BoS) bit. */
+ovs_be32
+set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos, ovs_be32 label)
+{
+ ovs_be32 lse = htonl(0);
+ set_mpls_lse_ttl(&lse, ttl);
+ set_mpls_lse_tc(&lse, tc);
+ set_mpls_lse_bos(&lse, bos);
+ set_mpls_lse_label(&lse, label);
+ return lse;
+}
+
+/* Push an new MPLS stack entry onto the MPLS stack and adjust 'packet->l2' and
+ * 'packet->l2_5' accordingly. The new entry will be the outermost entry on
+ * the stack.
+ *
+ * Previous to calling this function, 'packet->l2_5' must be set; if the MPLS
+ * label to be pushed will be the first label in 'packet', then it should be
+ * the same as 'packet->l3'. */
+static void
+push_mpls_lse(struct ofpbuf *packet, struct mpls_hdr *mh)
+{
+ char * header;
+ size_t len;
+ header = ofpbuf_push_uninit(packet, MPLS_HLEN);
+ len = (char *)packet->l2_5 - (char *)packet->l2;
+ memmove(header, packet->l2, len);
+ memcpy(header + len, mh, sizeof *mh);
+ packet->l2 = (char*)packet->l2 - MPLS_HLEN;
+ packet->l2_5 = (char*)packet->l2_5 - MPLS_HLEN;
+}
+
+/* Set MPLS label stack entry to outermost MPLS header.*/
+void
+set_mpls_lse(struct ofpbuf *packet, ovs_be32 mpls_lse)
+{
+ struct mpls_hdr *mh = packet->l2_5;
+
+ /* Packet type should be MPLS to set label stack entry. */
+ if (is_mpls(packet)) {
+ /* Update mpls label stack entry. */
+ mh->mpls_lse = mpls_lse;
+ }
+}
+
+/* Push MPLS label stack entry 'lse' onto 'packet' as the the outermost MPLS
+ * header. If 'packet' does not already have any MPLS labels, then its
+ * Ethertype is changed to 'ethtype' (which must be an MPLS Ethertype). */
+void
+push_mpls(struct ofpbuf *packet, ovs_be16 ethtype, ovs_be32 lse)
+{
+ struct mpls_hdr mh;
+
+ if (!eth_type_mpls(ethtype)) {
+ return;
+ }
+
+ if (!is_mpls(packet)) {
+ /* Set ethtype and MPLS label stack entry. */
+ set_ethertype(packet, ethtype);
+ packet->l2_5 = packet->l3;
+ }
+
+ /* Push new MPLS shim header onto packet. */
+ mh.mpls_lse = lse;
+ push_mpls_lse(packet, &mh);
+}
+
+/* If 'packet' is an MPLS packet, removes its outermost MPLS label stack entry.
+ * If the label that was removed was the only MPLS label, changes 'packet''s
+ * Ethertype to 'ethtype' (which ordinarily should not be an MPLS
+ * Ethertype). */
+void
+pop_mpls(struct ofpbuf *packet, ovs_be16 ethtype)
+{
+ struct mpls_hdr *mh = NULL;
+
+ if (is_mpls(packet)) {
+ size_t len;
+ mh = packet->l2_5;
+ len = (char*)packet->l2_5 - (char*)packet->l2;
+ /* If bottom of the stack set ethertype. */
+ if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
+ set_ethertype(packet, ethtype);
+ packet->l2_5 = NULL;
+ } else {
+ packet->l2_5 = (char*)packet->l2_5 + MPLS_HLEN;
+ }
+ /* Shift the l2 header forward. */
+ memmove((char*)packet->data + MPLS_HLEN, packet->data, len);
+ packet->size -= MPLS_HLEN;
+ packet->data = (char*)packet->data + MPLS_HLEN;
+ packet->l2 = (char*)packet->l2 + MPLS_HLEN;
+ }
+}
+
/* Converts hex digits in 'hex' to an Ethernet packet in '*packetp'. The
* caller must free '*packetp'. On success, returns NULL. On failure, returns
* an error message and stores NULL in '*packetp'. */
uint8_t
packet_get_tcp_flags(const struct ofpbuf *packet, const struct flow *flow)
{
- if (is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP && packet->l7) {
+ ovs_be16 dl_type = flow_innermost_dl_type(flow);
+ if (dl_type_is_ip_any(dl_type) &&
+ flow->nw_proto == IPPROTO_TCP && packet->l7) {
const struct tcp_header *tcp = packet->l4;
return TCP_FLAGS(tcp->tcp_ctl);
} else {
void eth_push_vlan(struct ofpbuf *, ovs_be16 tci);
void eth_pop_vlan(struct ofpbuf *);
+uint16_t eth_mpls_depth(const struct ofpbuf *packet);
+
+void set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type);
+
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);
const uint8_t mask[ETH_ADDR_LEN],
uint8_t dst[ETH_ADDR_LEN]);
+void set_mpls_lse(struct ofpbuf *, ovs_be32 label);
+void push_mpls(struct ofpbuf *packet, ovs_be16 ethtype, ovs_be32 lse);
+void pop_mpls(struct ofpbuf *, ovs_be16 ethtype);
+
+void set_mpls_lse_tc(ovs_be32 *lse, uint8_t tc);
+void set_mpls_lse_label(ovs_be32 *lse, ovs_be32 label);
+void set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos);
+ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos,
+ ovs_be32 label);
+
/* Example:
*
* uint8_t mac[ETH_ADDR_LEN];
#define ETH_TYPE_MPLS 0x8847
#define ETH_TYPE_MPLS_MCAST 0x8848
+static inline bool eth_type_mpls(ovs_be16 eth_type)
+{
+ return eth_type == htons(ETH_TYPE_MPLS) ||
+ eth_type == htons(ETH_TYPE_MPLS_MCAST);
+}
+
/* Minimum value for an Ethernet type. Values below this are IEEE 802.2 frame
* lengths. */
#define ETH_TYPE_MIN 0x600
} __attribute__((packed));
BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header));
+/* MPLS related definitions */
+#define MPLS_TTL_MASK 0x000000ff
+#define MPLS_TTL_SHIFT 0
+
+#define MPLS_BOS_MASK 0x00000100
+#define MPLS_BOS_SHIFT 8
+
+#define MPLS_TC_MASK 0x00000e00
+#define MPLS_TC_SHIFT 9
+
+#define MPLS_LABEL_MASK 0xfffff000
+#define MPLS_LABEL_SHIFT 12
+
+#define MPLS_HLEN 4
+
+struct mpls_hdr {
+ ovs_be32 mpls_lse;
+};
+BUILD_ASSERT_DECL(MPLS_HLEN == sizeof(struct mpls_hdr));
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls label in host byte order */
+static inline uint32_t
+mpls_lse_to_label(ovs_be32 mpls_lse)
+{
+ return (ntohl(mpls_lse) & MPLS_LABEL_MASK) >> MPLS_LABEL_SHIFT;
+}
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls tc */
+static inline uint8_t
+mpls_lse_to_tc(ovs_be32 mpls_lse)
+{
+ return (ntohl(mpls_lse) & MPLS_TC_MASK) >> MPLS_TC_SHIFT;
+}
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls ttl */
+static inline uint8_t
+mpls_lse_to_ttl(ovs_be32 mpls_lse)
+{
+ return (ntohl(mpls_lse) & MPLS_TTL_MASK) >> MPLS_TTL_SHIFT;
+}
+
+/* Set TTL in mpls lse. */
+static inline void
+flow_set_mpls_lse_ttl(ovs_be32 *mpls_lse, uint8_t ttl)
+{
+ *mpls_lse &= ~htonl(MPLS_TTL_MASK);
+ *mpls_lse |= htonl(ttl << MPLS_TTL_SHIFT);
+}
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls BoS bit */
+static inline uint8_t
+mpls_lse_to_bos(ovs_be32 mpls_lse)
+{
+ return (mpls_lse & htonl(MPLS_BOS_MASK)) != 0;
+}
+
#define IP_FMT "%"PRIu32".%"PRIu32".%"PRIu32".%"PRIu32
#define IP_ARGS(ip) \
ntohl(ip) >> 24, \
return ipv6_addr_equals(mask, &in6addr_exact);
}
+static inline bool dl_type_is_ip_any(ovs_be16 dl_type)
+{
+ return dl_type == htons(ETH_TYPE_IP)
+ || dl_type == htons(ETH_TYPE_IPV6);
+}
+
static inline bool is_ip_any(const struct flow *flow)
{
- return flow->dl_type == htons(ETH_TYPE_IP)
- || flow->dl_type == htons(ETH_TYPE_IPV6);
+ return dl_type_is_ip_any(flow->dl_type);
}
void format_ipv6_addr(char *addr_str, const struct in6_addr *addr);
cpu_usage = get_cpu_usage();
if (VLOG_IS_DBG_ENABLED()) {
level = VLL_DBG;
- } else if (cpu_usage > 50 && !VLOG_DROP_WARN(&rl)) {
- level = VLL_WARN;
+ } else if (cpu_usage > 50 && !VLOG_DROP_INFO(&rl)) {
+ level = VLL_INFO;
} else {
return;
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
static int
stream_open(struct stream *s, size_t max_size)
{
+ int error;
+
s->max_size = max_size;
ds_init(&s->log);
if (pipe(s->fds)) {
VLOG_WARN("failed to create pipe: %s", strerror(errno));
return errno;
}
- set_nonblocking(s->fds[0]);
- return 0;
+ error = set_nonblocking(s->fds[0]);
+ if (error) {
+ close(s->fds[0]);
+ close(s->fds[1]);
+ }
+ return error;
}
static void
free(node);
}
+/* Searches for 'name' in 'simap'. If found, deletes it and returns true. If
+ * not found, returns false without modifying 'simap'. */
+bool
+simap_find_and_delete(struct simap *simap, const char *name)
+{
+ struct simap_node *node = simap_find(simap, name);
+ if (node) {
+ simap_delete(simap, node);
+ return true;
+ }
+ return false;
+}
+
/* Searches 'simap' for a mapping with the given 'name'. Returns it, if found,
* or a null pointer if not. */
struct simap_node *
return node ? node->data : 0;
}
+/* Returns true if 'simap' contains a copy of 'name', false otherwise. */
+bool
+simap_contains(const struct simap *simap, const char *name)
+{
+ return simap_find(simap, name) != NULL;
+}
+
/* Returns an array that contains a pointer to each mapping in 'simap',
* ordered alphabetically by name. The returned array has simap_count(simap)
* elements.
struct simap_node *simap_find(const struct simap *, const char *);
struct simap_node *simap_find_len(const struct simap *,
const char *, size_t len);
+bool simap_contains(const struct simap *, const char *);
void simap_delete(struct simap *, struct simap_node *);
+bool simap_find_and_delete(struct simap *, const char *);
const struct simap_node **simap_sort(const struct simap *);
* it will only happen if style is SOCK_STREAM or SOCK_SEQPACKET, and only
* if a backlog of un-accepted connections has built up in the kernel.) */
if (nonblock) {
- int flags = fcntl(fd, F_GETFL, 0);
- if (flags == -1) {
- error = errno;
- goto error;
- }
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
- error = errno;
+ error = set_nonblocking(fd);
+ if (error) {
goto error;
}
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "packets.h"
#include "poll-loop.h"
#include "socket-util.h"
+#include "dirs.h"
#include "util.h"
#include "stream-provider.h"
#include "stream-fd.h"
unix_open(const char *name, char *suffix, struct stream **streamp,
uint8_t dscp OVS_UNUSED)
{
- const char *connect_path = suffix;
+ char *connect_path;
int fd;
+ connect_path = abs_file_name(ovs_rundir(), suffix);
fd = make_unix_socket(SOCK_STREAM, true, NULL, connect_path);
+
if (fd < 0) {
VLOG_DBG("%s: connection failed (%s)", connect_path, strerror(-fd));
+ free(connect_path);
return -fd;
}
+ free(connect_path);
return new_fd_stream(name, fd, check_connection_completion(fd), streamp);
}
punix_open(const char *name OVS_UNUSED, char *suffix,
struct pstream **pstreamp, uint8_t dscp OVS_UNUSED)
{
+ char *bind_path;
int fd, error;
- fd = make_unix_socket(SOCK_STREAM, true, suffix, NULL);
+ bind_path = abs_file_name(ovs_rundir(), suffix);
+ fd = make_unix_socket(SOCK_STREAM, true, bind_path, NULL);
if (fd < 0) {
- VLOG_ERR("%s: binding failed: %s", suffix, strerror(errno));
+ VLOG_ERR("%s: binding failed: %s", bind_path, strerror(errno));
+ free(bind_path);
return errno;
}
error = errno;
VLOG_ERR("%s: listen: %s", name, strerror(error));
close(fd);
+ free(bind_path);
return error;
}
- return new_fd_pstream(name, fd, punix_accept, NULL,
- xstrdup(suffix), pstreamp);
+ return new_fd_pstream(name, fd, punix_accept, NULL, bind_path, pstreamp);
}
static int
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
time_refresh();
now = time_msec();
- msecs = secs * 1000;
+ msecs = secs * 1000LL;
block_sigalrm(&oldsigs);
deadline = now < LLONG_MAX - msecs ? now + msecs : LLONG_MAX;
{
long long int interval = time_msec() - last_wakeup;
- if (interval >= 1000) {
+ if (interval >= 1000 && !warp_offset.tv_sec && !warp_offset.tv_nsec) {
const struct rusage *last_rusage = get_recent_rusage();
struct rusage rusage;
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
error = jsonrpc_transact_block(client, request, &reply);
if (error) {
VLOG_WARN("error communicating with %s: %s", jsonrpc_get_name(client),
- strerror(error));
+ ovs_retval_to_string(error));
return error;
}
int
log_2_ceil(uint32_t n)
{
- return log_2_floor(n) + !IS_POW2(n);
+ return log_2_floor(n) + !is_pow2(n);
}
/* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0. */
return msg;
}
+/* Set debugging levels. Abort with an error message if 's' is invalid. */
+void
+vlog_set_levels_from_string_assert(const char *s)
+{
+ char *error = vlog_set_levels_from_string(s);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+}
+
/* If 'arg' is null, configure maximum verbosity. Otherwise, sets
* configuration according to 'arg' (see vlog_set_levels_from_string()). */
void
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
enum vlog_level vlog_get_level(const struct vlog_module *, enum vlog_facility);
void vlog_set_levels(struct vlog_module *,
enum vlog_facility, enum vlog_level);
-char *vlog_set_levels_from_string(const char *);
+char *vlog_set_levels_from_string(const char *) WARN_UNUSED_RESULT;
+void vlog_set_levels_from_string_assert(const char *);
char *vlog_get_levels(void);
bool vlog_is_enabled(const struct vlog_module *, enum vlog_level);
bool vlog_should_drop(const struct vlog_module *, enum vlog_level,
/* Main process closed the IPC socket. Exit cleanly. */
break;
} else if (error != EAGAIN) {
- VLOG_FATAL("RPC receive failed (%s)", strerror(error));
+ VLOG_FATAL("RPC receive failed (%s)", ovs_retval_to_string(error));
}
poll_fd_wait(server_sock, POLLIN);
/* State that should be cleared from one connection to the next. */
/* OpenFlow state. */
- enum nx_role role; /* Role. */
+ enum ofp12_controller_role role; /* Role. */
enum ofputil_protocol protocol; /* Current protocol variant. */
enum nx_packet_in_format packet_in_format; /* OFPT_PACKET_IN format. */
snoop_preference(const struct ofconn *ofconn)
{
switch (ofconn->role) {
- case NX_ROLE_MASTER:
+ case OFPCR12_ROLE_MASTER:
return 3;
- case NX_ROLE_OTHER:
+ case OFPCR12_ROLE_EQUAL:
return 2;
- case NX_ROLE_SLAVE:
+ case OFPCR12_ROLE_SLAVE:
return 1;
+ case OFPCR12_ROLE_NOCHANGE:
default:
/* Shouldn't happen. */
return 0;
return ofconn->type;
}
+/* If a master election id is defined, stores it into '*idp' and returns
+ * true. Otherwise, stores UINT64_MAX into '*idp' and returns false. */
+bool
+ofconn_get_master_election_id(const struct ofconn *ofconn, uint64_t *idp)
+{
+ *idp = (ofconn->connmgr->master_election_id_defined
+ ? ofconn->connmgr->master_election_id
+ : UINT64_MAX);
+ return ofconn->connmgr->master_election_id_defined;
+}
+
/* Sets the master election id.
*
* Returns true if successful, false if the id is stale
/* Returns the role configured for 'ofconn'.
*
- * The default role, if no other role has been set, is NX_ROLE_OTHER. */
-enum nx_role
+ * The default role, if no other role has been set, is OFPCR12_ROLE_EQUAL. */
+enum ofp12_controller_role
ofconn_get_role(const struct ofconn *ofconn)
{
return ofconn->role;
}
-/* Changes 'ofconn''s role to 'role'. If 'role' is NX_ROLE_MASTER then any
- * existing master is demoted to a slave. */
+/* Changes 'ofconn''s role to 'role'. If 'role' is OFPCR12_ROLE_MASTER then
+ * any existing master is demoted to a slave. */
void
-ofconn_set_role(struct ofconn *ofconn, enum nx_role role)
+ofconn_set_role(struct ofconn *ofconn, enum ofp12_controller_role role)
{
- if (role == NX_ROLE_MASTER) {
+ if (role == OFPCR12_ROLE_MASTER) {
struct ofconn *other;
HMAP_FOR_EACH (other, hmap_node, &ofconn->connmgr->controllers) {
- if (other->role == NX_ROLE_MASTER) {
- other->role = NX_ROLE_SLAVE;
+ if (other->role == OFPCR12_ROLE_MASTER) {
+ other->role = OFPCR12_ROLE_SLAVE;
}
}
}
struct ofmonitor *monitor, *next_monitor;
int i;
- ofconn->role = NX_ROLE_OTHER;
+ ofconn->role = OFPCR12_ROLE_EQUAL;
ofconn_set_protocol(ofconn, OFPUTIL_P_NONE);
ofconn->packet_in_format = NXPIF_OPENFLOW10;
return false;
}
- async_config = (ofconn->role == NX_ROLE_SLAVE
+ async_config = (ofconn->role == OFPCR12_ROLE_SLAVE
? ofconn->slave_async_config
: ofconn->master_async_config);
if (!(async_config[type] & (1u << reason))) {
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Individual connections to OpenFlow controllers. */
enum ofconn_type ofconn_get_type(const struct ofconn *);
+bool ofconn_get_master_election_id(const struct ofconn *, uint64_t *idp);
bool ofconn_set_master_election_id(struct ofconn *, uint64_t);
-enum nx_role ofconn_get_role(const struct ofconn *);
-void ofconn_set_role(struct ofconn *, enum nx_role);
+enum ofp12_controller_role ofconn_get_role(const struct ofconn *);
+void ofconn_set_role(struct ofconn *, enum ofp12_controller_role);
enum ofputil_protocol ofconn_get_protocol(const struct ofconn *);
void ofconn_set_protocol(struct ofconn *, enum ofputil_protocol);
int error;
*in_bandp = NULL;
- error = netdev_open(local_name, "system", &local_netdev);
+ error = netdev_open(local_name, "internal", &local_netdev);
if (error) {
VLOG_ERR("failed to initialize in-band control: cannot open "
"datapath local port %s (%s)", local_name, strerror(error));
#include <errno.h>
-#include "autopath.h"
#include "bond.h"
#include "bundle.h"
#include "byte-order.h"
uint32_t sflow_odp_port; /* Output port for composing sFlow action. */
uint16_t user_cookie_offset;/* Used for user_action_cookie fixup. */
bool exit; /* No further actions should be processed. */
- struct flow orig_flow; /* Copy of original flow. */
};
static void action_xlate_ctx_init(struct action_xlate_ctx *,
struct list bundle_node; /* In struct ofbundle's "ports" list. */
struct cfm *cfm; /* Connectivity Fault Management, if any. */
tag_type tag; /* Tag associated with this port. */
- uint32_t bond_stable_id; /* stable_id to use as bond slave, or 0. */
bool may_enable; /* May be enabled in bonds. */
long long int carrier_seq; /* Carrier status changes. */
struct tnl_port *tnl_port; /* Tunnel handle, or null. */
struct timer next_expiration;
struct hmap odp_to_ofport_map; /* ODP port to ofport mapping. */
- struct sset tnl_backers; /* Set of dpif ports backing tunnels. */
+ struct simap tnl_backers; /* Set of dpif ports backing tunnels. */
/* Facet revalidation flags applying to facets which use this backer. */
enum revalidate_reason need_revalidate; /* Revalidate every facet. */
static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
const struct ofpbuf *, ovs_be16 initial_tci,
struct ds *);
-static bool may_dpif_port_del(struct ofport_dpif *);
/* Packet processing. */
static void update_learning_table(struct ofproto_dpif *,
struct tag_set revalidate_set = backer->revalidate_set;
bool need_revalidate = backer->need_revalidate;
struct ofproto_dpif *ofproto;
+ struct simap_node *node;
+ struct simap tmp_backers;
+
+ /* Handle tunnel garbage collection. */
+ simap_init(&tmp_backers);
+ simap_swap(&backer->tnl_backers, &tmp_backers);
+
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ struct ofport_dpif *iter;
+
+ if (backer != ofproto->backer) {
+ continue;
+ }
+
+ HMAP_FOR_EACH (iter, up.hmap_node, &ofproto->up.ports) {
+ const char *dp_port;
+
+ if (!iter->tnl_port) {
+ continue;
+ }
+
+ dp_port = netdev_vport_get_dpif_port(iter->up.netdev);
+ node = simap_find(&tmp_backers, dp_port);
+ if (node) {
+ simap_put(&backer->tnl_backers, dp_port, node->data);
+ simap_delete(&tmp_backers, node);
+ node = simap_find(&backer->tnl_backers, dp_port);
+ } else {
+ node = simap_find(&backer->tnl_backers, dp_port);
+ if (!node) {
+ uint32_t odp_port = UINT32_MAX;
+
+ if (!dpif_port_add(backer->dpif, iter->up.netdev,
+ &odp_port)) {
+ simap_put(&backer->tnl_backers, dp_port, odp_port);
+ node = simap_find(&backer->tnl_backers, dp_port);
+ }
+ }
+ }
+
+ iter->odp_port = node ? node->data : OVSP_NONE;
+ if (tnl_port_reconfigure(&iter->up, iter->odp_port,
+ &iter->tnl_port)) {
+ backer->need_revalidate = REV_RECONFIGURE;
+ }
+ }
+ }
+
+ SIMAP_FOR_EACH (node, &tmp_backers) {
+ dpif_port_del(backer->dpif, node->data);
+ }
+ simap_destroy(&tmp_backers);
switch (backer->need_revalidate) {
case REV_RECONFIGURE: COVERAGE_INC(rev_reconfigure); break;
drop_key_clear(backer);
}
+ /* Clear the revalidation flags. */
+ tag_set_init(&backer->revalidate_set);
+ backer->need_revalidate = 0;
+
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
struct facet *facet;
continue;
}
- /* Clear the revalidation flags. */
- tag_set_init(&backer->revalidate_set);
- backer->need_revalidate = 0;
-
HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
if (need_revalidate
|| tag_set_intersects(&revalidate_set, facet->tags)) {
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node,
&all_ofproto_dpifs) {
- if (sset_contains(&ofproto->backer->tnl_backers, devname)) {
+ if (simap_contains(&ofproto->backer->tnl_backers, devname)) {
goto next;
}
}
drop_key_clear(backer);
hmap_destroy(&backer->drop_keys);
- sset_destroy(&backer->tnl_backers);
+ simap_destroy(&backer->tnl_backers);
hmap_destroy(&backer->odp_to_ofport_map);
node = shash_find(&all_dpif_backers, backer->type);
free(backer->type);
hmap_init(&backer->drop_keys);
timer_set_duration(&backer->next_expiration, 1000);
backer->need_revalidate = 0;
- sset_init(&backer->tnl_backers);
+ simap_init(&backer->tnl_backers);
tag_set_init(&backer->revalidate_set);
*backerp = backer;
if (odp_port_to_ofp_port(ofproto, port->odp_port) != OFPP_NONE) {
VLOG_ERR("port %s already has an OpenFlow port number",
dpif_port.name);
+ dpif_port_destroy(&dpif_port);
return EBUSY;
}
hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
hash_int(port->odp_port, 0));
}
+ dpif_port_destroy(&dpif_port);
if (ofproto->sflow) {
dpif_sflow_add_port(ofproto->sflow, port_, port->odp_port);
const char *dp_port_name = netdev_vport_get_dpif_port(port->up.netdev);
const char *devname = netdev_get_name(port->up.netdev);
- if (dpif_port_exists(ofproto->backer->dpif, dp_port_name)
- && may_dpif_port_del(port)) {
+ if (dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
/* The underlying device is still there, so delete it. This
* happens when the ofproto is being destroyed, since the caller
* assumes that removal of attached ports will happen as part of
* destruction. */
- dpif_port_del(ofproto->backer->dpif, port->odp_port);
- sset_find_and_delete(&ofproto->backer->tnl_backers, dp_port_name);
+ if (!port->tnl_port) {
+ dpif_port_del(ofproto->backer->dpif, port->odp_port);
+ }
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
}
if (port->odp_port != OVSP_NONE && !port->tnl_port) {
static bool
bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
- struct lacp_slave_settings *lacp,
- uint32_t bond_stable_id)
+ struct lacp_slave_settings *lacp)
{
struct ofport_dpif *port;
lacp_slave_register(bundle->lacp, port, lacp);
}
- port->bond_stable_id = bond_stable_id;
-
return true;
}
ok = true;
for (i = 0; i < s->n_slaves; i++) {
if (!bundle_add_port(bundle, s->slaves[i],
- s->lacp ? &s->lacp_slaves[i] : NULL,
- s->bond_stable_ids ? s->bond_stable_ids[i] : 0)) {
+ s->lacp ? &s->lacp_slaves[i] : NULL)) {
ok = false;
}
}
}
LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
- bond_slave_register(bundle->bond, port, port->bond_stable_id,
- port->up.netdev);
+ bond_slave_register(bundle->bond, port, port->up.netdev);
}
} else {
bond_destroy(bundle->bond);
}
if (!dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
- int error = dpif_port_add(ofproto->backer->dpif, netdev, NULL);
+ uint32_t port_no = UINT32_MAX;
+ int error;
+
+ error = dpif_port_add(ofproto->backer->dpif, netdev, &port_no);
if (error) {
return error;
}
+ if (netdev_get_tunnel_config(netdev)) {
+ simap_put(&ofproto->backer->tnl_backers, dp_port_name, port_no);
+ }
}
if (netdev_get_tunnel_config(netdev)) {
sset_add(&ofproto->ghost_ports, devname);
- sset_add(&ofproto->backer->tnl_backers, dp_port_name);
} else {
sset_add(&ofproto->ports, devname);
}
return 0;
}
-/* Returns true if the odp_port backing 'ofport' may be deleted from the
- * datapath. In most cases, this function simply returns true. However, for
- * tunnels it's possible that multiple ofports use the same odp_port, in which
- * case we need to keep the odp_port backer around until the last ofport is
- * deleted. */
-static bool
-may_dpif_port_del(struct ofport_dpif *ofport)
-{
- struct dpif_backer *backer = ofproto_dpif_cast(ofport->up.ofproto)->backer;
- struct ofproto_dpif *ofproto_iter;
-
- if (!ofport->tnl_port) {
- return true;
- }
-
- HMAP_FOR_EACH (ofproto_iter, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
- struct ofport_dpif *iter;
-
- if (backer != ofproto_iter->backer) {
- continue;
- }
-
- HMAP_FOR_EACH (iter, up.hmap_node, &ofproto_iter->up.ports) {
- if (ofport == iter) {
- continue;
- }
-
- if (!strcmp(netdev_vport_get_dpif_port(ofport->up.netdev),
- netdev_vport_get_dpif_port(iter->up.netdev))) {
- return false;
- }
- }
- }
-
- return true;
-}
-
static int
port_del(struct ofproto *ofproto_, uint16_t ofp_port)
{
sset_find_and_delete(&ofproto->ghost_ports,
netdev_get_name(ofport->up.netdev));
- if (may_dpif_port_del(ofport)) {
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
+ if (!ofport->tnl_port) {
error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port);
if (!error) {
- const char *dpif_port;
-
/* The caller is going to close ofport->up.netdev. If this is a
* bonded port, then the bond is using that netdev, so remove it
* from the bond. The client will need to reconfigure everything
* after deleting ports, so then the slave will get re-added. */
- dpif_port = netdev_vport_get_dpif_port(ofport->up.netdev);
- sset_find_and_delete(&ofproto->backer->tnl_backers, dpif_port);
bundle_remove(&ofport->up);
}
}
uint32_t bucket;
uint32_t offset;
bool ghost;
+
+ struct ofproto_port port;
+ bool has_port;
};
static int
const struct sset *sset;
struct sset_node *node;
+ if (state->has_port) {
+ ofproto_port_destroy(&state->port);
+ state->has_port = false;
+ }
sset = state->ghost ? &ofproto->ghost_ports : &ofproto->ports;
while ((node = sset_at_position(sset, &state->bucket, &state->offset))) {
int error;
- error = port_query_by_name(ofproto_, node->name, port);
- if (error != ENODEV) {
+ error = port_query_by_name(ofproto_, node->name, &state->port);
+ if (!error) {
+ *port = state->port;
+ state->has_port = true;
+ return 0;
+ } else if (error != ENODEV) {
return error;
}
}
{
struct port_dump_state *state = state_;
+ if (state->has_port) {
+ ofproto_port_destroy(&state->port);
+ }
free(state);
return 0;
}
static enum slow_path_reason
process_special(struct ofproto_dpif *ofproto, const struct flow *flow,
- const struct ofpbuf *packet)
+ const struct ofport_dpif *ofport, const struct ofpbuf *packet)
{
- struct ofport_dpif *ofport = get_ofp_port(ofproto, flow->in_port);
-
if (!ofport) {
return 0;
- }
-
- if (ofport->cfm && cfm_should_process_flow(ofport->cfm, flow)) {
+ } else if (ofport->cfm && cfm_should_process_flow(ofport->cfm, flow)) {
if (packet) {
cfm_process_heartbeat(ofport->cfm, packet);
}
stp_process_packet(ofport, packet);
}
return SLOW_STP;
+ } else {
+ return 0;
}
- return 0;
}
static struct flow_miss *
-flow_miss_find(struct hmap *todo, const struct flow *flow, uint32_t hash)
+flow_miss_find(struct hmap *todo, const struct ofproto_dpif *ofproto,
+ const struct flow *flow, uint32_t hash)
{
struct flow_miss *miss;
HMAP_FOR_EACH_WITH_HASH (miss, hmap_node, hash, todo) {
- if (flow_equal(&miss->flow, flow)) {
+ if (miss->ofproto == ofproto && flow_equal(&miss->flow, flow)) {
return miss;
}
}
/* Add other packets to a to-do list. */
hash = flow_hash(&miss->flow, 0);
- existing_miss = flow_miss_find(&todo, &miss->flow, hash);
+ existing_miss = flow_miss_find(&todo, ofproto, &miss->flow, hash);
if (!existing_miss) {
hmap_insert(&todo, &miss->hmap_node, hash);
miss->ofproto = ofproto;
}
/* "action" upcalls need a closer look. */
- memcpy(&cookie, &upcall->userdata, sizeof(cookie));
+ if (!upcall->userdata) {
+ VLOG_WARN_RL(&rl, "action upcall missing cookie");
+ return BAD_UPCALL;
+ }
+ if (nl_attr_get_size(upcall->userdata) != sizeof(cookie)) {
+ VLOG_WARN_RL(&rl, "action upcall cookie has unexpected size %zu",
+ nl_attr_get_size(upcall->userdata));
+ return BAD_UPCALL;
+ }
+ memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof(cookie));
switch (cookie.type) {
case USER_ACTION_COOKIE_SFLOW:
return SFLOW_UPCALL;
case USER_ACTION_COOKIE_UNSPEC:
default:
- VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata);
+ VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64,
+ nl_attr_get_u64(upcall->userdata));
return BAD_UPCALL;
}
}
return;
}
- memcpy(&cookie, &upcall->userdata, sizeof(cookie));
+ memcpy(&cookie, nl_attr_get(upcall->userdata), sizeof(cookie));
dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
odp_in_port, &cookie);
}
dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
netdev_vport_inc_tx(ofport->up.netdev, &stats);
odp_put_tunnel_action(&flow.tunnel, &odp_actions);
+ odp_put_skb_mark_action(flow.skb_mark, &odp_actions);
} else {
odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
flow.vlan_tci);
\f
/* OpenFlow to datapath action translation. */
+static bool may_receive(const struct ofport_dpif *, struct action_xlate_ctx *);
static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
struct action_xlate_ctx *);
static void xlate_normal(struct action_xlate_ctx *);
ofpbuf_use_stack(&buf, stub, stub_size);
if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT32_MAX);
- odp_put_userspace_action(pid, &cookie, &buf);
+ odp_put_userspace_action(pid, &cookie, sizeof cookie, &buf);
} else {
put_userspace_action(ofproto, &buf, flow, &cookie);
}
pid = dpif_port_get_pid(ofproto->backer->dpif,
ofp_port_to_odp_port(ofproto, flow->in_port));
- return odp_put_userspace_action(pid, cookie, odp_actions);
+ return odp_put_userspace_action(pid, cookie, sizeof *cookie, odp_actions);
}
static void
/* If 'struct flow' gets additional metadata, we'll need to zero it out
* before traversing a patch port. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
if (!ofport) {
xlate_report(ctx, "Nonexistent output port");
struct ofport_dpif *peer = ofport_get_peer(ofport);
struct flow old_flow = ctx->flow;
const struct ofproto_dpif *peer_ofproto;
+ enum slow_path_reason special;
+ struct ofport_dpif *in_port;
if (!peer) {
xlate_report(ctx, "Nonexistent patch port peer");
ctx->flow.metadata = htonll(0);
memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
memset(ctx->flow.regs, 0, sizeof ctx->flow.regs);
- xlate_table_action(ctx, ctx->flow.in_port, 0, true);
+
+ in_port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
+ special = process_special(ctx->ofproto, &ctx->flow, in_port,
+ ctx->packet);
+ if (special) {
+ ctx->slow |= special;
+ } else if (!in_port || may_receive(in_port, ctx)) {
+ if (!in_port || stp_forward_in_state(in_port->stp_state)) {
+ xlate_table_action(ctx, ctx->flow.in_port, 0, true);
+ } else {
+ /* Forwarding is disabled by STP. Let OFPP_NORMAL and the
+ * learning action look at the packet, then drop it. */
+ struct flow old_base_flow = ctx->base_flow;
+ size_t old_size = ctx->odp_actions->size;
+ xlate_table_action(ctx, ctx->flow.in_port, 0, true);
+ ctx->base_flow = old_base_flow;
+ ctx->odp_actions->size = old_size;
+ }
+ }
+
ctx->flow = old_flow;
ctx->ofproto = ofproto_dpif_cast(ofport->up.ofproto);
if (packet->l2 && packet->l3) {
struct eth_header *eh;
+ uint16_t mpls_depth;
eth_pop_vlan(packet);
eh = packet->l2;
- /* If the Ethernet type is less than ETH_TYPE_MIN, it's likely an 802.2
- * LLC frame. Calculating the Ethernet type of these frames is more
- * trouble than seems appropriate for a simple assertion. */
- ovs_assert(ntohs(eh->eth_type) < ETH_TYPE_MIN
- || eh->eth_type == ctx->flow.dl_type);
-
memcpy(eh->eth_src, ctx->flow.dl_src, sizeof eh->eth_src);
memcpy(eh->eth_dst, ctx->flow.dl_dst, sizeof eh->eth_dst);
eth_push_vlan(packet, ctx->flow.vlan_tci);
}
+ mpls_depth = eth_mpls_depth(packet);
+
+ if (mpls_depth < ctx->flow.mpls_depth) {
+ push_mpls(packet, ctx->flow.dl_type, ctx->flow.mpls_lse);
+ } else if (mpls_depth > ctx->flow.mpls_depth) {
+ pop_mpls(packet, ctx->flow.dl_type);
+ } else if (mpls_depth) {
+ set_mpls_lse(packet, ctx->flow.mpls_lse);
+ }
+
if (packet->l4) {
if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
packet_set_ipv4(packet, ctx->flow.nw_src, ctx->flow.nw_dst,
ofpbuf_delete(packet);
}
+static void
+execute_mpls_push_action(struct action_xlate_ctx *ctx, ovs_be16 eth_type)
+{
+ ovs_assert(eth_type_mpls(eth_type));
+
+ if (ctx->base_flow.mpls_depth) {
+ ctx->flow.mpls_lse &= ~htonl(MPLS_BOS_MASK);
+ ctx->flow.mpls_depth++;
+ } else {
+ ovs_be32 label;
+ uint8_t tc, ttl;
+
+ if (ctx->flow.dl_type == htons(ETH_TYPE_IPV6)) {
+ label = htonl(0x2); /* IPV6 Explicit Null. */
+ } else {
+ label = htonl(0x0); /* IPV4 Explicit Null. */
+ }
+ tc = (ctx->flow.nw_tos & IP_DSCP_MASK) >> 2;
+ ttl = ctx->flow.nw_ttl ? ctx->flow.nw_ttl : 0x40;
+ ctx->flow.mpls_lse = set_mpls_lse_values(ttl, tc, 1, label);
+ ctx->flow.encap_dl_type = ctx->flow.dl_type;
+ ctx->flow.mpls_depth = 1;
+ }
+ ctx->flow.dl_type = eth_type;
+}
+
+static void
+execute_mpls_pop_action(struct action_xlate_ctx *ctx, ovs_be16 eth_type)
+{
+ ovs_assert(eth_type_mpls(ctx->flow.dl_type));
+ ovs_assert(!eth_type_mpls(eth_type));
+
+ if (ctx->flow.mpls_depth) {
+ ctx->flow.mpls_depth--;
+ ctx->flow.mpls_lse = htonl(0);
+ if (!ctx->flow.mpls_depth) {
+ ctx->flow.dl_type = eth_type;
+ ctx->flow.encap_dl_type = htons(0);
+ }
+ }
+}
+
static bool
compose_dec_ttl(struct action_xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
{
ovs_be64 tun_id;
};
-static void
-xlate_autopath(struct action_xlate_ctx *ctx,
- const struct ofpact_autopath *ap)
-{
- uint16_t ofp_port = ap->port;
- struct ofport_dpif *port = get_ofp_port(ctx->ofproto, ofp_port);
-
- if (!port || !port->bundle) {
- ofp_port = OFPP_NONE;
- } else if (port->bundle->bond) {
- /* Autopath does not support VLAN hashing. */
- struct ofport_dpif *slave = bond_choose_output_slave(
- port->bundle->bond, &ctx->flow, 0, &ctx->tags);
- if (slave) {
- ofp_port = slave->up.ofp_port;
- }
- }
- nxm_reg_load(&ap->dst, ofp_port, &ctx->flow);
-}
-
static bool
slave_enabled_cb(uint16_t ofp_port, void *ofproto_)
{
do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
struct action_xlate_ctx *ctx)
{
- const struct ofport_dpif *port;
bool was_evictable = true;
const struct ofpact *a;
- port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
- if (port && !may_receive(port, ctx)) {
- /* Drop this flow. */
- return;
- }
-
if (ctx->rule) {
/* Don't let the rule we're working on get evicted underneath us. */
was_evictable = ctx->rule->up.evictable;
nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
break;
+ case OFPACT_PUSH_MPLS:
+ execute_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)->ethertype);
+ break;
+
+ case OFPACT_POP_MPLS:
+ execute_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
+ break;
+
case OFPACT_DEC_TTL:
if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
goto out;
multipath_execute(ofpact_get_MULTIPATH(a), &ctx->flow);
break;
- case OFPACT_AUTOPATH:
- xlate_autopath(ctx, ofpact_get_AUTOPATH(a));
- break;
-
case OFPACT_BUNDLE:
ctx->ofproto->has_bundle_action = true;
xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
}
out:
- /* We've let OFPP_NORMAL and the learning action look at the packet,
- * so drop it now if forwarding is disabled. */
- if (port && !stp_forward_in_state(port->stp_state)) {
- ofpbuf_clear(ctx->odp_actions);
- add_sflow_action(ctx);
- }
if (ctx->rule) {
ctx->rule->up.evictable = was_evictable;
}
static bool hit_resubmit_limit;
enum slow_path_reason special;
+ struct ofport_dpif *in_port;
+ struct flow orig_flow;
COVERAGE_INC(ofproto_dpif_xlate);
if (ctx->ofproto->has_mirrors || hit_resubmit_limit) {
/* Do this conditionally because the copy is expensive enough that it
- * shows up in profiles.
- *
- * We keep orig_flow in 'ctx' only because I couldn't make GCC 4.4
- * believe that I wasn't using it without initializing it if I kept it
- * in a local variable. */
- ctx->orig_flow = ctx->flow;
+ * shows up in profiles. */
+ orig_flow = ctx->flow;
}
if (ctx->flow.nw_frag & FLOW_NW_FRAG_ANY) {
}
}
- special = process_special(ctx->ofproto, &ctx->flow, ctx->packet);
+ in_port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
+ special = process_special(ctx->ofproto, &ctx->flow, in_port, ctx->packet);
if (special) {
ctx->slow |= special;
} else {
uint32_t local_odp_port;
add_sflow_action(ctx);
- do_xlate_actions(ofpacts, ofpacts_len, ctx);
+
+ if (!in_port || may_receive(in_port, ctx)) {
+ do_xlate_actions(ofpacts, ofpacts_len, ctx);
+
+ /* We've let OFPP_NORMAL and the learning action look at the
+ * packet, so drop it now if forwarding is disabled. */
+ if (in_port && !stp_forward_in_state(in_port->stp_state)) {
+ ofpbuf_clear(ctx->odp_actions);
+ add_sflow_action(ctx);
+ }
+ }
if (ctx->max_resubmit_trigger && !ctx->resubmit_hook) {
if (!hit_resubmit_limit) {
} else if (!VLOG_DROP_ERR(&trace_rl)) {
struct ds ds = DS_EMPTY_INITIALIZER;
- ofproto_trace(ctx->ofproto, &ctx->orig_flow, ctx->packet,
+ ofproto_trace(ctx->ofproto, &orig_flow, ctx->packet,
initial_tci, &ds);
VLOG_ERR("Trace triggered by excessive resubmit "
"recursion:\n%s", ds_cstr(&ds));
}
}
if (ctx->ofproto->has_mirrors) {
- add_mirror_actions(ctx, &ctx->orig_flow);
+ add_mirror_actions(ctx, &orig_flow);
}
fix_sflow_action(ctx);
}
uint16_t flags = ntohs(osc->flags);
if (ofconn_get_type(ofconn) != OFCONN_PRIMARY
- || ofconn_get_role(ofconn) != NX_ROLE_SLAVE) {
+ || ofconn_get_role(ofconn) != OFPCR12_ROLE_SLAVE) {
enum ofp_config_flags cur = ofproto->frag_handling;
enum ofp_config_flags next = flags & OFPC_FRAG_MASK;
reject_slave_controller(struct ofconn *ofconn)
{
if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
- && ofconn_get_role(ofconn) == NX_ROLE_SLAVE) {
+ && ofconn_get_role(ofconn) == OFPCR12_ROLE_SLAVE) {
return OFPERR_OFPBRC_EPERM;
} else {
return 0;
static enum ofperr
handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
{
- struct ofputil_role_request rr;
+ struct ofputil_role_request request;
+ struct ofputil_role_request reply;
struct ofpbuf *buf;
- uint32_t role;
enum ofperr error;
- error = ofputil_decode_role_message(oh, &rr);
+ error = ofputil_decode_role_message(oh, &request);
if (error) {
return error;
}
- if (rr.request_current_role_only) {
- role = ofconn_get_role(ofconn); /* NX_ROLE_* */
- goto reply;
- }
-
- role = rr.role;
-
- if (ofconn_get_role(ofconn) != role
- && ofconn_has_pending_opgroups(ofconn)) {
- return OFPROTO_POSTPONE;
- }
+ if (request.role != OFPCR12_ROLE_NOCHANGE) {
+ if (ofconn_get_role(ofconn) != request.role
+ && ofconn_has_pending_opgroups(ofconn)) {
+ return OFPROTO_POSTPONE;
+ }
- if (rr.have_generation_id) {
- if (!ofconn_set_master_election_id(ofconn, rr.generation_id)) {
- return OFPERR_OFPRRFC_STALE;
+ if (request.have_generation_id
+ && !ofconn_set_master_election_id(ofconn, request.generation_id)) {
+ return OFPERR_OFPRRFC_STALE;
}
- }
- ofconn_set_role(ofconn, role);
+ ofconn_set_role(ofconn, request.role);
+ }
-reply:
- buf = ofputil_encode_role_reply(oh, role);
+ reply.role = ofconn_get_role(ofconn);
+ reply.have_generation_id = ofconn_get_master_election_id(
+ ofconn, &reply.generation_id);
+ buf = ofputil_encode_role_reply(oh, &reply);
ofconn_send_reply(ofconn, buf);
return 0;
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ofproto_controller_info {
bool is_connected;
- enum nx_role role;
+ enum ofp12_controller_role role;
struct {
const char *keys[4];
const char *values[4];
bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */
struct bond_settings *bond; /* Must be nonnull iff if n_slaves > 1. */
- uint32_t *bond_stable_ids; /* Array of n_slaves elements. */
struct lacp_settings *lacp; /* Nonnull to enable LACP. */
struct lacp_slave_settings *lacp_slaves; /* Array of n_slaves elements. */
*
* Ability to generate actions on input for ECN
* Ability to generate metadata for packet-outs
- * IPsec using skb mark.
* VXLAN.
* Multicast group management (possibly).
* Disallow netdevs with names like "gre64_system" to prevent collisions. */
VLOG_DEFINE_THIS_MODULE(tunnel);
+/* skb mark used for IPsec tunnel packets */
+#define IPSEC_MARK 1
+
struct tnl_match {
ovs_be64 in_key;
ovs_be32 ip_src;
ovs_be32 ip_dst;
uint32_t odp_port;
- bool in_key_present;
+ uint32_t skb_mark;
bool in_key_flow;
};
tnl_port->match.in_key = cfg->in_key;
tnl_port->match.ip_src = cfg->ip_src;
tnl_port->match.ip_dst = cfg->ip_dst;
- tnl_port->match.in_key_present = cfg->in_key_present;
+ tnl_port->match.skb_mark = cfg->ipsec ? IPSEC_MARK : 0;
tnl_port->match.in_key_flow = cfg->in_key_flow;
tnl_port->match.odp_port = odp_port;
match.ip_src = flow->tunnel.ip_dst;
match.ip_dst = flow->tunnel.ip_src;
match.in_key = flow->tunnel.tun_id;
- match.in_key_present = flow->tunnel.flags & FLOW_TNL_F_KEY;
+ match.skb_mark = flow->skb_mark;
tnl_port = tnl_find(&match);
if (!tnl_port) {
flow->tunnel.ip_src = tnl_port->match.ip_src;
flow->tunnel.ip_dst = tnl_port->match.ip_dst;
+ flow->skb_mark = tnl_port->match.skb_mark;
if (!cfg->out_key_flow) {
flow->tunnel.tun_id = cfg->out_key;
ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src),
IP_ARGS(match->ip_dst));
- if (match->in_key_present) {
- if (match->in_key_flow) {
- ds_put_cstr(ds, ", key=flow");
- } else {
- ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key));
- }
+ if (match->in_key_flow) {
+ ds_put_cstr(ds, ", key=flow");
+ } else {
+ ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key));
}
ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port);
+ ds_put_format(ds, ", skb mark=%"PRIu32, match->skb_mark);
}
static void
struct ds ds = DS_EMPTY_INITIALIZER;
tnl_match_fmt(&tnl_port->match, &ds);
- VLOG_DBG("%s tunnel port %s (%s)", action, tnl_port_get_name(tnl_port),
- ds_cstr(&ds));
+ VLOG_INFO("%s tunnel port %s (%s)", action,
+ tnl_port_get_name(tnl_port), ds_cstr(&ds));
ds_destroy(&ds);
}
}
}
file->n_transactions++;
- /* If it has been at least COMPACT_MIN_MSEC millseconds since the last time
- * we compacted (or at least COMPACT_RETRY_MSEC since the last time we
+ /* If it has been at least COMPACT_MIN_MSEC ms since the last time we
+ * compacted (or at least COMPACT_RETRY_MSEC ms since the last time we
* tried), and if there are at least 100 transactions in the database, and
* if the database is at least 10 MB, then compact the database. */
if (time_msec() >= file->next_compact
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
? shash_find_data(&table_schema->columns, column)
: NULL);
if (column_schema) {
- const struct ovsdb_error *error;
const struct ovsdb_type *type;
+ struct ovsdb_error *error;
struct ovsdb_datum datum;
type = &column_schema->type;
ds_init(&s);
ovsdb_datum_to_string(&datum, type, &s);
value_string = ds_steal_cstr(&s);
+ } else {
+ ovsdb_error_destroy(error);
}
}
if (!value_string) {
if self.enum:
literals = [value.toEnglish(escapeLiteral)
for value in self.enum.values]
- if len(literals) == 2:
+ if len(literals) == 1:
+ english = 'must be %s' % (literals[0])
+ elif len(literals) == 2:
english = 'either %s or %s' % (literals[0], literals[1])
else:
english = 'one of %s, %s, or %s' % (literals[0],
return errno.EAFNOSUPPORT, None
suffix = name.split(":", 1)[1]
+ if name.startswith("unix:"):
+ suffix = ovs.util.abs_file_name(ovs.dirs.RUNDIR, suffix)
error, sock = cls._open(suffix, dscp)
if error:
return error, None
return errno.EAFNOSUPPORT, None
bind_path = name[6:]
+ if name.startswith("punix:"):
+ bind_path = ovs.util.abs_file_name(ovs.dirs.RUNDIR, bind_path)
error, sock = ovs.socket_util.make_unix_socket(socket.SOCK_STREAM,
True, bind_path, None)
if error:
# without warranty of any kind.
/var/log/openvswitch/*.log {
+ daily
+ compress
sharedscripts
missingok
postrotate
tests/ovs-ofctl.at \
tests/odp.at \
tests/multipath.at \
- tests/autopath.at \
tests/lacp.at \
tests/learn.at \
tests/vconn.at \
tests/test-ovsdb.py \
tests/test-reconnect.py \
tests/MockXenAPI.py \
+ tests/test-unix-socket.py \
tests/test-unixctl.py \
tests/test-vlog.py
EXTRA_DIST += $(CHECK_PYFILES)
+++ /dev/null
-AT_BANNER([autopath link selection])
-
-AT_SETUP([autopath basic])
-AT_CHECK([ovs-ofctl parse-flow 'actions=autopath(1, NXM_NX_REG0[[]])'], [0],
- [usable protocols: any
-chosen protocol: OpenFlow10-table_id
-OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(1,NXM_NX_REG0[[]])
-], [stderr])
-AT_CHECK([ovs-ofctl parse-flow 'actions=autopath(2, NXM_NX_REG0[[2..30]])'], [0],
- [usable protocols: any
-chosen protocol: OpenFlow10-table_id
-OFPT_FLOW_MOD (xid=0x1): ADD actions=autopath(2,NXM_NX_REG0[[2..30]])
-], [stderr])
-AT_CHECK([[sed 's/^[^|]*|[^|]*|//' stderr]], [0], [dnl
-autopath|WARN|The autopath action is deprecated and may be removed in February 2013. Please email dev@openvswitch.org with concerns.
-])
-AT_CLEANUP
-
-AT_SETUP([autopath action missing argument])
-AT_CHECK([ovs-ofctl parse-flow actions=autopath], [1], [],
- [ovs-ofctl: : not enough arguments to autopath action
-])
-AT_CLEANUP
-
-AT_SETUP([autopath action bad port])
-AT_CHECK([ovs-ofctl parse-flow 'actions=autopath(bad, NXM_NX_REG0[[]])'], [1], [],
- [ovs-ofctl: bad, NXM_NX_REG0[[]]: bad port number
-])
-AT_CLEANUP
-
-AT_SETUP([autopath action destination too narrow])
-AT_CHECK([ovs-ofctl parse-flow 'actions=autopath(1,NXM_NX_REG0[[0..7]])'], [1], [],
- [ovs-ofctl: 1,NXM_NX_REG0[[0..7]]: 8-bit destination field has 256 possible values, less than required 65536
-])
-AT_CLEANUP
-
AT_SETUP([JSON-RPC request and successful reply - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
AT_CHECK([$PYTHON $srcdir/test-jsonrpc.py --detach --pidfile=`pwd`/pid listen punix:socket])
AT_CHECK([test -s pid])
AT_CHECK([kill -0 `cat pid`])
AT_SETUP([JSON-RPC request and error reply - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
AT_CHECK([$PYTHON $srcdir/test-jsonrpc.py --detach --pidfile=`pwd`/pid listen punix:socket])
AT_CHECK([test -s pid])
AT_CHECK([kill -0 `cat pid`])
AT_SETUP([JSON-RPC notification - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
AT_CHECK([$PYTHON $srcdir/test-jsonrpc.py --detach --pidfile=`pwd`/pid listen punix:socket])
AT_CHECK([test -s pid])
# When a daemon dies it deletes its pidfile, so make a copy.
AT_BANNER([JSON-RPC - C])
AT_SETUP([JSON-RPC request and successful reply])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
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_CLEANUP
AT_SETUP([JSON-RPC request and error reply])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
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_CLEANUP
AT_SETUP([JSON-RPC notification])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
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_SETUP([lacp - multi port config])
OVS_VSWITCHD_START([dnl
add-bond br0 bond p1 p2 --\
- set Port bond lacp=active \
+ set Port bond lacp=active bond-mode=active-backup \
other_config:lacp-time="fast" \
other_config:lacp-system-id=11:22:33:44:55:66 \
other_config:lacp-system-priority=54321 --\
OVS_VSWITCHD_STOP
AT_CLEANUP
+# In this use of a learn action, the first packet in the flow creates
+# a new flow that changes the behavior of subsequent packets in the
+# flow.
+AT_SETUP([learning action - self-modifying flow])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], 1, 2, 3)
+
+# Set up flow table for TCPv4 port learning.
+AT_CHECK([[ovs-ofctl add-flow br0 'actions=load:3->NXM_NX_REG0[0..15],learn(table=0,priority=65535,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2']])
+
+# Trace some packets arriving. The particular packets don't matter.
+for i in 1 2 3 4 5 6 7 8 9 10; 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)'
+done
+
+# Check for the learning entry.
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0],
+[[ n_packets=1, n_bytes=60, actions=load:0x3->NXM_NX_REG0[0..15],learn(table=0,priority=65535,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2
+ priority=65535,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:05 actions=output:3
+NXST_FLOW reply:
+]])
+
+# Check that the first packet went out port 2 and the rest out port 3.
+AT_CHECK(
+ [(ovs-ofctl dump-ports br0 2; ovs-ofctl dump-ports br0 3) | STRIP_XIDS], [0],
+ [OFPST_PORT reply: 1 ports
+ port 2: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
+ tx pkts=1, bytes=60, drop=0, errs=0, coll=0
+OFPST_PORT reply: 1 ports
+ port 3: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
+ tx pkts=9, bytes=540, drop=0, errs=0, coll=0
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([learning action - fin_timeout feature])
# This is a totally artificial use of the "learn" action. The only purpose
# is to check that specifying fin_idle_timeout or fin_hard_timeout causes
AT_CHECK([test-util testname], [0], [], [])
AT_CLEANUP])
-AT_SETUP([test unix socket -- short pathname])
+AT_SETUP([test unix socket, short pathname - C])
AT_CHECK([test-unix-socket x])
AT_CLEANUP
dnl is about 100 bytes. On Linux, we work around this by indirecting through
dnl a directory fd using /proc/self/fd/<dirfd>. We do not have a workaround
dnl for other platforms, so we skip the test there.
-AT_SETUP([test unix socket -- long pathname])
-AT_CHECK([dnl
- case `uname` in dnl (
- *[[lL]]inux*)
- exit 0
- ;; dnl (
- *)
- dnl Magic exit code to tell Autotest to skip this test.
- exit 77
- ;;
- esac
-])
+AT_SETUP([test unix socket, long pathname - C])
+AT_SKIP_IF([test ! -d /proc/self/fd])
+dnl Linux has a 108 byte limit; this is 150 bytes long.
+longname=012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+mkdir $longname
+cd $longname
+AT_CHECK([test-unix-socket ../$longname/socket socket])
+AT_CLEANUP
+
+AT_SETUP([test unix socket, short pathname - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_CHECK([$PYTHON $srcdir/test-unix-socket.py x])
+AT_CLEANUP
+
+dnl Unix sockets with long names are problematic because the name has to
+dnl go in a fixed-length field in struct sockaddr_un. Generally the limit
+dnl is about 100 bytes. On Linux, we work around this by indirecting through
+dnl a directory fd using /proc/self/fd/<dirfd>. We do not have a workaround
+dnl for other platforms, so we skip the test there.
+AT_SETUP([test unix socket, long pathname - Python])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+AT_SKIP_IF([test ! -d /proc/self/fd])
dnl Linux has a 108 byte limit; this is 150 bytes long.
-mkdir 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
-cd 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
-AT_CHECK([test-unix-socket ../012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/socket socket])
+longname=012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+mkdir $longname
+cd $longname
+AT_CHECK([$PYTHON $abs_srcdir/test-unix-socket.py ../$longname/socket socket])
AT_CLEANUP
AT_SETUP([ovs_assert])
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18)
skb_mark(0x1234),in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=3,ttl=64,bos=1)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=1)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=0)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8848),mpls(label=1000,tc=4,ttl=200,bos=1)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8848),mpls(label=1000,tc=4,ttl=200,bos=0)
])
(echo '# Valid forms without tun_id or VLAN header.'
sed 's/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
s/$/)/' odp-base.txt
+ echo
+ echo '# Valid forms with MPLS header.'
+ sed 's/\(eth([[^)]]*),?\)/\1,eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt
+
+ echo
+ echo '# Valid forms with MPLS multicast header.'
+ sed 's/\(eth([[^)]]*),?\)/\1,eth_type(0x8848),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt
+
echo
echo '# Valid forms with QoS priority.'
sed 's/^/skb_priority(0x1234),/' odp-base.txt
userspace(pid=9765,slow_path(cfm))
userspace(pid=9765,slow_path(cfm,match))
userspace(pid=9123,userdata=0x815309)
+userspace(pid=1234567,userdata(0102030405060708090a0b0c0d0e0f))
set(tun_id(0x7f10354))
set(in_port(2))
set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))
# 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=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
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
03 19 00 18 00 00 00 02 00 00 00 03 00 00 00 00 \
-00 00 00 00 00 00 00 00 \
+12 34 56 78 ab cd ef 90 \
"], [0], [dnl
-OFPT_ROLE_REPLY (OF1.2) (xid=0x2): role=slave
+OFPT_ROLE_REPLY (OF1.2) (xid=0x2): role=slave generation_id=1311768467750121360
])
AT_CLEANUP
cookie=0x7 table=5 in_port=84 actions=load:5->NXM_NX_REG4[[]],load:6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,controller,resubmit(85,6)
cookie=0x8 table=6 in_port=85 actions=mod_tp_src:85,controller,resubmit(86,7)
cookie=0x9 table=7 in_port=86 actions=mod_tp_dst:86,controller,controller
+cookie=0xa dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
+cookie=0xa dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
+cookie=0xa dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
+cookie=0xb dl_src=50:55:55:55:55:55 dl_type=0x8847 actions=load:1000->OXM_OF_MPLS_LABEL[[]],controller
+cookie=0xd dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,controller
+cookie=0xc dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:1000->OXM_OF_MPLS_LABEL[[]],load:7->OXM_OF_MPLS_TC[[]],controller
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
])
+dnl Modified MPLS controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:42,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)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07
+])
+
+dnl Modified MPLS controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=3,ttl=64,bos=1)
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:43,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:64,bos:0),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:64,bos:0),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:64,bos:0),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07
+])
+
+dnl Modified MPLS controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:44,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07
+])
+
+dnl Modified MPLS actions.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:55:55:55:55:55,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
+mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
+mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
+mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07
+])
+
+dnl Modified MPLS ipv6 controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=70:77:77:77:77:77,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mplsm(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mplsm(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mplsm(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07
+])
+
+
+dnl Modified MPLS pop action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=60:66:66:66:66:66,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=3,ttl=100,bos=1),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered)
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered)
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered)
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+])
+
dnl Checksum TCP.
AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --no-chdir --pidfile 2> ofctl_monitor.log])
cookie=0x7, table=5, n_packets=2, n_bytes=120, in_port=84 actions=load:0x5->NXM_NX_REG4[[]],load:0x6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,CONTROLLER:65535,resubmit(85,6)
cookie=0x8, table=6, n_packets=2, n_bytes=120, in_port=85 actions=mod_tp_src:85,CONTROLLER:65535,resubmit(86,7)
cookie=0x9, table=7, n_packets=2, n_bytes=120, in_port=86 actions=mod_tp_dst:86,CONTROLLER:65535,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xb, n_packets=3, n_bytes=180, dl_src=50:55:55:55:55:55,dl_type=0x8847 actions=load:0x3e8->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535
+ cookie=0xc, n_packets=3, n_bytes=180, dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:0x3e8->OXM_OF_MPLS_LABEL[[]],load:0x7->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xd, n_packets=3, n_bytes=180, dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,CONTROLLER:65535
n_packets=3, n_bytes=180, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535
NXST_FLOW reply:
])
--id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \
engine_id=1 engine_type=2 active_timeout=10 add-id-to-interface=false
-ON_EXIT([kill `test-netflow.pid`])
+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])
AT_CHECK([ovs-appctl time/stop])
AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 protocols=[[OpenFlow10,OpenFlow12,OpenFlow13]] fail-mode=secure -- $1 m4_if([$2], [], [], [| perl $srcdir/uuidfilt.pl])], [0], [$2])
])
+m4_divert_push([PREPARE_TESTS])
+check_logs () {
+ sed -n "$1
+/|WARN|/p
+/|ERR|/p
+/|EMER|/p" ovs-vswitchd.log ovsdb-server.log
+}
+m4_divert_pop([PREPARE_TESTS])
+
+# OVS_VSWITCHD_STOP([WHITELIST])
+#
+# Gracefully stops ovs-vswitchd and ovsdb-server, checking their log files
+# for messages with severity WARN or higher and signaling an error if any
+# is present. The optional WHITELIST may contain shell-quoted "sed"
+# commands to delete any warnings that are actually expected, e.g.:
+#
+# OVS_VSWITCHD_STOP(["/expected error/d"])
m4_define([OVS_VSWITCHD_STOP],
- [AT_CHECK([ovs-appctl -t ovs-vswitchd exit])
+ [AT_CHECK([check_logs $1])
+ AT_CHECK([ovs-appctl -t ovs-vswitchd exit])
AT_CHECK([ovs-appctl -t ovsdb-server exit])])
# ADD_OF_PORTS(BRIDGE, OF_PORT[, OF_PORT...])
# Become slave (generation_id is initially undefined, so 2^63+2 should not be stale)
ovs-appctl -t ovs-ofctl ofctl/send 031800180000000300000003000000008000000000000002
echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x3): role=slave generation_id=9223372036854775810"
-echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x3): role=slave"
+echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x3): role=slave generation_id=9223372036854775810"
# Try to become the master using a stale generation ID
ovs-appctl -t ovs-ofctl ofctl/send 031800180000000400000002000000000000000000000002
# Become master using a valid generation ID
ovs-appctl -t ovs-ofctl ofctl/send 031800180000000500000002000000000000000000000001
echo >>expout "send: OFPT_ROLE_REQUEST (OF1.2) (xid=0x5): role=master generation_id=1"
-echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x5): role=master"
+echo >>expout "OFPT_ROLE_REPLY (OF1.2) (xid=0x5): role=master generation_id=1"
ovs-appctl -t ovs-ofctl ofctl/barrier
echo >>expout "OFPT_BARRIER_REPLY (OF1.2) (xid=0x3):"
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], [stderr])
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])
m4_define([OVSDB_CHECK_EXECUTION],
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb execute execution positive $5])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
AT_CHECK([test-ovsdb execute "`$2`" m4_foreach([txn], [$3], [ 'txn'])],
[0], [stdout], [])
AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [$4])
AT_BANNER([OVSDB -- interface description language (IDL)])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
# OVSDB_CHECK_IDL_C(TITLE, [PRE-IDL-TXN], TRANSACTIONS, OUTPUT, [KEYWORDS],
# [FILTER])
#
m4_define([OVSDB_CHECK_IDL_C],
[AT_SETUP([$1 - C])
AT_KEYWORDS([ovsdb server idl positive $5])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
[0], [stdout], [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])
[AT_SETUP([$1 - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
AT_KEYWORDS([ovsdb server idl positive Python $5])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
[0], [stdout], [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])
[AT_SETUP([$1 - Python tcp])
AT_SKIP_IF([test $HAVE_PYTHON = no])
AT_KEYWORDS([ovsdb server idl positive Python with tcp socket $5])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
[0], [stdout], [ignore])
AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
dnl
dnl Creates an empty database named $1.
m4_define([OVSDB_INIT],
- [AT_CHECK(
+ [OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+ AT_CHECK(
[ovsdb-tool create $1 $abs_top_srcdir/vswitchd/vswitch.ovsschema],
[0], [stdout], [ignore])
AT_CHECK(
m4_define([OVSDB_CHECK_MONITOR],
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb server monitor positive $9])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
$2 > schema
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
m4_foreach([txn], [$3],
# TITLE is provided to AT_SETUP and KEYWORDS to AT_KEYWORDS.
m4_define([OVSDB_CHECK_EXECUTION],
[AT_SETUP([$1])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
AT_KEYWORDS([ovsdb server positive unix $5])
$2 > schema
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
\f
AT_SETUP([truncating corrupted database log])
AT_KEYWORDS([ovsdb server positive unix])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
ordinal_schema > schema
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
dnl Do one transaction and save the output.
AT_SETUP([truncating database log with bad transaction])
AT_KEYWORDS([ovsdb server positive unix])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
ordinal_schema > schema
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
dnl Do one transaction and save the output.
AT_SETUP([ovsdb-client get-schema-version])
AT_KEYWORDS([ovsdb server positive])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
ordinal_schema > schema
AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
AT_SETUP([database multiplexing implementation])
AT_KEYWORDS([ovsdb server positive])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
ordinal_schema > schema1
constraint_schema > schema2
AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore])
AT_SETUP([compacting online])
AT_KEYWORDS([ovsdb server compact])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
ordinal_schema > schema
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
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb server positive ssl $5])
AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
$2 > schema
AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
SSL_PORT=`cat stdout`
m4_define([OVSDB_CHECK_EXECUTION],
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb server positive tcp $5])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
$2 > schema
AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
TCP_PORT=`cat stdout`
m4_define([OVSDB_CHECK_EXECUTION],
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb server positive transient $5])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
$2 > schema
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
m4_foreach([txn], [$3],
m4_define([OVSDB_CHECK_EXECUTION],
[AT_SETUP([$1])
AT_KEYWORDS([ovsdb file positive $5])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
$2 > schema
touch .db.~lock~
AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
AT_SETUP([ovsdb-tool compact])
AT_KEYWORDS([ovsdb file positive])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
ordinal_schema > schema
dnl Make sure that "ovsdb-tool create" works with a dangling symlink,
dnl creating the target of the symlink rather than replacing the symlink
AT_SETUP([ovsdb-tool convert -- removing a column])
AT_KEYWORDS([ovsdb file positive])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
ordinal_schema > schema
AT_DATA([new-schema],
[[{"name": "ordinals",
AT_SETUP([ovsdb-tool convert -- adding a column])
AT_KEYWORDS([ovsdb file positive])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
AT_DATA([schema],
[[{"name": "ordinals",
"tables": {
for (i = 0; i < N_FLOWS; i++) {
random_bytes(&flows[i], sizeof flows[i]);
memset(flows[i].zeros, 0, sizeof flows[i].zeros);
+ flows[i].mpls_depth = 0;
flows[i].regs[0] = OFPP_NONE;
}
HMAP_FOR_EACH (table, hmap_node, &cls->tables) {
const struct cls_rule *head;
+ unsigned int max_priority = 0;
+ unsigned int max_count = 0;
assert(!hmap_is_empty(&table->rules));
unsigned int prev_priority = UINT_MAX;
const struct cls_rule *rule;
+ if (head->priority > max_priority) {
+ max_priority = head->priority;
+ max_count = 1;
+ } else if (head->priority == max_priority) {
+ ++max_count;
+ }
+
found_rules++;
LIST_FOR_EACH (rule, list, &head->list) {
assert(rule->priority < prev_priority);
+ assert(rule->priority <= table->max_priority);
+
prev_priority = rule->priority;
found_rules++;
found_dups++;
assert(classifier_find_rule_exactly(cls, rule) == rule);
}
}
+ assert(table->max_priority == max_priority);
+ assert(table->max_count == max_count);
}
assert(found_tables == hmap_count(&cls->tables));
random_bytes(&flow, sizeof flow);
memset(flow.zeros, 0, sizeof flow.zeros);
+ flow.mpls_depth = 0;
mp.max_link = n - 1;
multipath_execute(&mp, &flow);
/*
- * Copyright (c) 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ds in;
ds_init(&in);
- vlog_set_levels_from_string("odp_util:console:dbg");
+ vlog_set_levels_from_string_assert("odp_util:console:dbg");
while (!ds_get_test_line(&in, stdin)) {
enum odp_key_fitness fitness;
struct ofpbuf odp_key;
struct ds in;
ds_init(&in);
- vlog_set_levels_from_string("odp_util:console:dbg");
+ vlog_set_levels_from_string_assert("odp_util:console:dbg");
while (!ds_get_test_line(&in, stdin)) {
struct ofpbuf odp_actions;
struct ds out;
--- /dev/null
+#
+# Copyright (c) 2010, 2012, 2013 Nicira, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# 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 os
+import signal
+import socket
+import sys
+
+import ovs.socket_util
+
+def main(argv):
+ if len(argv) not in (2, 3):
+ sys.stderr.write("usage: %s SOCKETNAME1 [SOCKETNAME2]", argv[0])
+ sys.exit(1)
+
+ sockname1 = argv[1]
+ if len(argv) > 2:
+ sockname2 = argv[2]
+ else:
+ sockname2 = sockname1
+
+ signal.signal(signal.SIGALRM, signal.SIG_DFL)
+ signal.alarm(5)
+
+ # Create a listening socket under name 'sockname1'.
+ error, sock1 = ovs.socket_util.make_unix_socket(socket.SOCK_STREAM, False,
+ sockname1, None)
+ if error:
+ sys.stderr.write("%s: bind failed (%s)" % (sockname1,
+ os.strerror(error)))
+ sock1.listen(1)
+
+ # Connect to 'sockname2' (which should be the same file, perhaps under a
+ # different name).
+ error, sock2 = ovs.socket_util.make_unix_socket(socket.SOCK_STREAM, False,
+ None, sockname2)
+ if error:
+ sys.stderr.write("%s: connect failed (%s)" % (sockname2,
+ os.strerror(error)))
+
+if __name__ == '__main__':
+ main(sys.argv)
m4_include([tests/ovs-ofctl.at])
m4_include([tests/odp.at])
m4_include([tests/multipath.at])
-m4_include([tests/autopath.at])
m4_include([tests/learn.at])
m4_include([tests/vconn.at])
m4_include([tests/file_name.at])
ovs-appctl: ovs-vswitchd: server returned an error
])
-OVS_VSWITCHD_STOP
+OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"])
AT_CLEANUP
AT_SETUP([tunnel - ECN decapsulation])
Invalid flow
ovs-appctl: ovs-vswitchd: server returned an error
])
-OVS_VSWITCHD_STOP
+OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"])
AT_CLEANUP
AT_SETUP([tunnel - key match])
[send-short-hello],
[send-invalid-version-hello]],
[AT_SETUP([$1 vconn - m4_bpatsubst(testname, [-], [ ])])
+ OVS_RUNDIR=`pwd`; export OVS_RUNDIR
m4_if([$1], [ssl], [
AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
AT_CHECK([cp $abs_top_builddir/tests/testpki*.pem .])])
.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.
.
.IP "\fB\-\-entries=\fIlist\fR"
Use the capabilities specified in a comma-separated list.
.
+.IP "\fB\-\-log\-days=\fIdays\fR"
+Include the logs rotated in the previous \fIdays\fR days in the debug bundle.
+The number of log files included has a big impact on the eventual bundle size.
+The default value is 20 days.
+.
.IP "\fB\-\-output=\fIfiletype\fR"
Generate a debug bundle with the specified file type. Options include
\fBtar\fR, \fBtar.gz\fR, \fBtar.bz2\fR, and \fBzip\fR.
.
+.IP "\fB\-\-ovs\fR"
+Use only Open vSwitch relevant capabilities.
+.
.IP "\fB\-\-silent\fR"
Suppress output.
.
PROC_MOUNTS = '/proc/mounts'
ISCSI_CONF = '/etc/iscsi/iscsid.conf'
ISCSI_INITIATOR = '/etc/iscsi/initiatorname.iscsi'
-LVM_CACHE = '/etc/lvm/cache/.cache'
-LVM_CONFIG = '/etc/lvm/lvm.conf'
PROC_CPUINFO = '/proc/cpuinfo'
PROC_MEMINFO = '/proc/meminfo'
PROC_IOPORTS = '/proc/ioports'
PROC_CMDLINE = '/proc/cmdline'
PROC_CONFIG = '/proc/config.gz'
PROC_USB_DEV = '/proc/bus/usb/devices'
-PROC_XEN_BALLOON = '/proc/xen/balloon'
PROC_NET_BONDING_DIR = '/proc/net/bonding'
IFCFG_RE = re.compile(r'^.*/ifcfg-.*')
ROUTE_RE = re.compile(r'^.*/route-.*')
OPENVSWITCH_DEFAULT_CONTROLLER = '/etc/default/openvswitch-controller'
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/'
VAR_LOG_CORE_DIR = '/var/log/core'
-X11_LOGS_DIR = VAR_LOG_DIR
-X11_LOGS_RE = re.compile(r'.*/Xorg\..*$')
-X11_AUTH_DIR = '/root/'
-X11_AUTH_RE = re.compile(r'.*/\.((Xauthority)|(serverauth\.[0-9]*))$')
YUM_LOG = '/var/log/yum.log'
YUM_REPOS_DIR = '/etc/yum.repos.d'
-PAM_DIR = '/etc/pam.d'
-KRB5_CONF = '/etc/krb5.conf'
#
# External programs
ETHTOOL = 'ethtool'
FDISK = 'fdisk'
FIND = 'find'
-HDPARM = 'hdparm'
IFCONFIG = 'ifconfig'
IPTABLES = 'iptables'
ISCSIADM = 'iscsiadm'
LOSETUP = 'losetup'
LS = 'ls'
LSPCI = 'lspci'
-LVDISPLAY = 'lvdisplay'
-LVS = 'lvs'
MD5SUM = 'md5sum'
MODINFO = 'modinfo'
MPPUTIL = 'mppUtil'
OVS_VSCTL = 'ovs-vsctl'
OVS_APPCTL = 'ovs-appctl'
PS = 'ps'
-PVS = 'pvs'
ROUTE = 'route'
RPM = 'rpm'
SG_MAP = 'sg_map'
SYSCTL = 'sysctl'
TC = 'tc'
UPTIME = 'uptime'
-VGS = 'vgs'
-VGSCAN = 'vgscan'
ZCAT = 'zcat'
#
CAP_XML_ELEMENT = 'capability'
-CAP_BLOBS = 'blobs'
CAP_BOOT_LOADER = 'boot-loader'
-CAP_COLLECTD_LOGS = 'collectd-logs'
CAP_DISK_INFO = 'disk-info'
-CAP_FIRSTBOOT = 'firstboot'
CAP_HARDWARE_INFO = 'hardware-info'
-CAP_HDPARM_T = 'hdparm-t'
-CAP_HIGH_AVAILABILITY = 'high-availability'
CAP_KERNEL_INFO = 'kernel-info'
CAP_LOSETUP_A = 'loopback-devices'
CAP_MULTIPATH = 'multipath'
CAP_NETWORK_CONFIG = 'network-config'
CAP_NETWORK_STATUS = 'network-status'
-CAP_OEM = 'oem'
-CAP_PAM = 'pam'
+CAP_OPENVSWITCH_LOGS = 'ovs-system-logs'
CAP_PROCESS_LIST = 'process-list'
-CAP_PERSISTENT_STATS = 'persistent-stats'
CAP_SYSTEM_LOGS = 'system-logs'
CAP_SYSTEM_SERVICES = 'system-services'
-CAP_VNCTERM = 'vncterm'
-CAP_WLB = 'wlb'
-CAP_X11_LOGS = 'X11'
-CAP_X11_AUTH = 'X11-auth'
CAP_YUM = 'yum'
KB = 1024
cap_sizes = {}
unlimited_data = False
dbg = False
+# Default value for the number of rotated logs.
+log_days = 20
def cap(key, pii=PII_MAYBE, min_size=-1, max_size=-1, min_time=-1,
max_time=-1, mime=MIME_TEXT, checked=True, hidden=False):
cap_sizes[key] = 0
-cap(CAP_BLOBS, PII_NO, max_size=5*MB)
cap(CAP_BOOT_LOADER, PII_NO, max_size=3*KB,
max_time=5)
-cap(CAP_COLLECTD_LOGS, PII_MAYBE, max_size=50*MB,
- max_time=5)
cap(CAP_DISK_INFO, PII_MAYBE, max_size=50*KB,
max_time=20)
-cap(CAP_FIRSTBOOT, PII_YES, min_size=60*KB, max_size=80*KB)
cap(CAP_HARDWARE_INFO, PII_MAYBE, max_size=30*KB,
max_time=20)
-cap(CAP_HDPARM_T, PII_NO, min_size=0, max_size=5*KB,
- min_time=20, max_time=90, checked=False, hidden=True)
-cap(CAP_HIGH_AVAILABILITY, PII_MAYBE, max_size=5*MB)
cap(CAP_KERNEL_INFO, PII_MAYBE, max_size=120*KB,
max_time=5)
cap(CAP_LOSETUP_A, PII_MAYBE, max_size=KB, max_time=5)
max_time=10)
cap(CAP_NETWORK_CONFIG, PII_IF_CUSTOMIZED,
min_size=0, max_size=40*KB)
-cap(CAP_NETWORK_STATUS, PII_YES, max_size=50*KB,
+cap(CAP_NETWORK_STATUS, PII_YES, max_size=50*MB,
max_time=30)
-cap(CAP_PAM, PII_NO, max_size=50*KB)
-cap(CAP_PERSISTENT_STATS, PII_MAYBE, max_size=50*MB,
- max_time=60)
+cap(CAP_OPENVSWITCH_LOGS, PII_MAYBE, max_size=-1,
+ max_time=5)
cap(CAP_PROCESS_LIST, PII_YES, max_size=30*KB,
max_time=20)
-cap(CAP_SYSTEM_LOGS, PII_MAYBE, max_size=50*MB,
+cap(CAP_SYSTEM_LOGS, PII_MAYBE, max_size=200*MB,
max_time=5)
cap(CAP_SYSTEM_SERVICES, PII_NO, max_size=5*KB,
max_time=20)
-cap(CAP_VNCTERM, PII_MAYBE, checked = False)
-cap(CAP_WLB, PII_NO, max_size=3*MB,
- max_time=20)
-cap(CAP_X11_LOGS, PII_NO, max_size=100*KB)
-cap(CAP_X11_AUTH, PII_NO, max_size=100*KB)
cap(CAP_YUM, PII_IF_CUSTOMIZED, max_size=10*KB,
max_time=30)
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, binary=False):
if cap in entries:
if not label:
if isinstance(args, list):
label = ' '.join(a)
else:
label = args
- data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter}
+ data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter,
+ 'binary': binary}
def file_output(cap, path_list, newest_first=False):
"""
t = str(func).split()
data[label] = {'cap': cap, 'func': func}
+def log_output(cap, logs, newest_first=False):
+ global log_days
+ file_output(cap, logs)
+ file_output(cap,
+ ['%s.%d' % (f, n) for n in range(1, log_days+1) for f in logs], \
+ newest_first=newest_first)
+ file_output(cap,
+ ['%s.%d.gz' % (f, n) for n in range(1, log_days+1) for f in logs], \
+ newest_first=newest_first)
+
def collect_data():
process_lists = {}
v['output'] = StringIOmtime()
if not process_lists.has_key(cap):
process_lists[cap] = []
- process_lists[cap].append(ProcOutput(v['cmd_args'], caps[cap][MAX_TIME], v['output'], v['filter']))
+ process_lists[cap].append(
+ ProcOutput(v['cmd_args'], caps[cap][MAX_TIME], v['output'],
+ v['filter'], v['binary']))
elif v.has_key('filename') and v['filename'].startswith('/proc/'):
# proc files must be read into memory
try:
def main(argv=None):
global ANSWER_YES_TO_ALL, SILENT_MODE
- global entries, data, dbg, unlimited_data
+ global entries, data, dbg, unlimited_data, log_days
# Filter flags
only_ovs_info = False
return 1
output_file = None
- output_type = 'tar.bz2'
+ output_type = 'tar.gz'
output_fd = -1
if argv is None:
(options, params) = getopt.gnu_getopt(
argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=',
'output=', 'outfd=', 'outfile=', 'all', 'unlimited',
- 'debug', 'ovs'])
+ 'debug', 'ovs', 'log-days='])
except getopt.GetoptError, opterr:
print >>sys.stderr, opterr
return 2
only_ovs_info = True
collect_all_info = False
+ if k == '--log-days':
+ log_days = int(v)
+
if len(params) != 1:
print >>sys.stderr, "Invalid additional arguments", str(params)
return 2
cmd_output(CAP_BOOT_LOADER, [LS, '-lR', '/boot'])
cmd_output(CAP_BOOT_LOADER, [MD5SUM, BOOT_KERNEL, BOOT_INITRD], label='vmlinuz-initrd.md5sum')
- tree_output(CAP_COLLECTD_LOGS, COLLECTD_LOGS_DIR)
cmd_output(CAP_DISK_INFO, [FDISK, '-l'])
file_output(CAP_DISK_INFO, [PROC_PARTITIONS, PROC_MOUNTS])
file_output(CAP_DISK_INFO, [FSTAB, ISCSI_CONF, ISCSI_INITIATOR])
cmd_output(CAP_DISK_INFO, [DF, '-alT'])
cmd_output(CAP_DISK_INFO, [DF, '-alTi'])
- for d in disk_list():
- cmd_output(CAP_DISK_INFO, [HDPARM, '-I', '/dev/%s' % d])
if len(pidof('iscsid')) != 0:
cmd_output(CAP_DISK_INFO, [ISCSIADM, '-m', 'node'])
- cmd_output(CAP_DISK_INFO, [VGSCAN])
- cmd_output(CAP_DISK_INFO, [PVS])
- cmd_output(CAP_DISK_INFO, [VGS])
- cmd_output(CAP_DISK_INFO, [LVS])
- file_output(CAP_DISK_INFO, [LVM_CACHE, LVM_CONFIG])
cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_host'])
cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_disk'])
cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/fc_transport'])
cmd_output(CAP_DISK_INFO, [SG_MAP, '-x'])
func_output(CAP_DISK_INFO, 'scsi-hosts', dump_scsi_hosts)
- cmd_output(CAP_DISK_INFO, [LVDISPLAY, '--map'])
file_output(CAP_HARDWARE_INFO, [PROC_CPUINFO, PROC_MEMINFO, PROC_IOPORTS, PROC_INTERRUPTS])
cmd_output(CAP_HARDWARE_INFO, [DMIDECODE])
file_output(CAP_HARDWARE_INFO, [PROC_USB_DEV, PROC_SCSI])
file_output(CAP_HARDWARE_INFO, [SYSCONFIG_HWCONF])
cmd_output(CAP_HARDWARE_INFO, [LS, '-lR', '/dev'])
- # FIXME IDE?
- 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,
PROC_FILESYSTEMS, PROC_CMDLINE])
f.close()
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 not p.startswith('vif') and not p.startswith('tap'):
+ cmd_output(CAP_NETWORK_STATUS, [ETHTOOL, 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 e:
pass
- tree_output(CAP_PAM, PAM_DIR)
- file_output(CAP_PAM, [KRB5_CONF])
-
cmd_output(CAP_PROCESS_LIST, [PS, 'wwwaxf', '-eo', 'pid,tty,stat,time,nice,psr,pcpu,pmem,nwchan,wchan:25,args'], label='process-tree')
func_output(CAP_PROCESS_LIST, 'fd_usage', fd_usage)
- logs = ([ VAR_LOG_DIR + x for x in
- [ 'crit.log', 'kern.log', 'daemon.log', 'user.log',
- 'syslog', 'messages', 'secure', 'debug', 'dmesg', 'boot' ]]
- + [ OPENVSWITCH_LOG_DIR + x for x in
- [ 'ovs-vswitchd.log', 'ovsdb-server.log',
- 'ovs-xapi-sync.log', 'ovs-monitor-ipsec.log' ]])
- file_output(CAP_SYSTEM_LOGS, logs)
- file_output(CAP_SYSTEM_LOGS,
- [ '%s.%d' % (f, n) for n in range(20) for f in logs ])
- file_output(CAP_SYSTEM_LOGS,
- [ '%s.%d.gz' % (f, n) for n in range(20) for f in logs ])
+ system_logs = ([ VAR_LOG_DIR + x for x in
+ ['crit.log', 'kern.log', 'daemon.log', 'user.log',
+ 'syslog', 'messages', 'secure', 'debug', 'dmesg', 'boot']])
+ ovs_logs = ([ OPENVSWITCH_LOG_DIR + x for x in
+ ['ovs-vswitchd.log', 'ovsdb-server.log',
+ 'ovs-xapi-sync.log', 'ovs-monitor-ipsec.log']])
+ log_output(CAP_SYSTEM_LOGS, system_logs)
+ log_output(CAP_OPENVSWITCH_LOGS, ovs_logs)
if not os.path.exists('/var/log/dmesg') and not os.path.exists('/var/log/boot'):
cmd_output(CAP_SYSTEM_LOGS, [DMESG])
cmd_output(CAP_SYSTEM_SERVICES, [CHKCONFIG, '--list'])
- tree_output(CAP_X11_LOGS, X11_LOGS_DIR, X11_LOGS_RE)
- tree_output(CAP_X11_AUTH, X11_AUTH_DIR, X11_AUTH_RE)
tree_output(CAP_SYSTEM_LOGS, VAR_LOG_CORE_DIR)
file_output(CAP_YUM, [YUM_LOG])
else:
make_zip(subdir, output_file)
- clean_tapdisk_logs()
-
if dbg:
print >>sys.stderr, "Category sizes (max, actual):\n"
for c in caps.keys():
cap_sizes[c])
return 0
-def find_tapdisk_logs():
- return glob.glob('/var/log/blktap/*.log*')
-
-def generate_tapdisk_logs():
- for pid in pidof('tapdisk'):
- try:
- os.kill(pid, SIGUSR1)
- output_ts("Including logs for tapdisk process %d" % pid)
- except :
- pass
- # give processes a second to write their logs
- time.sleep(1)
-
-def clean_tapdisk_logs():
- for filename in find_tapdisk_logs():
- try:
- os.remove(filename)
- except :
- pass
-
-def filter_db_pii(str, state):
- if 'in_secret_table' not in state:
- state['in_secret_table'] = False
-
- if str.startswith('<table ') and 'name="secret"' in str:
- state['in_secret_table'] = True
- elif str.startswith('</table>'):
- state['in_secret_table'] = False
-
- if state['in_secret_table'] and str.startswith("<row"): # match only on DB rows
- str = re.sub(r'(value=")[^"]+(")', r'\1REMOVED\2', str)
- return str
-
def dump_scsi_hosts(cap):
output = ''
l = os.listdir('/sys/class/scsi_host')
continue
if el.tagName == "files":
newest_first = getBoolAttr(el, 'newest_first')
- file_output(dir, getText(el.childNodes).split(),
- newest_first=newest_first)
+ if el.getAttribute("type") == "logs":
+ log_output(dir, getText(el.childNodes).split(),
+ newest_first=newest_first)
+ else:
+ file_output(dir, getText(el.childNodes).split(),
+ newest_first=newest_first)
elif el.tagName == "directory":
pattern = el.getAttribute("pattern")
if pattern == '': pattern = None
elif el.tagName == "command":
label = el.getAttribute("label")
if label == '': label = None
- cmd_output(dir, getText(el.childNodes), label)
+ binary = getBoolAttr(el, 'binary')
+ cmd_output(dir, getText(el.childNodes), label, binary=binary)
def make_tar(subdir, suffix, output_fd, output_file):
global SILENT_MODE, data
class ProcOutput:
debug = False
- def __init__(self, command, max_time, inst=None, filter=None):
+ def __init__(self, command, max_time, inst=None, filter=None, binary=False):
self.command = command
self.max_time = max_time
self.inst = inst
self.timeout = int(time.time()) + self.max_time
self.filter = filter
self.filter_state = {}
+ if binary:
+ self.bufsize = 1048576 # 1MB buffer
+ else:
+ self.bufsize = 1 # line buffered
def __del__(self):
self.terminate()
try:
if ProcOutput.debug:
output_ts("Starting '%s'" % self.cmdAsStr())
- self.proc = Popen(self.command, bufsize=1, stdin=dev_null, stdout=PIPE, stderr=dev_null, shell=isinstance(self.command, str))
+ self.proc = Popen(self.command, bufsize=self.bufsize,
+ stdin=dev_null, stdout=PIPE, stderr=dev_null,
+ shell=isinstance(self.command, str))
old = fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_GETFD)
fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
self.running = True
def read_line(self):
assert self.running
- line = self.proc.stdout.readline()
+ if self.bufsize == 1:
+ line = self.proc.stdout.readline()
+ else:
+ line = self.proc.stdout.read(self.bufsize)
if line == '':
# process exited
self.proc.stdout.close()
#! /bin/sh
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
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.
+ # On Linux, ovs-vswitchd needs about one file descriptor per
+ # switch port, so this allows a very large number of switch
+ # ports.
ulimit -n 5000
# Start ovs-vswitchd.
"${script_ofports}" script_ofports
;;
*)
+ script_ofports=""
;;
esac
}
hmap_init(&actions_per_flow);
NL_ATTR_FOR_EACH (a, left, odp_actions.data, odp_actions.size) {
- if (nl_attr_type(a) == OVS_ACTION_ATTR_POP_VLAN) {
+ const struct ovs_action_push_vlan *push;
+ switch(nl_attr_type(a)) {
+ case OVS_ACTION_ATTR_POP_VLAN:
flow.vlan_tci = htons(0);
continue;
- }
-
- if (nl_attr_type(a) == OVS_ACTION_ATTR_PUSH_VLAN) {
- const struct ovs_action_push_vlan *push;
+ case OVS_ACTION_ATTR_PUSH_VLAN:
push = nl_attr_get_unspec(a, sizeof *push);
flow.vlan_tci = push->vlan_tci;
continue;
printf("no vlan: ");
}
+ if (af->flow.mpls_depth) {
+ printf("mpls(label=%"PRIu32",tc=%d,ttl=%d): ",
+ mpls_lse_to_label(af->flow.mpls_lse),
+ mpls_lse_to_tc(af->flow.mpls_lse),
+ mpls_lse_to_ttl(af->flow.mpls_lse));
+ } else {
+ printf("no mpls: ");
+ }
+
ds_clear(&s);
format_odp_actions(&s, af->actions.data, af->actions.size);
puts(ds_cstr(&s));
allows isn't supported at the moment.)
A priority of zero and the tag of zero are used for the new tag.
.
+.IP \fBpush_mpls\fR:\fIethertype\fR
+If the packet does not already contain any MPLS labels, changes the
+packet's Ethertype to \fIethertype\fR, which must be either the MPLS
+unicast Ethertype \fB0x8847\fR or the MPLS multicast Ethertype
+\fB0x8848\fR, and then pushes an initial label stack entry. The label
+stack entry's default label is 2 if the packet contains IPv6 and 0
+otherwise, its default traffic control value is the low 3 bits of the
+packet's DSCP value (0 if the packet is not IP), and its TTL is copied
+from the IP TTL (64 if the packet is not IP).
+.IP
+If the packet does already contain an MPLS label, pushes a new
+outermost label as a copy of the existing outermost label.
+.IP
+There are some limitations in the implementation. \fBpush_mpls\fR
+followed by another \fBpush_mpls\fR will result in the first
+\fBpush_mpls\fR being discarded.
+.
+.IP \fBpop_mpls\fR:\fIethertype\fR
+Strips the outermost MPLS label stack entry. If the MPLS label
+stripped was the only one, changes the ethertype of a packet to
+\fIethertype\fR, which should not ordinarily be an MPLS Ethertype.
+.
+.IP
+There are some limitations in the implementation. \fBpop_mpls\fR
+followed by another \fBpush_mpls\fR without an intermediate
+\fBpush_mpls\fR will result in the first \fBpush_mpls\fR being
+discarded.
+.
.IP \fBmod_dl_src\fB:\fImac\fR
Sets the source Ethernet address to \fImac\fR.
.
.IP
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.
-.IP
-Currently, \fIid\fR should be the OpenFlow port number of an interface on the
-bridge. If it isn't then \fIdst\fB[\fIstart\fB..\fIend\fB]\fR will be
-populated with the OpenFlow port "none". If \fIid\fR is a member of a bond,
-the normal bond selection logic will be used to choose the destination port.
-Otherwise, the register will be populated with \fIid\fR itself.
-.IP
-Refer to \fBnicira\-ext.h\fR for more details.
-.
.IP "\fBbundle(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIslave_type\fB, slaves:[\fIs1\fB, \fIs2\fB, ...])\fR"
Hashes \fIfields\fR using \fIbasis\fR as a universal hash parameter, then
applies the bundle link selection \fIalgorithm\fR to choose one of the listed
break;
}
}
- ovs_assert(IS_POW2(protocol));
+ ovs_assert(is_pow2(protocol));
printf("chosen protocol: %s\n", ofputil_protocol_to_string(protocol));
serial = $dir/serial # serial no file
private_key = $dir/private/cakey.pem# CA private key
RANDFILE = $dir/private/.rand # random number file
-default_days = 365 # how long to certify for
+default_days = 36525 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = md5 # md to use
policy = policy # default policy
-newkey $newkey -keyout private/cakey.pem -out careq.pem \
1>&3 2>&3
openssl ca -config ca.cnf -create_serial -out cacert.pem \
- -days 2191 -batch -keyfile private/cakey.pem -selfsign \
+ -days 36525 -batch -keyfile private/cakey.pem -selfsign \
-infiles careq.pem 1>&3 2>&3
chmod 0700 private/cakey.pem
# Create both the private key and certificate with restricted permissions.
(umask 077 && \
openssl x509 -in "$arg1-req.pem" -out "$arg1-cert.pem.tmp" \
- -signkey "$arg1-privkey.pem" -req -days 2191 -text) 2>&3 || exit $?
+ -signkey "$arg1-privkey.pem" -req -days 36525 -text) 2>&3 || exit $?
# Reset the permissions on the certificate to the user's default.
cat "$arg1-cert.pem.tmp" > "$arg1-cert.pem"
static void port_configure(struct port *);
static struct lacp_settings *port_configure_lacp(struct port *,
struct lacp_settings *);
-static void port_configure_bond(struct port *, struct bond_settings *,
- uint32_t *bond_stable_ids);
+static void port_configure_bond(struct port *, struct bond_settings *);
static bool port_is_synthetic(const struct port *);
static void reconfigure_system_stats(const struct ovsrec_open_vswitch *);
/* Get bond settings. */
if (s.n_slaves > 1) {
s.bond = &bond_settings;
- s.bond_stable_ids = xmalloc(s.n_slaves * sizeof *s.bond_stable_ids);
- port_configure_bond(port, &bond_settings, s.bond_stable_ids);
+ port_configure_bond(port, &bond_settings);
} else {
s.bond = NULL;
- s.bond_stable_ids = NULL;
-
LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
netdev_set_miimon_interval(iface->netdev, 0);
}
free(s.slaves);
free(s.trunks);
free(s.lacp_slaves);
- free(s.bond_stable_ids);
}
/* Pick local port hardware address and datapath ID for 'br'. */
error = netdev_open(port->name, "internal", &netdev);
if (!error) {
- uint16_t ofp_port = if_cfg->ofport;
+ uint16_t fake_ofp_port = if_cfg->ofport;
- ofproto_port_add(br->ofproto, netdev, &ofp_port);
+ ofproto_port_add(br->ofproto, netdev, &fake_ofp_port);
netdev_close(netdev);
} else {
VLOG_WARN("could not open network device %s (%s)",
}
static inline const char *
-nx_role_to_str(enum nx_role role)
+ofp12_controller_role_to_str(enum ofp12_controller_role role)
{
switch (role) {
- case NX_ROLE_OTHER:
+ case OFPCR12_ROLE_EQUAL:
return "other";
- case NX_ROLE_MASTER:
+ case OFPCR12_ROLE_MASTER:
return "master";
- case NX_ROLE_SLAVE:
+ case OFPCR12_ROLE_SLAVE:
return "slave";
+ case OFPCR12_ROLE_NOCHANGE:
default:
return "*** INVALID ROLE ***";
}
}
ovsrec_controller_set_is_connected(cfg, cinfo->is_connected);
- ovsrec_controller_set_role(cfg, nx_role_to_str(cinfo->role));
+ ovsrec_controller_set_role(cfg, ofp12_controller_role_to_str(
+ cinfo->role));
ovsrec_controller_set_status(cfg, &smap);
smap_destroy(&smap);
} else {
if (!strncmp(c->target, "unix:", 5)) {
/* Connect to a listening socket */
whitelist = xasprintf("unix:%s/", ovs_rundir());
- if (!equal_pathnames(c->target, whitelist,
- strlen(whitelist))) {
+ if (strchr(c->target, '/') &&
+ !equal_pathnames(c->target, whitelist,
+ strlen(whitelist))) {
+ /* Absolute path specified, but not in ovs_rundir */
VLOG_ERR_RL(&rl, "bridge %s: Not connecting to socket "
"controller \"%s\" due to possibility for "
"remote exploit. Instead, specify socket "
}
static void
-port_configure_bond(struct port *port, struct bond_settings *s,
- uint32_t *bond_stable_ids)
+port_configure_bond(struct port *port, struct bond_settings *s)
{
const char *detect_s;
struct iface *iface;
int miimon_interval;
- size_t i;
s->name = port->name;
s->balance = BM_AB;
s->fake_iface = port->cfg->bond_fake_iface;
- i = 0;
LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- long long stable_id;
-
- 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;
- }
- bond_stable_ids[i++] = stable_id;
-
netdev_set_miimon_interval(iface->netdev, miimon_interval);
}
}
{"name": "Open_vSwitch",
- "version": "6.11.3",
- "cksum": "2234602985 17310",
+ "version": "7.0.0",
+ "cksum": "3537583872 17299",
"tables": {
"Open_vSwitch": {
"columns": {
"min": 0, "max": 1}},
"bond_mode": {
"type": {"key": {"type": "string",
- "enum": ["set", ["balance-tcp", "balance-slb", "active-backup", "stable"]]},
+ "enum": ["set", ["balance-tcp", "balance-slb", "active-backup"]]},
"min": 0, "max": 1}},
"lacp": {
"type": {"key": {"type": "string",
information such as destination MAC address, IP address, and TCP
port.
</dd>
-
- <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
- <code>balance-tcp</code>, always taking into account L3 and L4
- fields even if LACP negotiations are unsuccessful. </p>
- <p>Slave selection decisions are made based on <ref table="Interface"
- column="other_config" key="bond-stable-id"/> if set. Otherwise,
- OpenFlow port number is used. Decisions are consistent across all
- <code>ovs-vswitchd</code> instances with equivalent
- <ref table="Interface" column="other_config" key="bond-stable-id"/>
- values.</p>
- </dd>
</dl>
<p>These columns apply only to bonded ports. Their values are
Same as IPSEC_GRE except 64 bit key.
</dd>
- <dt><code>capwap</code></dt>
- <dd>
- An Ethernet tunnel over the UDP transport portion of CAPWAP (RFC
- 5415). This allows interoperability with certain switches that do
- not support GRE. Only the tunneling component of the protocol is
- implemented. UDP ports 58881 and 58882 are used as the source and
- destination ports respectively. CAPWAP is currently supported only
- with the Linux kernel datapath with kernel version 2.6.26 or later.
-
- CAPWAP support is deprecated and will be removed no earlier than
- February 2013.
- </dd>
-
<dt><code>vxlan</code></dt>
<dd>
<p>
An Ethernet tunnel over the experimental, UDP-based VXLAN
protocol described at
- <code>http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02</code>.
+ <code>http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-03</code>.
VXLAN is currently supported only with the Linux kernel datapath
with kernel version 2.6.26 or later.
</p>
</p>
</dd>
+ <dt><code>lisp</code></dt>
+ <dd>
+ A layer 3 tunnel over the experimental, UDP-based Locator/ID
+ Separation Protocol (RFC 6830). LISP is currently supported only
+ with the Linux kernel datapath with kernel version 2.6.26 or later.
+ </dd>
+
<dt><code>patch</code></dt>
<dd>
A pair of virtual devices that act as a patch cable.
<p>
These options apply to interfaces with <ref column="type"/> of
<code>gre</code>, <code>ipsec_gre</code>, <code>gre64</code>,
- <code>ipsec_gre64</code>, <code>capwap</code>, and
- <code>vxlan</code>.
+ <code>ipsec_gre64</code>, <code>vxlan</code>, and <code>lisp</code>.
</p>
<p>
key="in_key"/> at all.
</li>
<li>
- A positive 24-bit (for VXLAN), 32-bit (for GRE) or 64-bit (for
- CAPWAP) number. The tunnel receives only packets with the
+ A positive 24-bit (for VXLAN and LISP), 32-bit (for GRE) or 64-bit
+ (for GRE64) number. The tunnel receives only packets with the
specified key.
</li>
<li>
key="out_key"/> at all.
</li>
<li>
- A positive 24-bit (for VXLAN), 32-bit (for GRE) or 64-bit (for
- CAPWAP) number. Packets sent through the tunnel will have the
+ A positive 24-bit (for VXLAN and LISP), 32-bit (for GRE) or 64-bit
+ (for GRE64) number. Packets sent through the tunnel will have the
specified key.
</li>
<li>
<column name="status" key="source_ip">
The source IP address used for an IPv4 tunnel end-point, such as
- <code>gre</code> or <code>capwap</code>.
+ <code>gre</code>.
</column>
<column name="status" key="tunnel_egress_iface">
- Egress interface for tunnels. Currently only relevant for GRE and
- CAPWAP tunnels. On Linux systems, this column will show the name of
- the interface which is responsible for routing traffic destined for the
- configured <ref column="options" key="remote_ip"/>. This could be an
- internal interface such as a bridge port.
+ Egress interface for tunnels. Currently only relevant for GRE tunnels
+ On Linux systems, this column will show the name of the interface
+ which is responsible for routing traffic destined for the configured
+ <ref column="options" key="remote_ip"/>. This could be an internal
+ interface such as a bridge port.
</column>
<column name="status" key="tunnel_egress_iface_carrier"
</group>
<group title="Bonding Configuration">
- <column name="other_config" key="bond-stable-id"
- type='{"type": "integer", "minInteger": 1}'>
- Used in <code>stable</code> bond mode to make slave
- selection decisions. Allocating <ref column="other_config"
- key="bond-stable-id"/> values consistently across interfaces
- participating in a bond will guarantee consistent slave selection
- decisions across <code>ovs-vswitchd</code> instances when using
- <code>stable</code> bonding mode.
- </column>
-
<column name="other_config" key="lacp-port-id"
type='{"type": "integer", "minInteger": 1, "maxInteger": 65535}'>
The LACP port ID of this <ref table="Interface"/>. Port IDs are
# without warranty of any kind.
/var/log/openvswitch/*.log {
+ daily
+ compress
sharedscripts
missingok
postrotate
install python/compat/argparse.py $RPM_BUILD_ROOT/usr/share/openvswitch/python
install -d -m 755 $RPM_BUILD_ROOT/etc/xensource/bugtool
-mv $RPM_BUILD_ROOT/usr/share/openvswitch/bugtool-plugins/* $RPM_BUILD_ROOT/etc/xensource/bugtool
+cp -rf $RPM_BUILD_ROOT/usr/share/openvswitch/bugtool-plugins/* $RPM_BUILD_ROOT/etc/xensource/bugtool
# Get rid of stuff we don't want to make RPM happy.
rm \
$RPM_BUILD_ROOT/usr/bin/ovs-benchmark \
- $RPM_BUILD_ROOT/usr/sbin/ovs-bugtool \
$RPM_BUILD_ROOT/usr/bin/ovs-controller \
$RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
$RPM_BUILD_ROOT/usr/bin/ovs-pki \
$RPM_BUILD_ROOT/usr/bin/ovs-test \
$RPM_BUILD_ROOT/usr/share/man/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-l3ping.8 \
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 \
/etc/logrotate.d/openvswitch
/etc/profile.d/openvswitch.sh
/usr/share/openvswitch/python/
+/usr/share/openvswitch/bugtool-plugins/*
/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/ovs-ctl
/usr/share/openvswitch/scripts/ovs-lib
/usr/share/openvswitch/vswitch.ovsschema
+/usr/sbin/ovs-bugtool
/usr/sbin/ovs-vlan-bug-workaround
/usr/sbin/ovs-vswitchd
/usr/sbin/ovsdb-server
/usr/share/man/man1/ovsdb-tool.1.gz
/usr/share/man/man5/ovs-vswitchd.conf.db.5.gz
/usr/share/man/man8/ovs-appctl.8.gz
+/usr/share/man/man8/ovs-bugtool.8.gz
/usr/share/man/man8/ovs-ctl.8.gz
/usr/share/man/man8/ovs-dpctl.8.gz
/usr/share/man/man8/ovs-ofctl.8.gz