diff mbox

[ovs-dev,1/4] Add OF actions for generic encap and decap

Message ID AM2PR07MB1042BC58E4D27ED0D8A377378AD30@AM2PR07MB1042.eurprd07.prod.outlook.com
State Changes Requested
Headers show

Commit Message

Zoltan Balogh June 30, 2017, 3:29 p.m. UTC
From: Jan Scheurich <jan.scheurich@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(hdr=<header>)
  encap(hdr=<header>,
        prop(class=<class>,type=<type>,val=<simple>),
        prop(class=<class>,type=<type>,val(<complex>)))

CLI syntax for decap action:
  decap()
  decap(packet_type(ns=<pt_ns>,type=<pt_type>))

The first header supported for encap and decap is "ethernet" to convert
packets between packet_type (1,Ethertype) and (0,0).

Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>
Signed-off-by: Yi Yang <yi.y.yang@intel.com>
---
 include/openflow/openflow-common.h |   1 +
 include/openvswitch/automake.mk    |   1 +
 include/openvswitch/ofp-actions.h  |  31 ++++
 include/openvswitch/ofp-ed-props.h |  69 +++++++
 include/openvswitch/ofp-errors.h   |   9 +
 lib/automake.mk                    |   1 +
 lib/ofp-actions.c                  | 368 ++++++++++++++++++++++++++++++++++++-
 lib/ofp-ed-props.c                 | 150 +++++++++++++++
 lib/packets.h                      |   3 +-
 ofproto/ofproto-dpif-xlate.c       |  11 +-
 10 files changed, 633 insertions(+), 11 deletions(-)
 create mode 100644 include/openvswitch/ofp-ed-props.h
 create mode 100644 lib/ofp-ed-props.c

Comments

Ben Pfaff July 11, 2017, 9:45 p.m. UTC | #1
On Fri, Jun 30, 2017 at 03:29:29PM +0000, Zoltán Balogh wrote:
> From: Jan Scheurich <jan.scheurich@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(hdr=<header>)
>   encap(hdr=<header>,
>         prop(class=<class>,type=<type>,val=<simple>),
>         prop(class=<class>,type=<type>,val(<complex>)))
> 
> CLI syntax for decap action:
>   decap()
>   decap(packet_type(ns=<pt_ns>,type=<pt_type>))
> 
> The first header supported for encap and decap is "ethernet" to convert
> packets between packet_type (1,Ethertype) and (0,0).
> 
> Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>
> Signed-off-by: Yi Yang <yi.y.yang@intel.com>

Thanks for working on this.  I have some comments.

The patch was partially corrupted: lines in the patch that should have
had a single space were changed to blank lines.  This prevented the
patch from applying without manual editing.  I was able to work around
it.  (If you use "git send-email", this problem does not happen.)

Please rename __OFPHTN_MAX to avoid the __ prefix, since that's reserved
to the C implementation.  Maybe OFPHTN_N_TYPES would be better.

GCC says:

    ../lib/ofp-actions.c:4189:13: error: cast from 'char *' to 'struct ofpact_encap *' increases required alignment from 1 to 4 [-Werror,-Wcast-align]
    ../lib/ofp-actions.c:4222:16: error: cast from 'const uint8_t *' (aka 'const unsigned char *') to 'const struct ofpact_ed_prop *' increases required alignment from 1 to 2 [-Werror,-Wcast-align]

ofp-actions.c is using OpenFlow action numbers 29 and 30, but there's no
standardization of those values.  I suggest using an NX extension number
instead, since we control those directly in OVS.

Same goes for the new OFPERR_OFPBAC_* errors.  You can use NX
extensions.

In three places I see [0] used for a flexible array member.  MSVC
rejects this.  You can use [] in place of [0].

