From patchwork Thu Aug 10 15:25:20 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jan Scheurich X-Patchwork-Id: 800236 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3xSsQZ3P4Hz9s2G for ; Fri, 11 Aug 2017 01:25:26 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id C0B1C4A3; Thu, 10 Aug 2017 15:25:24 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 35897412 for ; Thu, 10 Aug 2017 15:25:24 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from sessmg22.ericsson.net (sessmg22.ericsson.net [193.180.251.58]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 41D5D350 for ; Thu, 10 Aug 2017 15:25:23 +0000 (UTC) X-AuditID: c1b4fb3a-803ff70000001b2f-55-598c7ae1c16c Received: from ESESSHC014.ericsson.se (Unknown_Domain [153.88.183.60]) by sessmg22.ericsson.net (Symantec Mail Security) with SMTP id EE.A5.06959.1EA7C895; Thu, 10 Aug 2017 17:25:21 +0200 (CEST) Received: from ESESSMB107.ericsson.se ([169.254.7.80]) by ESESSHC014.ericsson.se ([153.88.183.60]) with mapi id 14.03.0352.000; Thu, 10 Aug 2017 17:25:20 +0200 From: Jan Scheurich To: Ben Pfaff Thread-Topic: [PATCH v5 1/2] OF support and translation of generic encap and decap Thread-Index: AQHTC2ZtxTSjQWfGsUy8o0GvJoTSAaJ6mpRggAAoFYCAAvqu4A== Date: Thu, 10 Aug 2017 15:25:20 +0000 Message-ID: References: <1501661053-113507-1-git-send-email-yi.y.yang@intel.com> <1501661053-113507-2-git-send-email-yi.y.yang@intel.com> <20170808193531.GF6175@ovn.org> In-Reply-To: <20170808193531.GF6175@ovn.org> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [153.88.183.18] MIME-Version: 1.0 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmpnkeLIzCtJLcpLzFFi42KZGbHdRvdhVU+kwYEOJYtXkxsYLY6e3sNs 8fvrNiYHZo/Fe14yeTy7+Z/R4/m1HpYA5igum5TUnMyy1CJ9uwSujEPHV7EW/P/NXLHkywLW BsY/T5m6GDk4JARMJN5Os+5i5OIQEjjCKNH3/ggLhLOYUeL6w6NsIEVsAgYSs3c7dDFycogI SEu8e/yOBcRmFpjIKDGrKxOkRFggWOLiXFaIkhCJR9vXMkHYThI/VjxnA7FZBFQl7p6ZB2bz CvhKLJi+nBli1VNGidPr54IlOAV0JKZfWwBmMwqISXw/tYYJYpe4xK0n88FsCQEBiSV7zjND 2KISLx//Y4WwFSU+vtrHCFGvJ3Fj6hQ2CFtbYtnC18wQiwUlTs58wjKBUXQWkrGzkLTMQtIy C0nLAkaWVYyixanFxbnpRkZ6qUWZycXF+Xl6eaklmxiB0XNwy2+rHYwHnzseYhTgYFTi4X2f 1hMpxJpYVlyZe4hRgoNZSYS3oxIoxJuSWFmVWpQfX1Sak1p8iFGag0VJnNdh34UIIYH0xJLU 7NTUgtQimCwTB6dUA+NsKU3nmMm3I7fuzS29aMMXtGpVn9LWqxwugbuWsu8JXNC6JDIvzcDO 2t2LbxJb373lsie05S2uzmF9uqH5e4NgnKPnuVN1jsqM24/r3JYN2fFC9ZrQNvU3CznnZ7IX 29pMUvNlWrLw2wm3J/ffNhm5dL9ik968616ElkbJua9me0pC2xlOvFdiKc5INNRiLipOBADz KY/3mgIAAA== Cc: "dev@openvswitch.org" Subject: Re: [ovs-dev] [PATCH v5 1/2] OF support and translation of generic encap and decap X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org The generic code today in function parse_ENCAP() uses the encap header string also as string for the property class. I am afraid that implementer of subsequent encap headers might not realize that this is a temporary implementation shortcut that should have been generalized. The minimum we should add now is a comment explaining this, such as, for example: BR, Jan > -----Original Message----- > From: Ben Pfaff [mailto:blp@ovn.org] > Sent: Tuesday, 08 August, 2017 21:36 > To: Jan Scheurich > Cc: Yi Yang ; dev@openvswitch.org; Zoltán Balogh > Subject: Re: [PATCH v5 1/2] OF support and translation of generic encap and decap > > We can add the additional syntax at the same time we add something that > has the need for it. > > On Tue, Aug 08, 2017 at 03:52:32PM +0000, Jan Scheurich wrote: > > I know this comment is late as the patch has been merged already, but I just returned from vacation and only found today: > > > > I agree that the new simplified ovs-ofctl syntax for encap properties looks quite neat for the NSH use case at hand: > > > > encap(nsh(md_type=1)) > > encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210))) > > > > But unfortunately we have fallen into the trap of oversimplifying. The generic encap action (drafted in EXT-382) distinguishes between > the encapsulation packet type (in this case (1,0x894f) for NSH) and the encap property class (in this case 0x4 for NSH), which may be > different for every property in an encap action. > > > > Using the "nsh" keyword both as shorthand for the packet type and the property class selector works here because the NSH encap action > only supports NSH properties so far, and in many simple applications of the generic encap action there may well be 1-1 relation between > the encap packet type and the class of all supported properties. But the ONF draft specifically decouples the packet type from property > classes for a richer set of use cases. > > > > For example there could be a more powerful generic encap operation to add multiple protocol layers in one action. In this case the > encap properties would likely belong to several property classes. Or an encapsulation header re-uses a general encap property already > defined elsewhere, which should not be duplicated. > > > > In order not to restrict the generality of the ovs-ofctl syntax, I'd suggest to separate packet type and the property class as follows: > > > > encap() > > encap((,(=,()),...) > > > > For NSH the syntax would look: > > > > encap(nsh,nsh(md_type=1)) > > encap(nsh,nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210))) > > > > To be user friendly we can also allow the above more concise syntax in the special case of a 1-1 relation between packet type and > property class. In that case we could re-add the generalization of the syntax after the OVS 2.8 release without braking backward > compatibility. > > > > BR, Jan > > > > > -----Original Message----- > > > From: Yi Yang [mailto:yi.y.yang@intel.com] > > > Sent: Wednesday, 02 August, 2017 10:04 > > > To: dev@openvswitch.org > > > Cc: blp@ovn.org; Jan Scheurich ; Yi Yang ; Zoltán Balogh > > > > > > Subject: [PATCH v5 1/2] OF support and translation of generic encap and decap > > > > > > From: Jan Scheurich > > > > > > This commit adds support for the OpenFlow actions generic encap > > > and decap (as specified in ONF EXT-382) to the OVS control plane. > > > > > > CLI syntax for encap action with properties: > > > encap(
) > > > encap(
(=,(,,),...)) > > > > > > For example: > > > encap(ethernet) > > > encap(nsh(md_type=1)) > > > encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210))) > > > > > > CLI syntax for decap action: > > > decap() > > > decap(packet_type(ns=,type=)) > > > > > > For example: > > > decap() > > > decap(packet_type(ns=0,type=0xfffe)) > > > decap(packet_type(ns=1,type=0x894f)) > > > > > > The first header supported for encap and decap is "ethernet" to convert > > > packets between packet_type (1,Ethertype) and (0,0). > > > > > > This commit also implements a skeleton for the translation of generic > > > encap and decap actions in ofproto-dpif and adds support to encap and > > > decap an Ethernet header. > > > > > > In general translation of encap commits pending actions and then rewrites > > > struct flow in accordance with the new packet type and header. In the > > > case of encap(ethernet) it suffices to change the packet type from > > > (1, Ethertype) to (0,0) and set the dl_type accordingly. A new > > > pending_encap flag in xlate ctx is set to mark that an corresponding > > > datapath encap action must be triggered at the next commit. In the > > > case of encap(ethernet) ofproto generetas a push_eth action. > > > > > > The general case for translation of decap() is to emit a datapath action > > > to decap the current outermost header and then recirculate the packet > > > to reparse the inner headers. In the special case of an Ethernet packet, > > > decap() just changes the packet type from (0,0) to (1, dl_type) without > > > a need to recirculate. The emission of the pop_eth action for the > > > datapath is postponed to the next commit. > > > > > > Hence encap(ethernet) and decap() on an Ethernet packet are OF octions > > > that only incur a cost in the dataplane when a modifed packet is > > > actually committed, e.g. because it is sent out. They can freely be > > > used for normalizing the packet type in the OF pipeline without > > > degrading performance. > > > > > > Signed-off-by: Jan Scheurich > > > Signed-off-by: Yi Yang > > > Signed-off-by: Zoltan Balogh > > > Co-authored-by: Zoltan Balogh > > > Signed-off-by: Ben Pfaff > > > --- > > > NEWS | 6 + > > > include/openflow/openflow-common.h | 1 + > > > include/openvswitch/automake.mk | 1 + > > > include/openvswitch/ofp-actions.h | 32 ++++ > > > include/openvswitch/ofp-ed-props.h | 77 ++++++++ > > > include/openvswitch/ofp-errors.h | 9 + > > > lib/automake.mk | 1 + > > > lib/odp-util.c | 84 ++++++--- > > > lib/odp-util.h | 3 +- > > > lib/ofp-actions.c | 364 ++++++++++++++++++++++++++++++++++++- > > > lib/ofp-ed-props.c | 157 ++++++++++++++++ > > > lib/packets.h | 3 +- > > > ofproto/ofproto-dpif-xlate.c | 116 +++++++++++- > > > utilities/ovs-ofctl.8.in | 76 +++++++- > > > 14 files changed, 883 insertions(+), 47 deletions(-) > > > create mode 100644 include/openvswitch/ofp-ed-props.h > > > create mode 100644 lib/ofp-ed-props.c > > > > > > diff --git a/NEWS b/NEWS > > > index facea02..ca02ca7 100644 > > > --- a/NEWS > > > +++ b/NEWS > > > @@ -62,11 +62,17 @@ Post-v2.7.0 > > > * The "learn" action now supports a "limit" option (see ovs-ofctl(8)). > > > * The port status bit OFPPS_LIVE now reflects link aliveness. > > > * OpenFlow 1.5 packet-out is now supported. > > > + * Support for OpenFlow 1.5 field packet_type and packet-type-aware > > > + pipeline (PTAP). > > > + * Added generic encap and decap actions (EXT-382). > > > + First supported use case is encap/decap for Ethernet. > > > - Fedora Packaging: > > > * OVN services are no longer restarted automatically after upgrade. > > > - Add --cleanup option to command 'ovs-appctl exit' (see ovs-vswitchd(8)). > > > - L3 tunneling: > > > * Use new tunnel port option "packet_type" to configure L2 vs. L3. > > > + * In conjunction with PTAP tunnel ports can handle a mix of L2 and L3 > > > + payload. > > > * New vxlan tunnel extension "gpe" to support VXLAN-GPE tunnels. > > > * New support for non-Ethernet (L3) payloads in GRE and VXLAN-GPE. > > > - The BFD detection multiplier is now user-configurable. > > > diff --git a/include/openflow/openflow-common.h b/include/openflow/openflow-common.h > > > index 5f1e225..d665519 100644 > > > --- a/include/openflow/openflow-common.h > > > +++ b/include/openflow/openflow-common.h > > > @@ -466,6 +466,7 @@ enum ofp_header_type_namespaces { > > > OFPHTN_IP_PROTO = 2, /* ns_type is a IP protocol number. */ > > > OFPHTN_UDP_TCP_PORT = 3, /* ns_type is a TCP or UDP port. */ > > > OFPHTN_IPV4_OPTION = 4, /* ns_type is an IPv4 option number. */ > > > + OFPHTN_N_TYPES > > > }; > > > > > > #endif /* openflow/openflow-common.h */ > > > diff --git a/include/openvswitch/automake.mk b/include/openvswitch/automake.mk > > > index 6bace61..0a139d0 100644 > > > --- a/include/openvswitch/automake.mk > > > +++ b/include/openvswitch/automake.mk > > > @@ -12,6 +12,7 @@ openvswitchinclude_HEADERS = \ > > > include/openvswitch/meta-flow.h \ > > > include/openvswitch/ofpbuf.h \ > > > include/openvswitch/ofp-actions.h \ > > > + include/openvswitch/ofp-ed-props.h \ > > > include/openvswitch/ofp-errors.h \ > > > include/openvswitch/ofp-msgs.h \ > > > include/openvswitch/ofp-parse.h \ > > > diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h > > > index 198107e..4357f4e 100644 > > > --- a/include/openvswitch/ofp-actions.h > > > +++ b/include/openvswitch/ofp-actions.h > > > @@ -25,6 +25,7 @@ > > > #include "openvswitch/ofp-util.h" > > > #include "openvswitch/ofp-errors.h" > > > #include "openvswitch/types.h" > > > +#include "openvswitch/ofp-ed-props.h" > > > > > > #ifdef __cplusplus > > > extern "C" { > > > @@ -93,6 +94,10 @@ struct vl_mff_map; > > > OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact, "push_mpls") \ > > > OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact, "pop_mpls") \ > > > \ > > > + /* Generic encap & decap */ \ > > > + OFPACT(ENCAP, ofpact_encap, props, "encap") \ > > > + OFPACT(DECAP, ofpact_decap, ofpact, "decap") \ > > > + \ > > > /* Metadata. */ \ > > > OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact, "set_tunnel") \ > > > OFPACT(SET_QUEUE, ofpact_queue, ofpact, "set_queue") \ > > > @@ -974,6 +979,33 @@ struct ofpact_unroll_xlate { > > > ovs_be64 rule_cookie; /* OVS_BE64_MAX if none. */ > > > }; > > > > > > +/* OFPACT_ENCAP. > > > + * > > > + * Used for NXAST_ENCAP. */ > > > + > > > +struct ofpact_encap { > > > + struct ofpact ofpact; > > > + ovs_be32 new_pkt_type; /* Packet type of the header to add. */ > > > + uint16_t hdr_size; /* New header size in bytes. */ > > > + uint16_t n_props; /* Number of encap properties. */ > > > + struct ofpact_ed_prop props[]; /* Properties in internal format. */ > > > +}; > > > + > > > +/* OFPACT_DECAP. > > > + * > > > + * Used for NXAST_DECAP. */ > > > +struct ofpact_decap { > > > + struct ofpact ofpact; > > > + > > > + /* New packet type. > > > + * > > > + * The special value (0,0xFFFE) "Use next proto" is used to request OVS to > > > + * automatically set the new packet type based on the decap'ed header's > > > + * next protocol. > > > + */ > > > + ovs_be32 new_pkt_type; > > > +}; > > > + > > > /* Converting OpenFlow to ofpacts. */ > > > enum ofperr ofpacts_pull_openflow_actions(struct ofpbuf *openflow, > > > unsigned int actions_len, > > > diff --git a/include/openvswitch/ofp-ed-props.h b/include/openvswitch/ofp-ed-props.h > > > new file mode 100644 > > > index 0000000..cf2fa62 > > > --- /dev/null > > > +++ b/include/openvswitch/ofp-ed-props.h > > > @@ -0,0 +1,77 @@ > > > +/* > > > + * Copyright (c) 2017 Intel, 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 OPENVSWITCH_OFP_ED_PROPS_H > > > +#define OPENVSWITCH_OFP_ED_PROPS_H 1 > > > + > > > +#include "openvswitch/ofp-errors.h" > > > +#include "openvswitch/types.h" > > > +#include "openvswitch/ofpbuf.h" > > > + > > > +#ifdef __cplusplus > > > +extern "C" { > > > +#endif > > > + > > > +enum ofp_ed_prop_class { > > > + OFPPPC_BASIC = 0, /* ONF Basic class. */ > > > + OFPPPC_MPLS = 1, /* MPLS property class. */ > > > + OFPPPC_GRE = 2, /* GRE property class. */ > > > + OFPPPC_GTP = 3, /* GTP property class. */ > > > + > > > + /* Experimenter property class. > > > + * > > > + * First 32 bits of property data > > > + * is exp id after that is the experimenter property data. > > > + */ > > > + OFPPPC_EXPERIMENTER=0xffff > > > +}; > > > + > > > +/* > > > + * External representation of encap/decap properties. > > > + * These must be padded to a multiple of 8 bytes. > > > + */ > > > +struct ofp_ed_prop_header { > > > + ovs_be16 prop_class; > > > + uint8_t type; > > > + uint8_t len; > > > +}; > > > + > > > +/* > > > + * Internal representation of encap/decap properties > > > + */ > > > +struct ofpact_ed_prop { > > > + uint16_t prop_class; > > > + uint8_t type; > > > + uint8_t len; > > > +}; > > > + > > > +enum ofperr decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop, > > > + struct ofpbuf *out, size_t *remaining); > > > +enum ofperr encode_ed_prop(const struct ofpact_ed_prop **prop, > > > + struct ofpbuf *out); > > > +bool parse_ed_prop_class(const char *str, uint16_t *prop_class); > > > +bool parse_ed_prop_type(uint16_t prop_class, const char *str, uint8_t *type); > > > +char *parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type, > > > + const char *str, struct ofpbuf *out); > > > +char *format_ed_prop_class(const struct ofpact_ed_prop *prop); > > > +char *format_ed_prop_type(const struct ofpact_ed_prop *prop); > > > +void format_ed_prop(struct ds *s, const struct ofpact_ed_prop *prop); > > > + > > > +#ifdef __cplusplus > > > +} > > > +#endif > > > + > > > +#endif /* ofp-ed-props.h */ > > > diff --git a/include/openvswitch/ofp-errors.h b/include/openvswitch/ofp-errors.h > > > index b551ce6..3d09be3 100644 > > > --- a/include/openvswitch/ofp-errors.h > > > +++ b/include/openvswitch/ofp-errors.h > > > @@ -276,6 +276,15 @@ enum ofperr { > > > * 64. */ > > > OFPERR_NXBAC_BAD_CONJUNCTION, > > > > > > + /* NX1.3+(39). Unsupported packet type in encap or decap. */ > > > + OFPERR_NXBAC_BAD_HEADER_TYPE, > > > + > > > + /* NX1.3+(40). Unrecognized encap or decap property. */ > > > + OFPERR_NXBAC_UNKNOWN_ED_PROP, > > > + > > > + /* NX1.3+(41). Error in encap or decap property. */ > > > + OFPERR_NXBAC_BAD_ED_PROP, > > > + > > > /* ## --------------------- ## */ > > > /* ## OFPET_BAD_INSTRUCTION ## */ > > > /* ## --------------------- ## */ > > > diff --git a/lib/automake.mk b/lib/automake.mk > > > index 54a1032..bd56f43 100644 > > > --- a/lib/automake.mk > > > +++ b/lib/automake.mk > > > @@ -149,6 +149,7 @@ lib_libopenvswitch_la_SOURCES = \ > > > lib/odp-util.c \ > > > lib/odp-util.h \ > > > lib/ofp-actions.c \ > > > + lib/ofp-ed-props.c \ > > > lib/ofp-errors.c \ > > > lib/ofp-msgs.c \ > > > lib/ofp-parse.c \ > > > diff --git a/lib/odp-util.c b/lib/odp-util.c > > > index 9d95d53..6390734 100644 > > > --- a/lib/odp-util.c > > > +++ b/lib/odp-util.c > > > @@ -5922,13 +5922,17 @@ put_ethernet_key(const struct ovs_key_ethernet *eth, struct flow *flow) > > > } > > > > > > static void > > > -commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow, > > > - struct ofpbuf *odp_actions, > > > - struct flow_wildcards *wc, > > > - bool use_masked) > > > +commit_set_ether_action(const struct flow *flow, struct flow *base_flow, > > > + struct ofpbuf *odp_actions, > > > + struct flow_wildcards *wc, > > > + bool use_masked) > > > { > > > struct ovs_key_ethernet key, base, mask; > > > > > > + if (flow->packet_type != htonl(PT_ETH)) { > > > + return; > > > + } > > > + > > > get_ethernet_key(flow, &key); > > > get_ethernet_key(base_flow, &base); > > > get_ethernet_key(&wc->masks, &mask); > > > @@ -5941,29 +5945,6 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow, > > > } > > > > > > static void > > > -commit_ether_action(const struct flow *flow, struct flow *base_flow, > > > - struct ofpbuf *odp_actions, struct flow_wildcards *wc, > > > - bool use_masked) > > > -{ > > > - if (flow->packet_type == htonl(PT_ETH)) { > > > - if (base_flow->packet_type != htonl(PT_ETH)) { > > > - odp_put_push_eth_action(odp_actions, &flow->dl_src, &flow->dl_dst); > > > - base_flow->packet_type = flow->packet_type; > > > - base_flow->dl_src = flow->dl_src; > > > - base_flow->dl_dst = flow->dl_dst; > > > - } else { > > > - commit_set_ether_addr_action(flow, base_flow, odp_actions, wc, > > > - use_masked); > > > - } > > > - } else { > > > - if (base_flow->packet_type == htonl(PT_ETH)) { > > > - odp_put_pop_eth_action(odp_actions); > > > - base_flow->packet_type = flow->packet_type; > > > - } > > > - } > > > -} > > > - > > > -static void > > > commit_vlan_action(const struct flow* flow, struct flow *base, > > > struct ofpbuf *odp_actions, struct flow_wildcards *wc) > > > { > > > @@ -6400,6 +6381,50 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow, > > > } > > > } > > > > > > +static void > > > +commit_packet_type_change(const struct flow *flow, > > > + struct flow *base_flow, > > > + struct ofpbuf *odp_actions, > > > + struct flow_wildcards *wc, > > > + bool pending_encap) > > > +{ > > > + if (flow->packet_type == base_flow->packet_type) { > > > + return; > > > + } > > > + > > > + if (pending_encap) { > > > + switch (ntohl(flow->packet_type)) { > > > + case PT_ETH: { > > > + /* push_eth */ > > > + odp_put_push_eth_action(odp_actions, &flow->dl_src, > > > + &flow->dl_dst); > > > + base_flow->packet_type = flow->packet_type; > > > + base_flow->dl_src = flow->dl_src; > > > + base_flow->dl_dst = flow->dl_dst; > > > + break; > > > + } > > > + default: > > > + /* Only the above protocols are supported for encap. The check > > > + * is done at action decoding. */ > > > + OVS_NOT_REACHED(); > > > + } > > > + } else { > > > + if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE && > > > + base_flow->packet_type == htonl(PT_ETH)) { > > > + /* pop_eth */ > > > + odp_put_pop_eth_action(odp_actions); > > > + base_flow->packet_type = flow->packet_type; > > > + base_flow->dl_src = eth_addr_zero; > > > + base_flow->dl_dst = eth_addr_zero; > > > + } else { > > > + /* All other cases are handled through recirculation. */ > > > + OVS_NOT_REACHED(); > > > + } > > > + } > > > + > > > + wc->masks.packet_type = OVS_BE32_MAX; > > > +} > > > + > > > /* 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 > > > * key from 'base' into 'flow', and then changes 'base' the same way. Does not > > > @@ -6412,12 +6437,13 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow, > > > enum slow_path_reason > > > commit_odp_actions(const struct flow *flow, struct flow *base, > > > struct ofpbuf *odp_actions, struct flow_wildcards *wc, > > > - bool use_masked) > > > + bool use_masked, bool pending_encap) > > > { > > > enum slow_path_reason slow1, slow2; > > > bool mpls_done = false; > > > > > > - commit_ether_action(flow, base, odp_actions, wc, use_masked); > > > + commit_packet_type_change(flow, base, odp_actions, wc, pending_encap); > > > + commit_set_ether_action(flow, base, odp_actions, wc, use_masked); > > > /* Make packet a non-MPLS packet before committing L3/4 actions, > > > * which would otherwise do nothing. */ > > > if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) { > > > diff --git a/lib/odp-util.h b/lib/odp-util.h > > > index 224f78e..bdb2354 100644 > > > --- a/lib/odp-util.h > > > +++ b/lib/odp-util.h > > > @@ -276,7 +276,8 @@ enum slow_path_reason commit_odp_actions(const struct flow *, > > > struct flow *base, > > > struct ofpbuf *odp_actions, > > > struct flow_wildcards *wc, > > > - bool use_masked); > > > + bool use_masked, > > > + bool pending_encap); > > > > > > > > /* ofproto-dpif interface. > > > * > > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c > > > index ae27d9d..16da78c 100644 > > > --- a/lib/ofp-actions.c > > > +++ b/lib/ofp-actions.c > > > @@ -342,6 +342,12 @@ enum ofp_raw_action_type { > > > /* NX1.0+(43): void. */ > > > NXAST_RAW_CT_CLEAR, > > > > > > + /* NX1.3+(46): struct nx_action_encap, ... */ > > > + NXAST_RAW_ENCAP, > > > + > > > + /* NX1.3+(47): struct nx_action_decap, ... */ > > > + NXAST_RAW_DECAP, > > > + > > > /* ## ------------------ ## */ > > > /* ## Debugging actions. ## */ > > > /* ## ------------------ ## */ > > > @@ -472,6 +478,8 @@ ofpact_next_flattened(const struct ofpact *ofpact) > > > case OFPACT_WRITE_METADATA: > > > case OFPACT_GOTO_TABLE: > > > case OFPACT_NAT: > > > + case OFPACT_ENCAP: > > > + case OFPACT_DECAP: > > > return ofpact_next(ofpact); > > > > > > case OFPACT_CLONE: > > > @@ -4019,6 +4027,304 @@ format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a, > > > ds_chomp(s, ','); > > > ds_put_format(s, "%s)%s", colors.paren, colors.end); > > > } > > > + > > > +/* Action structure for NXAST_ENCAP */ > > > +struct nx_action_encap { > > > + ovs_be16 type; /* OFPAT_VENDOR. */ > > > + ovs_be16 len; /* Total size including any property TLVs. */ > > > + ovs_be32 vendor; /* NX_VENDOR_ID. */ > > > + ovs_be16 subtype; /* NXAST_ENCAP. */ > > > + ovs_be16 hdr_size; /* Header size in bytes, 0 = 'not specified'.*/ > > > + ovs_be32 new_pkt_type; /* Header type to add and PACKET_TYPE of result. */ > > > + struct ofp_ed_prop_header props[]; /* Encap TLV properties. */ > > > +}; > > > +OFP_ASSERT(sizeof(struct nx_action_encap) == 16); > > > + > > > +static enum ofperr > > > +decode_NXAST_RAW_ENCAP(const struct nx_action_encap *nae, > > > + enum ofp_version ofp_version OVS_UNUSED, > > > + struct ofpbuf *out) > > > +{ > > > + struct ofpact_encap *encap; > > > + const struct ofp_ed_prop_header *ofp_prop; > > > + size_t props_len; > > > + uint16_t n_props = 0; > > > + int err; > > > + > > > + encap = ofpact_put_ENCAP(out); > > > + encap->ofpact.raw = NXAST_RAW_ENCAP; > > > + switch (ntohl(nae->new_pkt_type)) { > > > + case PT_ETH: > > > + /* Add supported encap header types here. */ > > > + break; > > > + default: > > > + return OFPERR_NXBAC_BAD_HEADER_TYPE; > > > + } > > > + encap->new_pkt_type = nae->new_pkt_type; > > > + encap->hdr_size = ntohs(nae->hdr_size); > > > + > > > + ofp_prop = nae->props; > > > + props_len = ntohs(nae->len) - offsetof(struct nx_action_encap, props); > > > + n_props = 0; > > > + while (props_len > 0) { > > > + err = decode_ed_prop(&ofp_prop, out, &props_len); > > > + if (err) { > > > + return err; > > > + } > > > + n_props++; > > > + } > > > + encap->n_props = n_props; > > > + out->header = &encap->ofpact; > > > + ofpact_finish_ENCAP(out, &encap); > > > + > > > + return 0; > > > +} > > > + > > > +static void > > > +encode_ENCAP(const struct ofpact_encap *encap, > > > + enum ofp_version ofp_version OVS_UNUSED, > > > + struct ofpbuf *out) > > > +{ > > > + size_t start_ofs = out->size; > > > + struct nx_action_encap *nae = put_NXAST_ENCAP(out); > > > + int i; > > > + > > > + nae->new_pkt_type = encap->new_pkt_type; > > > + nae->hdr_size = htons(encap->hdr_size); > > > + const struct ofpact_ed_prop *prop = encap->props; > > > + for (i = 0; i < encap->n_props; i++) { > > > + encode_ed_prop(&prop, out); > > > + } > > > + pad_ofpat(out, start_ofs); > > > +} > > > + > > > +static bool > > > +parse_encap_header(const char *hdr, ovs_be32 *packet_type) > > > +{ > > > + if (strcmp(hdr, "ethernet") == 0) { > > > + *packet_type = htonl(PT_ETH); > > > + } else { > > > + return false; > > > + } > > > + return true; > > > +} > > > + > > > +static char * > > > +parse_ed_props(const uint16_t prop_class, char **arg, int *n_props, struct ofpbuf *out) > > > +{ > > > + char *key, *value, *err; > > > + uint8_t prop_type; > > > + > > > + while (ofputil_parse_key_value(arg, &key, &value)) { > > > + if (!parse_ed_prop_type(prop_class, key, &prop_type)) { > > > + return xasprintf("Invalid property: %s", key); > > > + } > > > + if (value == NULL) { > > > + return xasprintf("Missing the value for property: %s", key); > > > + } > > > + err = parse_ed_prop_value(prop_class, prop_type, value, out); > > > + if (err != NULL) { > > > + return err; > > > + } > > > + (*n_props)++; > > > + } > > > + return NULL; > > > +} > > > + > > > +/* The string representation of the encap action is > > > + * encap(header_type(prop=,tlv(,,),...)) > > > + */ > > > + > > > +static char * OVS_WARN_UNUSED_RESULT > > > +parse_ENCAP(char *arg, > > > + const struct ofputil_port_map *port_map OVS_UNUSED, > > > + struct ofpbuf *out, > > > + enum ofputil_protocol *usable_protocols OVS_UNUSED) > > > +{ > > > + struct ofpact_encap *encap; > > > + char *key, *value, *str; > > > + char *error = NULL; > > > + uint16_t prop_class; > > > + int n_props = 0; > > > + > > > + encap = ofpact_put_ENCAP(out); > > > + encap->hdr_size = 0; > > > + /* Parse encap header type. */ > > > + str = arg; > > > + if (!ofputil_parse_key_value(&arg, &key, &value)) { > > > + return xasprintf("Missing encap hdr: %s", str); > > > + } > > > + if (!parse_encap_header(key, &encap->new_pkt_type)) { > > > + return xasprintf("Encap hdr not supported: %s", value); > > > + } > > > + if (!parse_ed_prop_class(key, &prop_class)) { > > > + return xasprintf("Invalid encap prop class: %s", key); > > > + } > > > + /* Parse encap properties. */ > > > + error = parse_ed_props(prop_class, &value, &n_props, out); > > > + if (error != NULL) { > > > + return error; > > > + } > > > + /* ofbuf out may have been re-allocated. */ > > > + encap = out->header; > > > + encap->n_props = n_props; > > > + ofpact_finish_ENCAP(out, &encap); > > > + return NULL; > > > +} > > > + > > > +static char * > > > +format_encap_pkt_type(const ovs_be32 pkt_type) > > > +{ > > > + switch (ntohl(pkt_type)) { > > > + case PT_ETH: > > > + return "ethernet"; > > > + default: > > > + return "UNKNOWN"; > > > + } > > > +} > > > + > > > +static void > > > +format_ed_props(struct ds *s, uint16_t n_props, > > > + const struct ofpact_ed_prop *prop) > > > +{ > > > + const uint8_t *p = (uint8_t *) prop; > > > + int i; > > > + > > > + if (n_props == 0) { > > > + return; > > > + } > > > + for (i = 0; i < n_props; i++) { > > > + format_ed_prop(s, prop); > > > + ds_put_char(s, ','); > > > + p += ROUND_UP(prop->len, 8); > > > + prop = ALIGNED_CAST(const struct ofpact_ed_prop *, p); > > > + } > > > + if (n_props > 0) { > > > + ds_chomp(s, ','); > > > + } > > > +} > > > + > > > +static void > > > +format_ENCAP(const struct ofpact_encap *a, > > > + const struct ofputil_port_map *port_map OVS_UNUSED, > > > + struct ds *s) > > > +{ > > > + ds_put_format(s, "%sencap(%s", colors.paren, colors.end); > > > + ds_put_format(s, "%s", format_encap_pkt_type(a->new_pkt_type)); > > > + if (a->n_props > 0) { > > > + ds_put_format(s, "%s(%s", colors.paren, colors.end); > > > + format_ed_props(s, a->n_props, a->props); > > > + ds_put_format(s, "%s)%s", colors.paren, colors.end); > > > + } > > > + ds_put_format(s, "%s)%s", colors.paren, colors.end); > > > +} > > > + > > > +/* Action structure for NXAST_DECAP */ > > > +struct nx_action_decap { > > > + ovs_be16 type; /* OFPAT_VENDOR. */ > > > + ovs_be16 len; /* Total size including any property TLVs. */ > > > + ovs_be32 vendor; /* NX_VENDOR_ID. */ > > > + ovs_be16 subtype; /* NXAST_DECAP. */ > > > + uint8_t pad[2]; /* 2 bytes padding */ > > > + > > > + /* Packet type or result. > > > + * > > > + * The special value (0,0xFFFE) "Use next proto" > > > + * is used to request OVS to automatically set the new packet type based > > > + * on the decap'ed header's next protocol. > > > + */ > > > + ovs_be32 new_pkt_type; > > > +}; > > > +OFP_ASSERT(sizeof(struct nx_action_decap) == 16); > > > + > > > +static enum ofperr > > > +decode_NXAST_RAW_DECAP(const struct nx_action_decap *nad, > > > + enum ofp_version ofp_version OVS_UNUSED, > > > + struct ofpbuf *ofpacts) > > > +{ > > > + struct ofpact_decap * decap; > > > + > > > + if (ntohs(nad->len) > sizeof *nad) { > > > + /* No properties supported yet. */ > > > + return OFPERR_NXBAC_UNKNOWN_ED_PROP; > > > + } > > > + > > > + decap = ofpact_put_DECAP(ofpacts); > > > + decap->ofpact.raw = NXAST_RAW_DECAP; > > > + decap->new_pkt_type = nad->new_pkt_type; > > > + return 0; > > > +} > > > + > > > +static void > > > +encode_DECAP(const struct ofpact_decap *decap, > > > + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) > > > +{ > > > + struct nx_action_decap *nad = put_NXAST_DECAP(out); > > > + > > > + nad->len = htons(sizeof(struct nx_action_decap)); > > > + nad->new_pkt_type = decap->new_pkt_type; > > > +} > > > + > > > +static char * OVS_WARN_UNUSED_RESULT > > > +parse_DECAP(char *arg, > > > + const struct ofputil_port_map *port_map OVS_UNUSED, > > > + struct ofpbuf *ofpacts, > > > + enum ofputil_protocol *usable_protocols OVS_UNUSED) > > > +{ > > > + struct ofpact_decap *decap; > > > + char *key, *value, *pos; > > > + char *error = NULL; > > > + uint16_t ns, type; > > > + > > > + decap = ofpact_put_DECAP(ofpacts); > > > + /* Default next packet_type is PT_USE_NEXT_PROTO. */ > > > + decap->new_pkt_type = htonl(PT_USE_NEXT_PROTO); > > > + > > > + /* Parse decap packet_type if given. */ > > > + if (ofputil_parse_key_value(&arg, &key, &value)) { > > > + if (strcmp(key, "packet_type") == 0) { > > > + pos = value; > > > + if (!ofputil_parse_key_value(&pos, &key, &value) > > > + || strcmp(key, "ns") != 0) { > > > + return xstrdup("Missing packet_type attribute ns"); > > > + } > > > + error = str_to_u16(value, "ns", &ns); > > > + if (error) { > > > + return error; > > > + } > > > + if (ns >= OFPHTN_N_TYPES) { > > > + return xasprintf("Unsupported ns value: %"PRIu16, ns); > > > + } > > > + if (!ofputil_parse_key_value(&pos, &key, &value) > > > + || strcmp(key, "type") != 0) { > > > + return xstrdup("Missing packet_type attribute type"); > > > + } > > > + error = str_to_u16(value, "type", &type); > > > + if (error) { > > > + return error; > > > + } > > > + decap->new_pkt_type = htonl(PACKET_TYPE(ns, type)); > > > + } else { > > > + return xasprintf("Invalid decap argument: %s", key); > > > + } > > > + } > > > + return NULL; > > > +} > > > + > > > +static void > > > +format_DECAP(const struct ofpact_decap *a, > > > + const struct ofputil_port_map *port_map OVS_UNUSED, > > > + struct ds *s) > > > +{ > > > + ds_put_format(s, "%sdecap(%s", colors.paren, colors.end); > > > + if (a->new_pkt_type != htonl(PT_USE_NEXT_PROTO)) { > > > + ds_put_format(s, "packet_type(ns=%"PRIu16",id=%#"PRIx16")", > > > + pt_ns(a->new_pkt_type), > > > + pt_ns_type(a->new_pkt_type)); > > > + } > > > + ds_put_format(s, "%s)%s", colors.paren, colors.end); > > > +} > > > + > > > > > > > > /* Action structures for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE, and > > > * NXAST_RESUBMIT_TABLE_CT. > > > @@ -6802,6 +7108,8 @@ ofpact_is_set_or_move_action(const struct ofpact *a) > > > case OFPACT_SET_TUNNEL: > > > case OFPACT_SET_VLAN_PCP: > > > case OFPACT_SET_VLAN_VID: > > > + case OFPACT_ENCAP: > > > + case OFPACT_DECAP: > > > return true; > > > case OFPACT_BUNDLE: > > > case OFPACT_CLEAR_ACTIONS: > > > @@ -6877,6 +7185,8 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a) > > > case OFPACT_SET_VLAN_PCP: > > > case OFPACT_SET_VLAN_VID: > > > case OFPACT_STRIP_VLAN: > > > + case OFPACT_ENCAP: > > > + case OFPACT_DECAP: > > > return true; > > > > > > /* In general these actions are excluded because they are not part of > > > @@ -6984,6 +7294,8 @@ ofpacts_execute_action_set(struct ofpbuf *action_list, > > > /* The OpenFlow spec "Action Set" section specifies this order. */ > > > ofpacts_copy_last(action_list, action_set, OFPACT_STRIP_VLAN); > > > ofpacts_copy_last(action_list, action_set, OFPACT_POP_MPLS); > > > + ofpacts_copy_last(action_list, action_set, OFPACT_DECAP); > > > + ofpacts_copy_last(action_list, action_set, OFPACT_ENCAP); > > > ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_MPLS); > > > ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_VLAN); > > > ofpacts_copy_last(action_list, action_set, OFPACT_DEC_TTL); > > > @@ -7127,6 +7439,8 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type) > > > case OFPACT_CT: > > > case OFPACT_CT_CLEAR: > > > case OFPACT_NAT: > > > + case OFPACT_ENCAP: > > > + case OFPACT_DECAP: > > > default: > > > return OVSINST_OFPIT11_APPLY_ACTIONS; > > > } > > > @@ -7488,8 +7802,8 @@ inconsistent_match(enum ofputil_protocol *usable_protocols) > > > *usable_protocols &= OFPUTIL_P_OF10_ANY; > > > } > > > > > > -/* May modify flow->dl_type, flow->nw_proto and flow->vlan_tci, > > > - * caller must restore them. > > > +/* May modify flow->packet_type, flow->dl_type, flow->nw_proto and > > > + * flow->vlan_tci, caller must restore them. > > > * > > > * Modifies some actions, filling in fields that could not be properly set > > > * without context. */ > > > @@ -7501,6 +7815,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, > > > struct flow *flow = &match->flow; > > > const struct ofpact_enqueue *enqueue; > > > const struct mf_field *mf; > > > + ovs_be16 dl_type = get_dl_type(flow); > > > > > > switch (a->type) { > > > case OFPACT_OUTPUT: > > > @@ -7578,7 +7893,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, > > > > > > case OFPACT_SET_IPV4_SRC: > > > case OFPACT_SET_IPV4_DST: > > > - if (flow->dl_type != htons(ETH_TYPE_IP)) { > > > + if (dl_type != htons(ETH_TYPE_IP)) { > > > inconsistent_match(usable_protocols); > > > } > > > return 0; > > > @@ -7643,7 +7958,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, > > > case OFPACT_SET_MPLS_TC: > > > case OFPACT_SET_MPLS_TTL: > > > case OFPACT_DEC_MPLS_TTL: > > > - if (!eth_type_mpls(flow->dl_type)) { > > > + if (!eth_type_mpls(dl_type)) { > > > inconsistent_match(usable_protocols); > > > } > > > return 0; > > > @@ -7681,6 +7996,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, > > > return 0; > > > > > > case OFPACT_PUSH_MPLS: > > > + if (flow->packet_type != htonl(PT_ETH)) { > > > + inconsistent_match(usable_protocols); > > > + } > > > flow->dl_type = ofpact_get_PUSH_MPLS(a)->ethertype; > > > /* The packet is now MPLS and the MPLS payload is opaque. > > > * Thus nothing can be assumed about the network protocol. > > > @@ -7689,7 +8007,8 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, > > > return 0; > > > > > > case OFPACT_POP_MPLS: > > > - if (!eth_type_mpls(flow->dl_type)) { > > > + if (flow->packet_type != htonl(PT_ETH) > > > + || !eth_type_mpls(dl_type)) { > > > inconsistent_match(usable_protocols); > > > } > > > flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype; > > > @@ -7708,7 +8027,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, > > > case OFPACT_CT: { > > > struct ofpact_conntrack *oc = ofpact_get_CT(a); > > > > > > - if (!dl_type_is_ip_any(flow->dl_type) > > > + if (!dl_type_is_ip_any(dl_type) > > > || (flow->ct_state & CS_INVALID && oc->flags & NX_CT_F_COMMIT) > > > || (oc->alg == IPPORT_FTP && flow->nw_proto != IPPROTO_TCP) > > > || (oc->alg == IPPORT_TFTP && flow->nw_proto != IPPROTO_UDP)) { > > > @@ -7733,10 +8052,10 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, > > > case OFPACT_NAT: { > > > struct ofpact_nat *on = ofpact_get_NAT(a); > > > > > > - if (!dl_type_is_ip_any(flow->dl_type) || > > > - (on->range_af == AF_INET && flow->dl_type != htons(ETH_TYPE_IP)) || > > > + if (!dl_type_is_ip_any(dl_type) || > > > + (on->range_af == AF_INET && dl_type != htons(ETH_TYPE_IP)) || > > > (on->range_af == AF_INET6 > > > - && flow->dl_type != htons(ETH_TYPE_IPV6))) { > > > + && dl_type != htons(ETH_TYPE_IPV6))) { > > > return OFPERR_OFPBAC_MATCH_INCONSISTENT; > > > } > > > return 0; > > > @@ -7785,6 +8104,29 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, > > > case OFPACT_DEBUG_RECIRC: > > > return 0; > > > > > > + case OFPACT_ENCAP: > > > + flow->packet_type = ofpact_get_ENCAP(a)->new_pkt_type; > > > + if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) { > > > + flow->dl_type = htons(pt_ns_type(flow->packet_type)); > > > + } > > > + if (!is_ip_any(flow)) { > > > + flow->nw_proto = 0; > > > + } > > > + return 0; > > > + > > > + case OFPACT_DECAP: > > > + if (flow->packet_type == htonl(PT_ETH)) { > > > + /* Adjust the packet_type to allow subsequent actions. */ > > > + flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, > > > + ntohs(flow->dl_type)); > > > + } else { > > > + /* The actual packet_type is only known after decapsulation. > > > + * Do not allow subsequent actions that depend on packet headers. */ > > > + flow->packet_type = htonl(PT_UNKNOWN); > > > + flow->dl_type = OVS_BE16_MAX; > > > + } > > > + return 0; > > > + > > > default: > > > OVS_NOT_REACHED(); > > > } > > > @@ -7810,6 +8152,7 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len, > > > enum ofputil_protocol *usable_protocols) > > > { > > > struct ofpact *a; > > > + ovs_be32 packet_type = match->flow.packet_type; > > > ovs_be16 dl_type = match->flow.dl_type; > > > uint8_t nw_proto = match->flow.nw_proto; > > > enum ofperr error = 0; > > > @@ -7825,6 +8168,7 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len, > > > } > > > } > > > /* Restore fields that may have been modified. */ > > > + match->flow.packet_type = packet_type; > > > match->flow.dl_type = dl_type; > > > memcpy(&match->flow.vlans, &vlans, sizeof(vlans)); > > > match->flow.nw_proto = nw_proto; > > > @@ -8276,6 +8620,8 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port) > > > case OFPACT_CT: > > > case OFPACT_CT_CLEAR: > > > case OFPACT_NAT: > > > + case OFPACT_ENCAP: > > > + case OFPACT_DECAP: > > > default: > > > return false; > > > } > > > diff --git a/lib/ofp-ed-props.c b/lib/ofp-ed-props.c > > > new file mode 100644 > > > index 0000000..a346138 > > > --- /dev/null > > > +++ b/lib/ofp-ed-props.c > > > @@ -0,0 +1,157 @@ > > > +/* > > > + * Copyright (c) 2017 Intel, 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 > > > +#include > > > +#include "openvswitch/ofp-ed-props.h" > > > +#include "openvswitch/ofp-util.h" > > > +#include "openvswitch/ofpbuf.h" > > > +#include "openvswitch/ofp-parse.h" > > > +#include "util.h" > > > +#include "lib/packets.h" > > > + > > > + > > > +enum ofperr > > > +decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop, > > > + struct ofpbuf *out OVS_UNUSED, > > > + size_t *remaining) > > > +{ > > > + uint16_t prop_class = ntohs((*ofp_prop)->prop_class); > > > + size_t len = (*ofp_prop)->len; > > > + size_t pad_len = ROUND_UP(len, 8); > > > + > > > + if (pad_len > *remaining) { > > > + return OFPERR_OFPBAC_BAD_LEN; > > > + } > > > + > > > + switch (prop_class) { > > > + default: > > > + return OFPERR_NXBAC_UNKNOWN_ED_PROP; > > > + } > > > + > > > + *remaining -= pad_len; > > > + *ofp_prop = ALIGNED_CAST(const struct ofp_ed_prop_header *, > > > + ((char *)(*ofp_prop) + pad_len)); > > > + return 0; > > > +} > > > + > > > +enum ofperr > > > +encode_ed_prop(const struct ofpact_ed_prop **prop, > > > + struct ofpbuf *out OVS_UNUSED) > > > +{ > > > + size_t prop_len; > > > + > > > + switch ((*prop)->prop_class) { > > > + default: > > > + return OFPERR_OFPBAC_BAD_ARGUMENT; > > > + } > > > + > > > + *prop = ALIGNED_CAST(const struct ofpact_ed_prop *, > > > + ((char *)(*prop) + prop_len)); > > > + return 0; > > > +} > > > + > > > +bool > > > +parse_ed_prop_class(const char *str OVS_UNUSED, > > > + uint16_t *prop_class) > > > +{ > > > + if (!strcmp(str,"basic")) { > > > + *prop_class = OFPPPC_BASIC; > > > + } else if (!strcmp(str,"ethernet")) { > > > + *prop_class = OFPPPC_BASIC; > > > + } else if (!strcmp(str,"mpls")) { > > > + *prop_class = OFPPPC_MPLS; > > > + } else if (!strcmp(str,"gre")) { > > > + *prop_class = OFPPPC_GRE; > > > + } else if (!strcmp(str,"gtp")) { > > > + *prop_class = OFPPPC_GTP; > > > + } else { > > > + return false; > > > + } > > > + return true; > > > +} > > > + > > > +bool > > > +parse_ed_prop_type(uint16_t prop_class, > > > + const char *str OVS_UNUSED, > > > + uint8_t *type OVS_UNUSED) > > > +{ > > > + switch (prop_class) { > > > + default: > > > + return false; > > > + } > > > +} > > > + > > > +/* Parse the value of an encap/decap property based on property class > > > + * and type and append the parsed property in internal format to the > > > + * ofpbuf out. > > > + * Returns a malloced string in the event of a parse error. The caller > > > + * must free the string. > > > + */ > > > + > > > +char * > > > +parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type OVS_UNUSED, > > > + const char *value, struct ofpbuf *out OVS_UNUSED) > > > +{ > > > + > > > + if (value == NULL || *value == '\0') { > > > + return xstrdup("Value missing for encap property"); > > > + } > > > + > > > + switch (prop_class) { > > > + default: > > > + /* Unsupported property classes rejected before. */ > > > + OVS_NOT_REACHED(); > > > + } > > > + > > > + return NULL; > > > +} > > > + > > > +char * > > > +format_ed_prop_class(const struct ofpact_ed_prop *prop) > > > +{ > > > + switch (prop->prop_class) { > > > + case OFPPPC_BASIC: > > > + return "basic"; > > > + case OFPPPC_MPLS: > > > + return "mpls"; > > > + case OFPPPC_GRE: > > > + return "gre"; > > > + case OFPPPC_GTP: > > > + return "gtp"; > > > + default: > > > + OVS_NOT_REACHED(); > > > + } > > > +} > > > + > > > +char * > > > +format_ed_prop_type(const struct ofpact_ed_prop *prop) > > > +{ > > > + switch (prop->prop_class) { > > > + default: > > > + OVS_NOT_REACHED(); > > > + } > > > +} > > > + > > > +void > > > +format_ed_prop(struct ds *s OVS_UNUSED, > > > + const struct ofpact_ed_prop *prop) > > > +{ > > > + switch (prop->prop_class) { > > > + default: > > > + OVS_NOT_REACHED(); > > > + } > > > +} > > > diff --git a/lib/packets.h b/lib/packets.h > > > index a9d5e84..8287ca3 100644 > > > --- a/lib/packets.h > > > +++ b/lib/packets.h > > > @@ -1265,7 +1265,8 @@ pt_ns_type(ovs_be32 packet_type) > > > > > > /* Well-known packet_type field values. */ > > > enum packet_type { > > > - PT_ETH = PACKET_TYPE(OFPHTN_ONF, 0x0000), /* Default: Ethernet */ > > > + PT_ETH = PACKET_TYPE(OFPHTN_ONF, 0x0000), /* Default PT: Ethernet */ > > > + PT_USE_NEXT_PROTO = PACKET_TYPE(OFPHTN_ONF, 0xfffe), /* Pseudo PT for decap. */ > > > PT_IPV4 = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_IP), > > > PT_IPV6 = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_IPV6), > > > PT_MPLS = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS), > > > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > > > index c103793..a2f5dc7 100644 > > > --- a/ofproto/ofproto-dpif-xlate.c > > > +++ b/ofproto/ofproto-dpif-xlate.c > > > @@ -234,6 +234,8 @@ struct xlate_ctx { > > > bool in_action_set; /* Currently translating action_set, if true. */ > > > bool in_packet_out; /* Currently translating a packet_out msg, if > > > * true. */ > > > + bool pending_encap; /* Waiting to commit a pending encap > > > + * action, if true. */ > > > > > > uint8_t table_id; /* OpenFlow table ID where flow was found. */ > > > ovs_be64 rule_cookie; /* Cookie of the rule being translated. */ > > > @@ -3465,7 +3467,8 @@ xlate_commit_actions(struct xlate_ctx *ctx) > > > > > > ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow, > > > ctx->odp_actions, ctx->wc, > > > - use_masked); > > > + use_masked, ctx->pending_encap); > > > + ctx->pending_encap = false; > > > } > > > > > > static void > > > @@ -5503,6 +5506,8 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end, > > > case OFPACT_METER: > > > case OFPACT_SAMPLE: > > > case OFPACT_CLONE: > > > + case OFPACT_ENCAP: > > > + case OFPACT_DECAP: > > > case OFPACT_DEBUG_RECIRC: > > > case OFPACT_CT: > > > case OFPACT_CT_CLEAR: > > > @@ -5692,6 +5697,93 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc) > > > } > > > > > > static void > > > +rewrite_flow_encap_ethernet(struct xlate_ctx *ctx, > > > + struct flow *flow, > > > + struct flow_wildcards *wc) > > > +{ > > > + wc->masks.packet_type = OVS_BE32_MAX; > > > + if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) { > > > + /* Only adjust the packet_type and zero the dummy Ethernet addresses. */ > > > + ovs_be16 ethertype = pt_ns_type_be(flow->packet_type); > > > + flow->packet_type = htonl(PT_ETH); > > > + flow->dl_src = eth_addr_zero; > > > + flow->dl_dst = eth_addr_zero; > > > + flow->dl_type = ethertype; > > > + } else { > > > + xlate_report_debug(ctx, OFT_ACTION, > > > + "encap(ethernet) unsupported for packet type " > > > + "ethernet"); > > > + /* TODO: Error handling: drop packet. */ > > > + ctx->error = 1; > > > + } > > > +} > > > + > > > +static void > > > +xlate_generic_encap_action(struct xlate_ctx *ctx, > > > + const struct ofpact_encap *encap) > > > +{ > > > + struct flow *flow = &ctx->xin->flow; > > > + struct flow_wildcards *wc = ctx->wc; > > > + > > > + /* Ensure that any pending actions on the inner packet are applied before > > > + * rewriting the flow */ > > > + xlate_commit_actions(ctx); > > > + > > > + /* Rewrite the flow to reflect the effect of pushing the new encap header. */ > > > + switch (ntohl(encap->new_pkt_type)) { > > > + case PT_ETH: > > > + rewrite_flow_encap_ethernet(ctx, flow, wc); > > > + break; > > > + default: > > > + /* TODO: Error handling: Should not happen if the PT is checked > > > + * at decoding */ > > > + break; > > > + } > > > + > > > + if (!ctx->error) { > > > + /* The actual encap datapath action will be generated at next commit. */ > > > + ctx->pending_encap = true; > > > + } > > > +} > > > + > > > +/* Returns true if packet must be recirculated after decapsulation. */ > > > +static bool > > > +xlate_generic_decap_action(struct xlate_ctx *ctx, > > > + const struct ofpact_decap *decap OVS_UNUSED) > > > +{ > > > + struct flow *flow = &ctx->xin->flow; > > > + > > > + /* Ensure that any pending actions on the current packet are applied > > > + * before generating the decap action. */ > > > + xlate_commit_actions(ctx); > > > + > > > + /* We assume for now that the new_pkt_type is PT_USE_NEXT_PROTO. */ > > > + switch (ntohl(flow->packet_type)) { > > > + case PT_ETH: > > > + if (flow->vlans[0].tci & htons(VLAN_CFI)) { > > > + /* Error handling: drop packet. */ > > > + xlate_report_debug(ctx, OFT_ACTION, "Dropping packet, cannot " > > > + "decap Ethernet if VLAN is present."); > > > + ctx->error = 1; > > > + } else { > > > + /* Just change the packet_type. > > > + * Delay generating pop_eth to the next commit. */ > > > + flow->packet_type = htonl(PACKET_TYPE(OFPHTN_ETHERTYPE, > > > + ntohs(flow->dl_type))); > > > + ctx->wc->masks.dl_type = OVS_BE16_MAX; > > > + } > > > + return false; > > > + default: > > > + xlate_report_debug(ctx, OFT_ACTION, > > > + "decap() for unsupported packet type %x", > > > + ntohl(flow->packet_type)); > > > + /* TODO: Error handling: drop packet. */ > > > + ctx->error = 1; > > > + return false; > > > + } > > > +} > > > + > > > +static void > > > recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx) > > > { > > > /* No need to recirculate if already exiting. */ > > > @@ -5765,6 +5857,8 @@ recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx) > > > case OFPACT_EXIT: > > > case OFPACT_SAMPLE: > > > case OFPACT_CLONE: > > > + case OFPACT_ENCAP: > > > + case OFPACT_DECAP: > > > case OFPACT_UNROLL_XLATE: > > > case OFPACT_CT: > > > case OFPACT_CT_CLEAR: > > > @@ -6181,6 +6275,22 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, > > > xlate_clone(ctx, ofpact_get_CLONE(a)); > > > break; > > > > > > + case OFPACT_ENCAP: > > > + xlate_generic_encap_action(ctx, ofpact_get_ENCAP(a)); > > > + break; > > > + > > > + case OFPACT_DECAP: { > > > + bool recirc_needed = > > > + xlate_generic_decap_action(ctx, ofpact_get_DECAP(a)); > > > + if (!ctx->error && recirc_needed) { > > > + /* Recirculate for parsing of inner packet. */ > > > + ctx_trigger_freeze(ctx); > > > + /* Then continue with next action. */ > > > + a = ofpact_next(a); > > > + } > > > + break; > > > + } > > > + > > > case OFPACT_CT: > > > compose_conntrack_action(ctx, ofpact_get_CT(a)); > > > break; > > > @@ -6423,7 +6533,7 @@ xlate_wc_finish(struct xlate_ctx *ctx) > > > * use non-header fields as part of the cache. */ > > > flow_wildcards_clear_non_packet_fields(ctx->wc); > > > > > > - /* Wildcard ethernet addresses if the original packet type was not > > > + /* Wildcard ethernet fields if the original packet type was not > > > * Ethernet. */ > > > if (ctx->xin->upcall_flow->packet_type != htonl(PT_ETH)) { > > > ctx->wc->masks.dl_dst = eth_addr_zero; > > > @@ -6512,6 +6622,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) > > > .in_group = false, > > > .in_action_set = false, > > > .in_packet_out = xin->in_packet_out, > > > + .pending_encap = false, > > > > > > .table_id = 0, > > > .rule_cookie = OVS_BE64_MAX, > > > @@ -6672,6 +6783,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) > > > flow->packet_type = htonl(PT_ETH); > > > flow->dl_src = eth_addr_zero; > > > flow->dl_dst = eth_addr_zero; > > > + ctx.pending_encap = true; > > > } > > > > > > if (!xin->ofpacts && !ctx.rule) { > > > diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in > > > index 6552590..f6bd903 100644 > > > --- a/utilities/ovs-ofctl.8.in > > > +++ b/utilities/ovs-ofctl.8.in > > > @@ -1561,17 +1561,23 @@ the action set, the one written later replaces the earlier action: > > > \fBpop_mpls\fR > > > . > > > .IP 2. > > > -\fBpush_mpls\fR > > > +\fBdecap\fR > > > . > > > .IP 3. > > > -\fBpush_vlan\fR > > > +\fBencap\fR > > > . > > > .IP 4. > > > +\fBpush_mpls\fR > > > +. > > > +.IP 5. > > > +\fBpush_vlan\fR > > > +. > > > +.IP 6. > > > \fBdec_ttl\fR > > > .IQ > > > \fBdec_mpls_ttl\fR > > > . > > > -.IP 5. > > > +.IP 7. > > > \fBload\fR > > > .IQ > > > \fBmove\fR > > > @@ -1611,10 +1617,10 @@ the later modification takes effect, and when they modify > > > different parts of a field (or different fields), then both > > > modifications are applied. > > > . > > > -.IP 6. > > > +.IP 8. > > > \fBset_queue\fR > > > . > > > -.IP 7. > > > +.IP 9. > > > \fBgroup\fR > > > .IQ > > > \fBoutput\fR > > > @@ -1630,6 +1636,7 @@ not visible.) > > > .RE > > > .IP > > > Only the actions listed above may be written to the action set. > > > +\fBencap\fR and \fBdecap\fR actions are nonstandard. > > > . > > > .IP \fBwrite_metadata\fB:\fIvalue\fR[/\fImask\fR] > > > Updates the metadata field for the flow. If \fImask\fR is omitted, the > > > @@ -1730,6 +1737,65 @@ that is saved and restored includes all flow data and metadata > > > \fBpush\fR and \fBpop\fR actions, and the OpenFlow action set. > > > .IP > > > This action was added in Open vSwitch 2.6.90. > > > +. > > > +.IP > "\fBencap(\fR\fIheader\fR[\fB(\fR\fIprop\fR\fB=\fR\fIvalue\fR,\fItlv\fR\fB(\fR\fIclass\fR,\fItype\fR,\fIvalue\fB)\fR,...\fB)\fR]\fB)\fR" > > > +Encapsulates the packet with a new packet header, e.g., ethernet > > > +or nsh. > > > +. > > > +.RS > > > +.IP "\fIheader\fR" > > > +Used to specify encapsulation header type. > > > +. > > > +.IP "\fIprop\fR\fB=\fR\fIvalue\fR" > > > +Used to specify the initial value for the property in the encapsulation header. > > > +. > > > +.IP "\fItlv\fR\fB(\fR\fIclass\fR,\fItype\fR,\fIvalue\fB)\fR" > > > +Used to specify the initial value for the TLV (Type Length Value) > > > +in the encapsulation header. > > > +.RE > > > +.IP > > > +For example, \fBencap(ethernet)\fR will encapsulate the L3 packet with > > > +Ethernet header. > > > +.IP > > > +\fBencap(nsh(md_type=1))\fR will encapsulate the packet with nsh header > > > +and nsh metadata type 1. > > > +.IP > > > +\fBencap(nsh(md_type=2,tlv(0x1000,10,0x12345678)))\fR will encapsulate > > > +the packet with nsh header and nsh metadata type 2, and the nsh TLV with > > > +class 0x1000 and type 10 is set to 0x12345678. > > > +.IP > > > +\fIprop\fR\fB=\fR\fIvalue\fR is just used to set some > > > +necessary fields for encapsulation header initialization. Other fields > > > +in the encapsulation header must be set by \fBset_field\fR action. New > > > +encapsulation header implementation must add new match fields and > > > +corresponding \fBset\fR action in order that \fBset_field\fR action can > > > +change the fields in the encapsulation header on demand. > > > +.IP > > > +\fBencap(nsh(md_type=1)),\fR > > > +\fBset_field:0x1234->nsh_spi,set_field:0x11223344->nsh_c1\fR > > > +is an example to encapsulate nsh header and set nsh spi and c1. > > > +.IP > > > +This action was added in Open vSwitch 2.8. > > > +. > > > +.IP "\fBdecap(\fR[\fBpacket_type(ns=\fR\fInamespace\fR\fB,type=\fR\fItype\fR\fB)\fR]\fB)\fR" > > > +Decapsulates the outer packet header. > > > +. > > > +.RS > > > +.IP "\fBpacket_type(ns=\fR\fInamespace\fR\fB,type=\fR\fItype\fR\fB)\fR" > > > +It is optional and used to specify the outer header type of the > > > +decapsulated packet. \fInamespace\fR is 0 for Ethernet packet, > > > +1 for L3 packet, \fItype\fR\ is L3 protocol type, e.g., > > > +0x894f for nsh, 0x0 for Ethernet. > > > +.RE > > > +.IP > > > +By default, \fBdecap()\fR will decapsulate the outer packet header > > > +according to the packet header type, if > > > +\fBpacket_type(ns=\fR\fInamespace\fR\fB,type=\fR\fItype\fR\fB)\fR > > > +is given, it will decapsulate the given packet header, it will fail > > > +if the actual outer packet header type is not of > > > +\fBpacket_type(ns=\fR\fInamespace\fR\fB,type=\fR\fItype\fR\fB)\fR. > > > +.IP > > > +This action was added in Open vSwitch 2.8. > > > .RE > > > . > > > .PP > > > -- > > > 2.1.0 > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index bfc8a80..84522e0 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -4134,9 +4134,19 @@ parse_ed_props(const uint16_t prop_class, char **arg, int *n_props, struct ofpbu return NULL; } -/* The string representation of the encap action is - * encap(header_type(prop=,tlv(,,),...)) - */ +/* The generic string representation of the encap action is + * encap() + * encap((=,(),...)) + * + * TODO: The current implementation only supports the simple case that all + * encap parameters for a given encap header type are in a single property + * class, identified by the same keyword as the encap header type. + * To represent different property classes allowed by the OF action, the + * syntax should be generalized as follows: + * + * encap((=,(),...), + * (=,(),...),...) +*/ static char * OVS_WARN_UNUSED_RESULT parse_ENCAP(char *arg,