[ovs-dev] [PATCH v5 1/2] OF support and translation of generic encap and decap

Jan Scheurich jan.scheurich at ericsson.com
Thu Aug 10 15:25:20 UTC 2017


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:

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=<value>,tlv(<class>,<type>,<value>),...))
- */
+/* The generic string representation of the encap action is
+ * encap(<header_type>)
+ * encap(<header_type>(<prop1>=<value>,<prop2>(<structured value>),...))
+ *
+ * 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(<header_type>(<prop1>=<value>,<prop2>(<structured value>),...),
+ *       <class1>(<prop3>=<value>,<prop4>(<structured value>),...),...)
+*/

 static char * OVS_WARN_UNUSED_RESULT
 parse_ENCAP(char *arg,

BR, Jan

> -----Original Message-----
> From: Ben Pfaff [mailto:blp at ovn.org]
> Sent: Tuesday, 08 August, 2017 21:36
> To: Jan Scheurich <jan.scheurich at ericsson.com>
> Cc: Yi Yang <yi.y.yang at intel.com>; dev at openvswitch.org; Zoltán Balogh <zoltan.balogh at ericsson.com>
> 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(<PacketType>)
> > encap((<PacketType>,<PropClass>(<PropType>=<Value>,<PropType>(<ComplexValue>)),...)
> >
> > 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 at intel.com]
> > > Sent: Wednesday, 02 August, 2017 10:04
> > > To: dev at openvswitch.org
> > > Cc: blp at ovn.org; Jan Scheurich <jan.scheurich at ericsson.com>; Yi Yang <yi.y.yang at intel.com>; Zoltán Balogh
> > > <zoltan.balogh at ericsson.com>
> > > Subject: [PATCH v5 1/2] OF support and translation of generic encap and decap
> > >
> > > From: Jan Scheurich <jan.scheurich at ericsson.com>
> > >
> > > 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(<header>)
> > >   encap(<header>(<prop>=<value>,<tlv>(<class>,<type>,<value>),...))
> > >
> > > 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=<pt_ns>,type=<pt_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 <jan.scheurich at ericsson.com>
> > > Signed-off-by: Yi Yang <yi.y.yang at intel.com>
> > > Signed-off-by: Zoltan Balogh <zoltan.balogh at ericsson.com>
> > > Co-authored-by: Zoltan Balogh <zoltan.balogh at ericsson.com>
> > > Signed-off-by: Ben Pfaff <blp at ovn.org>
> > > ---
> > >  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=<value>,tlv(<class>,<type>,<value>),...))
> > > + */
> > > +
> > > +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 <config.h>
> > > +#include <arpa/inet.h>
> > > +#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
> >


More information about the dev mailing list