In encode_ENCAP(), please put a space on each side of the binary
operators here:
+    for (i=0; i<encap->n_props; i++) {
Same in format_ed_props().

What is your plan for handling ofpact_check__() with decap actions?

This patch is incomplete since it doesn't actually implement the encap
or decap actions (in do_xlate_actions()).  Maybe that's in a later
patch.

Thanks,

Ben.
Zoltan Balogh July 13, 2017, 4:49 p.m. UTC | #2
Hello Ben,


> GCC says:
> 
>     ../lib/ofp-actions.c:4189:13: error: cast from 'char *' to 'struct ofpact_encap *' increases required alignment
> from 1 to 4 [-Werror,-Wcast-align]
>     ../lib/ofp-actions.c:4222:16: error: cast from 'const uint8_t *' (aka 'const unsigned char *') to 'const struct
> ofpact_ed_prop *' increases required alignment from 1 to 2 [-Werror,-Wcast-align]

I cannot see these errors. I've tested with GCC 4.8.4 on Ubuntu 14.04.5 LTS and with GCC 6.3.0 on Ubuntu 17.04. 
Which version of GCC and Linux distro did you use? Did you pass any special args to make or to the configure script?

Best regards,
Zoltan
Ben Pfaff July 13, 2017, 5:22 p.m. UTC | #3
On Thu, Jul 13, 2017 at 04:49:09PM +0000, Zoltán Balogh wrote:
> Hello Ben,
> 
> 
> > GCC says:
> > 
> >     ../lib/ofp-actions.c:4189:13: error: cast from 'char *' to 'struct ofpact_encap *' increases required alignment
> > from 1 to 4 [-Werror,-Wcast-align]
> >     ../lib/ofp-actions.c:4222:16: error: cast from 'const uint8_t *' (aka 'const unsigned char *') to 'const struct
> > ofpact_ed_prop *' increases required alignment from 1 to 2 [-Werror,-Wcast-align]
> 
> I cannot see these errors. I've tested with GCC 4.8.4 on Ubuntu 14.04.5 LTS and with GCC 6.3.0 on Ubuntu 17.04. 
> Which version of GCC and Linux distro did you use? Did you pass any special args to make or to the configure script?

It might be specific to 32-bit builds, since I build and test on i386.
Yang, Yi July 14, 2017, 2:03 a.m. UTC | #4
I also tried it with 32 bit build on 64 bit machine by a simple c program with " -m32 -Werror -Wcast-align" options, but I can't reproduce this error information. The error message is saying char pointer or uint8 pointer may be any address (non-alignment to 4 or 8 bytes boundary), but struct ofpact_ed_prop pointer aligns to 4 or 8 bytes boundary, our code and variable struct results in that we can hardly handle this.

A way is to remove "-Wcast-align" from ovs compiling option.

-----Original Message-----
From: Ben Pfaff [mailto:blp@ovn.org] 
Sent: Friday, July 14, 2017 1:22 AM
To: Zoltán Balogh <zoltan.balogh@ericsson.com>
Cc: 'dev@openvswitch.org' <dev@openvswitch.org>; Jan Scheurich <jan.scheurich@ericsson.com>; Georg Schmuecking <Georg.Schmuecking@ericsson.com>; Jiri Benc (jbenc@redhat.com) <jbenc@redhat.com>; Yang, Yi Y <yi.y.yang@intel.com>
Subject: Re: [PATCH 1/4] Add OF actions for generic encap and decap

On Thu, Jul 13, 2017 at 04:49:09PM +0000, Zoltán Balogh wrote:
> Hello Ben,
> 
> 
> > GCC says:
> > 
> >     ../lib/ofp-actions.c:4189:13: error: cast from 'char *' to 
> > 'struct ofpact_encap *' increases required alignment from 1 to 4 [-Werror,-Wcast-align]
> >     ../lib/ofp-actions.c:4222:16: error: cast from 'const uint8_t *' 
> > (aka 'const unsigned char *') to 'const struct ofpact_ed_prop *' 
> > increases required alignment from 1 to 2 [-Werror,-Wcast-align]
> 
> I cannot see these errors. I've tested with GCC 4.8.4 on Ubuntu 14.04.5 LTS and with GCC 6.3.0 on Ubuntu 17.04. 
> Which version of GCC and Linux distro did you use? Did you pass any special args to make or to the configure script?

It might be specific to 32-bit builds, since I build and test on i386.
diff mbox

Patch

diff --git a/include/openflow/openflow-common.h b/include/openflow/openflow-common.h
index 3a0a575..2776712 100644
--- a/include/openflow/openflow-common.h
+++ b/include/openflow/openflow-common.h
@@ -465,6 +465,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_MAX
 };
 
 #endif /* openflow/openflow-common.h */
diff --git a/include/openvswitch/automake.mk b/include/openvswitch/automake.mk
index 699d9d7..c8e67a2 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 7b4aa92..33021e5 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"
 
 struct vl_mff_map;
 
@@ -89,6 +90,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")    \
@@ -969,6 +974,32 @@  struct ofpact_unroll_xlate {
     ovs_be64 rule_cookie;         /* OVS_BE64_MAX if none. */
 };
 
+/* OFPACT_ENCAP.
+ *
+ * Used for OFPAT_ENCAP. */
+
+#define OFPACT_ENCAP_MAX_PROP_SIZE 256
+
+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[0]; /* Properties in internal format. */
+};
+
+/* OFPACT_DECAP.
+ *
+ * Used for OFPAT_DECAP. */
+struct ofpact_decap {
+    struct ofpact ofpact;
+    ovs_be32 new_pkt_type;         /* 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.*/
+};
+
 /* 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..1db7e56
--- /dev/null
+++ b/include/openvswitch/ofp-ed-props.h
@@ -0,0 +1,69 @@ 
+/*
+ * 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"
+
+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. */
+    /*  new values go here  */
+    OFPPPC_EXPERIMENTER=0xffff,  /* Experimenter property class.
+                                  * First 32 bits of property data is exp
+                                  * id after that is the experimenter
+                                  * property data. */
+};
+
+/*
+ * External representation of encap/decap properties.
+ * These must be padded to a multiple of 4 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_value(struct ds *s, const struct ofpact_ed_prop *prop);
+
+#endif /* ofp-ed-props.h */
diff --git a/include/openvswitch/ofp-errors.h b/include/openvswitch/ofp-errors.h
index aeb58e0..c249459 100644
--- a/include/openvswitch/ofp-errors.h
+++ b/include/openvswitch/ofp-errors.h
@@ -272,6 +272,15 @@  enum ofperr {
      * 64. */
     OFPERR_NXBAC_BAD_CONJUNCTION,
 
+    /* OF1.5+(2,18).  Unsupported packet type in encap or decap. */
+    OFPERR_OFPBAC_BAD_HEADER_TYPE,
+
+    /* OF1.5+(2,20).  Unrecognized encap or decap property. */
+    OFPERR_OFPBAC_UNKNOWN_ED_PROP,
+
+    /* OF1.5+(2,21).  Error in encap or decap property. */
+    OFPERR_OFPBAC_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/ofp-actions.c b/lib/ofp-actions.c
index ae27d9d..a50d476 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -255,6 +255,12 @@  enum ofp_raw_action_type {
     /* NX1.0-1.4(6): struct nx_action_reg_move, ... VLMFF */
     NXAST_RAW_REG_MOVE,
 
+    /* OF1.3+(29): struct ofp_action_encap, ... */
+    OFPAT_RAW13_ENCAP,
+
+    /* OF1.3+(30): struct ofp_action_decap, ... */
+    OFPAT_RAW13_DECAP,
+
 /* ## ------------------------- ## */
 /* ## Nicira extension 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,316 @@  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 OFPAT_ENCAP */
+struct ofp_action_encap {
+    ovs_be16 type;         /* OFPAT_ENCAP */
+    ovs_be16 len;          /* Total size including any property TLVs. */
+    ovs_be32 new_pkt_type; /* Header type to add and PACKET_TYPE of result. */
+    ovs_be16 hdr_size;     /* (optional) Header size in bytes to add,
+                              0=not specified.*/
+    uint8_t pad[6];        /* Align to 64bits. */
+    struct ofp_ed_prop_header props[0];
+                           /* Encap TLV properties. */
+};
+OFP_ASSERT(sizeof(struct ofp_action_encap) == 16);
+
+static enum ofperr
+decode_OFPAT_RAW13_ENCAP(const struct ofp_action_encap *oae,
+                         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;
+    int err;
+
+    encap = ofpact_put_ENCAP(out);
+    encap->ofpact.raw = OFPAT_RAW13_ENCAP;
+    switch (ntohl(oae->new_pkt_type)) {
+    case PT_ETH:
+        /* Add supported encap header types here. */
+        break;
+    default:
+        return OFPERR_OFPBAC_BAD_HEADER_TYPE;
+    }
+    encap->new_pkt_type = oae->new_pkt_type;
+    encap->hdr_size = ntohs(oae->hdr_size);
+
+    ofp_prop = oae->props;
+    props_len = ntohs(oae->len) - offsetof(struct ofp_action_encap, props);
+    while (props_len > 0) {
+        err = decode_ed_prop(&ofp_prop, out, &props_len);
+        if (err) {
+            return err;
+        }
+        encap->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 ofp_action_encap *oae = put_OFPAT13_ENCAP(out);
+    int i;
+
+    oae->new_pkt_type = encap->new_pkt_type;
+    oae->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(char **arg, int *n_props, struct ofpbuf *out)
+{
+    char *key, *value, *err, *prop, *str;
+    uint16_t prop_class;
+    uint8_t prop_type;
+
+    str = *arg;
+    while (ofputil_parse_key_value(arg, &key, &value)) {
+        if (strcmp(key, "prop") != 0) {
+            return xasprintf("Invalid encap argument: %s", key);
+        }
+        /* Parse property class. */
+        str = prop = value;
+        if (!ofputil_parse_key_value(&prop, &key, &value)
+            || strcmp(key, "class") != 0 || value == NULL) {
+            return xasprintf("Missing prop class: %s", str);
+        }
+        if (!parse_ed_prop_class(value, &prop_class)) {
+            return xasprintf("Invalid encap prop class: %s", value);
+        }
+        /* Parse property type. */
+        str = prop;
+        if (!ofputil_parse_key_value(&prop, &key, &value)
+            || strcmp(key, "type") != 0 || value == NULL) {
+            return xasprintf("Missing encap prop type: %s", str);
+        }
+        if (!parse_ed_prop_type(prop_class, value, &prop_type)) {
+            return xasprintf("Invalid property type: %s", value);
+        }
+        /* Parse the prop value dependent on class and type. */
+        str = prop;
+        if (!ofputil_parse_key_value(&prop, &key, &value)
+            || strcmp(key, "val") != 0 || value == NULL) {
+            return xasprintf("Missing encap prop value: %s", str);
+        }
+        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(hdr=<pkt_type>,prop(class=<class>,type=<type>,val=<val>),prop(...),...)
+ */
+
+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;
+    size_t start_ofs;
+    int n_props = 0;
+
+    start_ofs = out->size;
+    encap = ofpact_put_ENCAP(out);
+    encap->hdr_size = 0;
+    /* Parse encap header type. */
+    str = arg;
+    if (!ofputil_parse_key_value(&arg, &key, &value)
+        || strcmp(key, "hdr") != 0 || value == NULL) {
+        return xasprintf("Missing encap hdr: %s", str);
+    }
+    if (!parse_encap_header(value, &encap->new_pkt_type)) {
+        return xasprintf("Encap hdr not supported: %s", value);
+    }
+    /* Parse encap properties. */
+    error = parse_ed_props(&arg, &n_props, out);
+    if (error != NULL) {
+        return error;
+    }
+    /* ofbuf out may have been re-allocated. */
+    encap = (struct ofpact_encap *) ((char *)out->data + start_ofs);
+    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++) {
+        ds_put_format(s, ",prop(class=%s,", format_ed_prop_class(prop));
+        ds_put_format(s, "type=%s,", format_ed_prop_type(prop));
+        format_ed_prop_value(s, prop);
+        ds_put_cstr(s, ")");
+        p += ROUND_UP(prop->len, 8);
+        prop = (const struct ofpact_ed_prop *) p;
+    }
+}
+
+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, "hdr=%s", format_encap_pkt_type(a->new_pkt_type));
+    format_ed_props(s, a->n_props, a->props);
+    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+}
+
+/* Action structure for OFPAT_DECAP */
+struct ofp_action_decap {
+    ovs_be16 type;         /* OFPAT_DECAP */
+    ovs_be16 len;          /* Total size including any property TLVs. */
+    ovs_be32 new_pkt_type; /* 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_be16 hdr_size;     /* (optional) Header size in bytes to remove,
+                              0=not specified. NOT SUPPORTED. */
+    uint8_t pad[6];        /* Align to 64bits. */
+    struct ofp_ed_prop_header props[0];
+                           /* Decap TLV properties. NOT SUPPORTED. */
+
+};
+OFP_ASSERT(sizeof(struct ofp_action_decap) == 16);
+
+static enum ofperr
+decode_OFPAT_RAW13_DECAP(const struct ofp_action_decap *oad,
+                          enum ofp_version ofp_version OVS_UNUSED,
+                          struct ofpbuf *ofpacts)
+{
+    struct ofpact_decap * decap;
+
+    decap = ofpact_put_DECAP(ofpacts);
+    decap->ofpact.raw = OFPAT_RAW13_DECAP;
+    decap->new_pkt_type = oad->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 ofp_action_decap *oad = put_OFPAT13_DECAP(out);
+
+    oad->len = htons(16);
+    oad->new_pkt_type = decap->new_pkt_type;
+    /* No support for hdr_size and decap property TLVs yet. */
+    oad->hdr_size = 0;
+}
+
+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_MAX) {
+                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 +7120,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 +7197,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 +7306,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 +7451,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 +7814,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 +7827,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 +7905,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 +7970,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 +8008,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 +8019,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 +8039,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 +8064,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 +8116,21 @@  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:
+        /* FIXME: The resulting packet_type is not known at flow deployment
+         * time. How can we allow actions with pre-requisites after decap? */
+        return 0;
+
     default:
         OVS_NOT_REACHED();
     }
@@ -7810,6 +8156,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 +8172,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 +8624,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..8e58f6f
--- /dev/null
+++ b/lib/ofp-ed-props.c
@@ -0,0 +1,150 @@ 
+/*
+ * 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);
+
+    switch (prop_class) {
+    default:
+        return OFPERR_OFPBAC_UNKNOWN_ED_PROP;
+    }
+
+    *remaining -= pad_len;
+    *ofp_prop = (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 = (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,"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_value(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 1f4fe1d..e2247c8 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -5241,6 +5241,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:
@@ -5502,6 +5504,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:
@@ -5917,6 +5921,11 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             xlate_clone(ctx, ofpact_get_CLONE(a));
             break;
 
+        case OFPACT_ENCAP:
+        case OFPACT_DECAP: {
+            break;
+        }
+
         case OFPACT_CT:
             compose_conntrack_action(ctx, ofpact_get_CT(a));
             break;
@@ -6158,7 +6167,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;