diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index dd1a76e..5a64b38 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -756,6 +756,23 @@ struct ovs_action_push_eth {
 #endif
 };
 
+#define OVS_ENDECAP_MAX_PROPS_LEN 256
+/*
+ * struct ovs_action_endecap - %OVS_ACTION_ATTR_ENCAP, OVS_ACTION_ATTR_DECAP
+ * @ns: packet type name space.
+ * @type: packet type.
+ * @hdr_size: size of encap or decap header.
+ * @props_len: length of encap or decap properties.
+ * @props: encap or decap properties.
+ */
+struct ovs_action_endecap {
+	uint16_t ns;
+	uint16_t type;
+	uint16_t hdr_size;
+	uint16_t props_len;
+	uint8_t  props[OVS_ENDECAP_MAX_PROPS_LEN];
+};
+
 /**
  * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
  *
@@ -831,6 +848,10 @@ enum ovs_nat_attr {
  * @OVS_ACTION_ATTR_PUSH_ETH: Push a new outermost Ethernet header onto the
  * packet.
  * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the packet.
+ * @OVS_ACTION_ATTR_ENCAP: Generic encap action to push generic header, such
+ * as NSH header, Ethernet header, etc.
+ * @OVS_ACTION_ATTR_DECAP: Generic decap action to remove generic header, such
+ * as NSH header, Ethernet header, etc.
  *
  * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not all
  * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
@@ -866,6 +887,8 @@ enum ovs_action_attr {
 	OVS_ACTION_ATTR_TRUNC,        /* u32 struct ovs_action_trunc. */
 	OVS_ACTION_ATTR_PUSH_ETH,     /* struct ovs_action_push_eth. */
 	OVS_ACTION_ATTR_POP_ETH,      /* No argument. */
+	OVS_ACTION_ATTR_ENCAP,        /* struct ovs_action_endecap. */
+	OVS_ACTION_ATTR_DECAP,        /* struct ovs_action_endecap. */
 
 #ifndef __KERNEL__
 	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
diff --git a/include/openvswitch/automake.mk b/include/openvswitch/automake.mk
index d7a499d..bdfc0d6 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 e7529bf..749e3ec 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,       ofpact, "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,30 @@ struct ofpact_unroll_xlate {
     ovs_be64 rule_cookie;         /* OVS_BE64_MAX if none. */
 };
 
+/* OFPACT_ENCAP.
+ *
+ * Used for OFPAT_ENCAP. */
+struct ofpact_encap {
+    struct ofpact ofpact;
+    struct ofp_header_type new_pkt_type;  /* new header type */
+    uint16_t hdr_size;                    /* new header size in bytes */
+    uint16_t props_len;                   /* total length in bytes of props */
+    uint8_t pads[4];                      /* aligned to 8 bytes */
+    uint8_t props[0];	  /* encap/decap properties */
+};
+
+/* OFPACT_DECAP.
+ *
+ * Used for OFPAT_DECAP. */
+struct ofpact_decap {
+    struct ofpact ofpact;
+    struct ofp_header_type new_pkt_type;  /* new header type */
+    uint16_t hdr_size;                    /* new header size in bytes */
+    uint16_t props_len;                   /* total length in bytes of props */
+    uint8_t pads[4];                      /* aligned to 8 bytes */
+    uint8_t props[0];   /* encap/decap properties */
+};
+
 /* 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..01ffbae
--- /dev/null
+++ b/include/openvswitch/ofp-ed-props.h
@@ -0,0 +1,87 @@
+/*
+ * 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 */
+	OFPPPC_NSH   = 4,            /* NSH 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
+	                              */
+};
+
+enum ofp_ed_nsh_prop_type {
+	OFPPPT_PROP_NSH_NONE = 0,    /* unused */
+	OFPPPT_PROP_NSH_MDTYPE = 1,  /* property MDTYPE in NSH */
+	OFPPPT_PROP_NSH_TLV = 2,     /* property TLV in NSH */
+	/*  new values go here  */
+};
+
+struct ofp_header_type {
+	uint16_t ns;
+        uint16_t type;
+};
+
+struct ofp_ed_prop_header {
+	uint16_t prop_class;
+	uint8_t type;
+	uint8_t len;
+};
+
+struct ofp_ed_prop_nsh_md_type {
+	ovs_be16 prop_class;     /* encap/decap property class NSH */
+	uint8_t type;            /* encap/decap property type MDTYPE */
+	uint8_t len;             /* property length  = 8 */
+	uint8_t md_type;         /* NSH MD type */
+	uint8_t pad[3];          /* Padding to 8 bytes */
+};
+
+struct ofp_ed_prop_nsh_tlv {
+	ovs_be16 prop_class;     /* encap/decap property class NSH */
+	uint8_t type;            /* encap/decap property type TLV */
+	uint8_t len;             /* property length  = 8 + tlv_len + padding */
+	ovs_be16 tlv_class;      /* Metadata class */
+	uint8_t tlv_type;        /* Metadata type including C bit */
+	uint8_t tlv_len;         /* Metadata value length (0-127) */
+	uint8_t data[0];         /* tlv_len octets of metadata value,
+	                          * padded to a multiple of 4 bytes
+	                          */
+};
+
+enum ofperr ed_prop_ntohs(struct ofp_ed_prop_header * props, uint16_t length);
+enum ofperr ed_prop_htons(struct ofp_ed_prop_header * props, uint16_t length);
+struct ofp_header_type get_encap_header_type(const char * type);
+uint16_t get_ed_prop_class(const char * prop_class_name);
+uint8_t get_ed_prop_type(uint16_t prop_class, const char * prop_type_name);
+char * parse_and_put_ed_prop(char ** arg, struct ofpbuf *ofpacts, uint16_t * props_len);
+char * get_ed_pkt_type(const struct ofp_header_type * pkt_type);
+char * get_ed_prop_class_name(const struct ofp_ed_prop_header * prop_header);
+char * get_ed_prop_type_name(const struct ofp_ed_prop_header * prop_header);
+void format_ed_props(const struct ofp_ed_prop_header *props, const uint16_t props_len, struct ds *s);
+
+#endif /* ofp-ed-props.h */
diff --git a/lib/automake.mk b/lib/automake.mk
index 62b2f38..39977a6 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/dpif-netdev.c b/lib/dpif-netdev.c
index eca83e3..f5fd00c 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -5175,6 +5175,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
     case OVS_ACTION_ATTR_PUSH_ETH:
     case OVS_ACTION_ATTR_POP_ETH:
     case OVS_ACTION_ATTR_CLONE:
+    case OVS_ACTION_ATTR_ENCAP:
+    case OVS_ACTION_ATTR_DECAP:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/dpif.c b/lib/dpif.c
index 30cb155..fa0f617 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1224,6 +1224,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
     case OVS_ACTION_ATTR_PUSH_ETH:
     case OVS_ACTION_ATTR_POP_ETH:
     case OVS_ACTION_ATTR_CLONE:
+    case OVS_ACTION_ATTR_ENCAP:
+    case OVS_ACTION_ATTR_DECAP:
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 6b9a642..4df1f0c 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -615,6 +615,8 @@ requires_datapath_assistance(const struct nlattr *a)
     case OVS_ACTION_ATTR_PUSH_ETH:
     case OVS_ACTION_ATTR_POP_ETH:
     case OVS_ACTION_ATTR_CLONE:
+    case OVS_ACTION_ATTR_ENCAP:
+    case OVS_ACTION_ATTR_DECAP:
         return false;
 
     case OVS_ACTION_ATTR_UNSPEC:
@@ -772,6 +774,13 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
             }
             break;
 
+        case OVS_ACTION_ATTR_ENCAP:
+            /* TODO */
+            break;
+        case OVS_ACTION_ATTR_DECAP:
+            /* TODO */
+            break;
+
         case OVS_ACTION_ATTR_OUTPUT:
         case OVS_ACTION_ATTR_TUNNEL_PUSH:
         case OVS_ACTION_ATTR_TUNNEL_POP:
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 476bc8b..6da7d78 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -128,6 +128,8 @@ odp_action_len(uint16_t type)
     case OVS_ACTION_ATTR_PUSH_ETH: return sizeof(struct ovs_action_push_eth);
     case OVS_ACTION_ATTR_POP_ETH: return 0;
     case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE;
+    case OVS_ACTION_ATTR_ENCAP: return ATTR_LEN_VARIABLE;
+    case OVS_ACTION_ATTR_DECAP: return ATTR_LEN_VARIABLE;
 
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
@@ -911,6 +913,12 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
     case OVS_ACTION_ATTR_CLONE:
         format_odp_clone_action(ds, a);
         break;
+    case OVS_ACTION_ATTR_ENCAP:
+        /* TODO */
+        break;
+    case OVS_ACTION_ATTR_DECAP:
+        /* TODO */
+        break;
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
     default:
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index be8cc20..8fd3b8f 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. ## */
 /* ## ------------------------- ## */
@@ -471,6 +477,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:
@@ -3881,6 +3889,203 @@ format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a, struct ds *s)
     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 */
+    struct ofp_header_type 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/decap 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 *ofpacts)
+{
+    struct ofpact_encap * encap;
+    uint16_t length;
+    struct ofp_ed_prop_header * props = NULL;
+
+    encap = ofpact_put_ENCAP(ofpacts);
+    encap->ofpact.raw = OFPAT_RAW13_ENCAP;
+    encap->new_pkt_type.ns = ntohs(oae->new_pkt_type.ns);
+    encap->new_pkt_type.type = ntohs(oae->new_pkt_type.type);
+    encap->hdr_size = ntohs(oae->hdr_size);
+
+    length = ntohs(oae->len) - offsetof(struct ofp_action_encap, props);
+    encap->props_len = length;
+    if (length > 0) {
+        props = ofpbuf_put(ofpacts, oae->props, length);
+        ed_prop_ntohs(props, length);
+    }
+    encap = ofpacts->header;
+    ofpact_finish_ENCAP(ofpacts, &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);
+    struct ofp_ed_prop_header * props = NULL;
+
+    oae->new_pkt_type.ns = htons(encap->new_pkt_type.ns);
+    oae->new_pkt_type.type = htons(encap->new_pkt_type.type);
+    oae->hdr_size = htons(encap->hdr_size);
+    if (encap->props_len > 0) {
+        props = ofpbuf_put(out, encap->props, encap->props_len);
+        ed_prop_htons(props, encap->props_len);
+    }
+    pad_ofpat(out, start_ofs);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_ENCAP(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+              enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    struct ofpact_encap * encap;
+    char *key, *value;
+    char *error = NULL;
+    uint16_t props_len;
+
+    encap = ofpact_put_ENCAP(ofpacts);
+
+    /* Parse encap type */
+    if (!ofputil_parse_key_value(&arg, &key, &value)) {
+        return xasprintf("Invalid encap arguments: %s", arg);
+    }
+    struct ofp_header_type header_type = get_encap_header_type(key);
+    encap->new_pkt_type.ns = header_type.ns;
+    encap->new_pkt_type.type = header_type.type;
+
+    error = parse_and_put_ed_prop(&arg, ofpacts, &props_len);
+    if (error != NULL) {
+        return error;
+    }
+    encap = ofpacts->header;
+    encap->props_len = props_len;
+    ofpact_finish_ENCAP(ofpacts, &encap);
+    return NULL;
+}
+
+static void
+format_ENCAP(const struct ofpact_encap *a, struct ds *s)
+{
+    struct ofp_ed_prop_header * prop_hdr = (struct ofp_ed_prop_header *)a->props;
+
+    ds_put_format(s, "%sencap(%s", colors.paren, colors.end);
+    ds_put_format(s, "%s", get_ed_pkt_type(&a->new_pkt_type));
+    format_ed_props(prop_hdr, a->props_len, s);
+    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 */
+    struct ofp_header_type 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/decap properties */
+};
+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;
+    uint16_t length;
+    struct ofp_ed_prop_header * props = NULL;
+
+    decap = ofpact_put_DECAP(ofpacts);
+    decap->ofpact.raw = OFPAT_RAW13_DECAP;
+    decap->new_pkt_type.ns = ntohs(oad->new_pkt_type.ns);
+    decap->new_pkt_type.type = ntohs(oad->new_pkt_type.type);
+    decap->hdr_size = ntohs(oad->hdr_size);
+
+    length = ntohs(oad->len) - offsetof(struct ofp_action_decap, props);
+    decap->props_len = length;
+    if (length > 0) {
+        props = ofpbuf_put(ofpacts, oad->props, length);
+        ed_prop_ntohs(props, length);
+    }
+    decap = ofpacts->header;
+    ofpact_finish_DECAP(ofpacts, &decap);
+    return 0;
+}
+
+static void
+encode_DECAP(const struct ofpact_decap *decap,
+                enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
+{
+    size_t start_ofs = out->size;
+    struct ofp_action_decap *oad = put_OFPAT13_DECAP(out);
+    struct ofp_ed_prop_header * props = NULL;
+
+    oad->new_pkt_type.ns = htons(decap->new_pkt_type.ns);
+    oad->new_pkt_type.type = htons(decap->new_pkt_type.type);
+    oad->hdr_size = htons(decap->hdr_size);
+    if (decap->props_len > 0) {
+        props = ofpbuf_put(out, decap->props, decap->props_len);
+        ed_prop_htons(props, decap->props_len);
+    }
+    pad_ofpat(out, start_ofs);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_DECAP(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+              enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    struct ofpact_decap * decap;
+    char *key, *value;
+    char *error = NULL;
+    uint16_t props_len;
+
+    decap = ofpact_put_DECAP(ofpacts);
+
+    /* Parse decap type */
+    if (!ofputil_parse_key_value(&arg, &key, &value)) {
+        return xasprintf("Invalid decap arguments: %s", arg);
+    }
+    struct ofp_header_type header_type = get_encap_header_type(key);
+    decap->new_pkt_type.ns = header_type.ns;
+    decap->new_pkt_type.type = header_type.type;
+
+    error = parse_and_put_ed_prop(&arg, ofpacts, &props_len);
+    if (error != NULL) {
+        return error;
+    }
+    decap = ofpacts->header;
+    decap->props_len = props_len;
+    ofpact_finish_DECAP(ofpacts, &decap);
+    return NULL;
+}
+
+static void
+format_DECAP(const struct ofpact_decap *a, struct ds *s)
+{
+    struct ofp_ed_prop_header * prop_hdr = (struct ofp_ed_prop_header *)a->props;
+
+    ds_put_format(s, "%sdecap(%s", colors.paren, colors.end);
+    ds_put_format(s, "%s", get_ed_pkt_type(&a->new_pkt_type));
+    format_ed_props(prop_hdr, a->props_len, s);
+    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+}
+
 
 /* Action structures for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE, and
  * NXAST_RESUBMIT_TABLE_CT.
@@ -6590,6 +6795,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:
@@ -6665,6 +6872,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
@@ -6772,6 +6981,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);
@@ -6915,6 +7126,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;
     }
@@ -7573,6 +7786,10 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
     case OFPACT_DEBUG_RECIRC:
         return 0;
 
+    case OFPACT_ENCAP:
+    case OFPACT_DECAP:
+        return 0;
+
     default:
         OVS_NOT_REACHED();
     }
@@ -8064,6 +8281,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..19de240
--- /dev/null
+++ b/lib/ofp-ed-props.c
@@ -0,0 +1,334 @@
+/*
+ * 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"
+
+enum ofperr
+ed_prop_ntohs(struct ofp_ed_prop_header * props, uint16_t length)
+{
+    char * ptr = (char *)props;
+    while (length > sizeof(struct ofp_ed_prop_header)) {
+        uint16_t prop_class = ntohs(props->prop_class);
+        switch(prop_class) {
+        case OFPPPC_NSH:
+            switch(props->type) {
+            case OFPPPT_PROP_NSH_MDTYPE: {
+                props->prop_class = prop_class;
+                ptr += sizeof(struct ofp_ed_prop_nsh_md_type);
+                length -= sizeof(struct ofp_ed_prop_nsh_md_type);
+                break;
+            case OFPPPT_PROP_NSH_TLV: {
+                struct ofp_ed_prop_nsh_tlv * prop_nsh_tlv = (struct ofp_ed_prop_nsh_tlv *)props;
+                props->prop_class = prop_class;
+                prop_nsh_tlv->tlv_class = ntohs(prop_nsh_tlv->tlv_class);
+                ptr += sizeof(struct ofp_ed_prop_nsh_tlv) + prop_nsh_tlv->tlv_len;
+                length -= sizeof(struct ofp_ed_prop_nsh_tlv) + prop_nsh_tlv->tlv_len;
+                break;
+            }
+            default:
+                return OFPERR_OFPBAC_BAD_ARGUMENT;
+            }
+            props = (struct ofp_ed_prop_header *)ptr;
+            break;
+        }
+        default:
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+    }
+    return 0;
+}
+
+enum ofperr
+ed_prop_htons(struct ofp_ed_prop_header * props, uint16_t length)
+{
+    char * ptr = (char *)props;
+    while (length > sizeof(struct ofp_ed_prop_header)) {
+        uint16_t prop_class = props->prop_class;
+        switch(prop_class) {
+        case OFPPPC_NSH:
+            switch(props->type) {
+            case OFPPPT_PROP_NSH_MDTYPE: {
+                props->prop_class = htons(prop_class);
+                ptr += sizeof(struct ofp_ed_prop_nsh_md_type);
+                length -= sizeof(struct ofp_ed_prop_nsh_md_type);
+                break;
+            case OFPPPT_PROP_NSH_TLV: {
+                struct ofp_ed_prop_nsh_tlv * prop_nsh_tlv = (struct ofp_ed_prop_nsh_tlv *)props;
+                props->prop_class = htons(prop_class);
+                prop_nsh_tlv->tlv_class = htons(prop_nsh_tlv->tlv_class);
+                ptr += sizeof(struct ofp_ed_prop_nsh_tlv) + prop_nsh_tlv->tlv_len;
+                length -= sizeof(struct ofp_ed_prop_nsh_tlv) + prop_nsh_tlv->tlv_len;
+                break;
+            }
+            default:
+                return OFPERR_OFPBAC_BAD_ARGUMENT;
+            }
+            props = (struct ofp_ed_prop_header *)ptr;
+            break;
+        }
+        default:
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+    }
+    return 0;
+}
+
+struct ofp_header_type get_encap_header_type(const char * type)
+{
+    struct ofp_header_type header_type;
+    if (strcmp(type, "nsh") == 0) {
+        header_type.ns = OFPHTN_ETHERTYPE;
+        header_type.type = 0x894f;
+    } else {
+        header_type.ns = OFPHTN_ONF;
+        header_type.type = 0;
+    }
+    return header_type;
+}
+
+uint16_t
+get_ed_prop_class(const char * prop_class_name)
+{
+    if (strcmp(prop_class_name, "nsh") == 0) {
+        return OFPPPC_NSH;
+    } else {
+        return 0;
+    }
+}
+
+uint8_t
+get_ed_prop_type(uint16_t prop_class, const char * prop_type_name)
+{
+    switch(prop_class) {
+    case OFPPPC_NSH:
+        if (strcmp(prop_type_name, "md_type") == 0) {
+            return OFPPPT_PROP_NSH_MDTYPE;
+        } else if (strcmp(prop_type_name, "tlv_hdr") == 0) {
+            return OFPPPT_PROP_NSH_TLV;
+        } else {
+            return 0;
+        }
+    default:
+        return 0;
+    }
+}
+
+char * parse_and_put_ed_prop(char ** arg, struct ofpbuf *ofpacts, uint16_t * props_len)
+{
+    char * error = NULL;
+    char *key, *value;
+    uint16_t length = 0;
+
+    while (true) {
+        *arg += strspn(*arg, ", \t\r\n{}");
+        if (**arg == '\0') {
+            break;
+        }
+
+        /* Get property class */
+        if (!ofputil_parse_key_value(arg, &key, &value)) {
+            return xasprintf("Invalid encap arguments: %s", *arg);
+        }
+        uint16_t prop_class = get_ed_prop_class(key);
+        if (prop_class == OFPPPC_BASIC) {
+            return xasprintf("Invalid encap arguments: %s", *arg);
+        }
+
+        /* Get property type */
+        if (!ofputil_parse_key_value(arg, &key, &value)) {
+            return xasprintf("Invalid encap arguments: %s", *arg);
+        }
+
+        uint8_t prop_type = get_ed_prop_type(prop_class, key);
+        if (prop_type == OFPPPT_PROP_NSH_NONE) {
+            return xasprintf("Invalid encap arguments: %s", *arg);
+        }
+
+        switch(prop_class) {
+        case OFPPPC_NSH:
+            switch(prop_type) {
+            case OFPPPT_PROP_NSH_MDTYPE: {
+                uint8_t md_type;
+                if (value == NULL) {
+                    return xasprintf("Invalid encap arguments: %s", *arg);
+                }
+                error = str_to_u8(value, "md_type", &md_type);
+                if (error != NULL) {
+                    return error;
+                }
+                struct ofp_ed_prop_nsh_md_type * prop_nsh_md_type
+                    = ofpbuf_put_uninit(ofpacts, sizeof(struct ofp_ed_prop_nsh_md_type));
+                prop_nsh_md_type->prop_class = prop_class;
+                prop_nsh_md_type->type = prop_type;
+                prop_nsh_md_type->len = sizeof(struct ofp_ed_prop_nsh_md_type);
+                prop_nsh_md_type->md_type = md_type;
+                length += sizeof(struct ofp_ed_prop_nsh_md_type);
+                break;
+            }
+            case OFPPPT_PROP_NSH_TLV: {
+                uint16_t tlv_hdr;
+                size_t tlv_value_len;
+                if (value == NULL) {
+                    return xasprintf("Invalid encap arguments: %s", *arg);
+                }
+                error = str_to_u16(value, "tlv_hdr", &tlv_hdr);
+                if (error != NULL) {
+                    return error;
+                }
+                struct ofp_ed_prop_nsh_tlv * prop_nsh_tlv
+                    = ofpbuf_put_uninit(ofpacts, sizeof(struct ofp_ed_prop_nsh_tlv));
+                prop_nsh_tlv->prop_class = prop_class;
+                prop_nsh_tlv->type = prop_type;
+                prop_nsh_tlv->tlv_class = tlv_hdr;
+
+                /* Get NSH TLV: tlv_type */
+                if (!ofputil_parse_key_value(arg, &key, &value)) {
+                    return xasprintf("Invalid encap arguments: %s", *arg);
+                }
+                uint8_t tlv_type;
+                error = str_to_u8(key, "tlv_type", &tlv_type);
+                if (error != NULL) {
+                    return error;
+                }
+                prop_nsh_tlv->tlv_type = tlv_type;
+
+                /* Get NSH TLV: tlv_len */
+                if (!ofputil_parse_key_value(arg, &key, &value)) {
+                    return xasprintf("Invalid encap arguments: %s", *arg);
+                }
+                uint8_t tlv_len;
+                error = str_to_u8(key, "tlv_len", &tlv_len);
+                if (error != NULL) {
+                    return error;
+                }
+                prop_nsh_tlv->tlv_len = tlv_len;
+
+                /* Get NSH TLV: value */
+                if (!ofputil_parse_key_value(arg, &key, &value)) {
+                    return xasprintf("Invalid encap arguments: %s", *arg);
+                }
+
+                ofpbuf_put_hex(ofpacts, key + 2, &tlv_value_len);
+                prop_nsh_tlv->len = sizeof(struct ofp_ed_prop_nsh_tlv) + tlv_value_len;
+                length += sizeof(struct ofp_ed_prop_nsh_tlv) + tlv_value_len;
+                break;
+            }
+            default:
+                return xasprintf("Invalid encap arguments: %s", *arg);
+            }
+            break;
+        default:
+            return xasprintf("Invalid encap arguments: %s", *arg);
+        }
+    }
+    if (props_len != NULL) {
+        *props_len = length;
+    }
+    return NULL;
+}
+
+char *
+get_ed_pkt_type(const struct ofp_header_type * pkt_type)
+{
+    if ((pkt_type->ns == OFPHTN_ETHERTYPE)
+        && (pkt_type->type == 0x894f)) {
+        return "nsh";
+    } else {
+        /* TODO */
+        return NULL;
+    }
+}
+
+char *
+get_ed_prop_class_name(const struct ofp_ed_prop_header * prop_header)
+{
+    switch(prop_header->prop_class) {
+    case OFPPPC_NSH:
+        return "nsh";
+    default:
+        return NULL;
+    }
+}
+
+char *
+get_ed_prop_type_name(const struct ofp_ed_prop_header * prop_header)
+{
+    switch(prop_header->prop_class) {
+    case OFPPPC_NSH:
+        switch(prop_header->type) {
+        case OFPPPT_PROP_NSH_MDTYPE:
+            return "md_type";
+        case OFPPPT_PROP_NSH_TLV:
+            return "tlv_hdr";
+        default:
+            return NULL;
+        }
+        break;
+    default:
+        return NULL;
+    }
+}
+
+void
+format_ed_props(const struct ofp_ed_prop_header *props, const uint16_t props_len, struct ds *s)
+{
+    struct ofp_ed_prop_header * prop_hdr = NULL;
+    size_t ofs = 0;
+    const uint8_t * ptr = (uint8_t *)props;
+
+    while (ofs + sizeof(struct ofp_ed_prop_header) < props_len) {
+        prop_hdr = (struct ofp_ed_prop_header *)ptr;
+        ds_put_format(s, ",{%s,", get_ed_prop_class_name(prop_hdr));
+        ds_put_format(s, "%s:", get_ed_prop_type_name(prop_hdr));
+        switch(prop_hdr->prop_class) {
+        case OFPPPC_NSH:
+            switch(prop_hdr->type) {
+            case OFPPPT_PROP_NSH_MDTYPE: {
+                struct ofp_ed_prop_nsh_md_type * prop_nsh_md_type = (struct ofp_ed_prop_nsh_md_type *)prop_hdr;
+                ds_put_format(s, "%d}", prop_nsh_md_type->md_type);
+                ofs += sizeof(struct ofp_ed_prop_nsh_md_type);
+                ptr += sizeof(struct ofp_ed_prop_nsh_md_type);
+                break;
+            }
+            case OFPPPT_PROP_NSH_TLV: {
+                struct ofp_ed_prop_nsh_tlv * tlv = (struct ofp_ed_prop_nsh_tlv *)prop_hdr;
+                ds_put_format(s, "0x%04x,", tlv->tlv_class);
+                ds_put_format(s, "%d,%d,0x", tlv->tlv_type, tlv->tlv_len);
+                for (uint8_t i = 0; i < tlv->tlv_len; i++) {
+                    ds_put_format(s, "%02x", tlv->data[i]);
+                }
+                ds_put_char(s, '}');
+                ofs += sizeof(struct ofp_ed_prop_nsh_tlv) + tlv->tlv_len;
+                ptr += sizeof(struct ofp_ed_prop_nsh_tlv) + tlv->tlv_len;
+                break;
+            }
+            default:
+                ofs += sizeof(struct ofp_ed_prop_header);
+                break;
+            }
+            break;
+        default:
+            ofs += sizeof(struct ofp_ed_prop_header);
+            break;
+        }
+    }
+}
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 5e910f2..a078b61 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -7687,7 +7687,7 @@ ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
      * If there is no value, we are done. */
     const char *value_delims;
     if (key_delim == ':' || key_delim == '=') {
-        value_delims = ", \t\r\n";
+        value_delims = ", \t\r\n}";
     } else if (key_delim == '(') {
         value_delims = ")";
     } else {
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 4c7e362..0d4e8f1 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1199,6 +1199,8 @@ dpif_sflow_read_actions(const struct flow *flow,
 	    break;
 	case OVS_ACTION_ATTR_SAMPLE:
 	case OVS_ACTION_ATTR_CLONE:
+	case OVS_ACTION_ATTR_ENCAP:
+	case OVS_ACTION_ATTR_DECAP:
 	case OVS_ACTION_ATTR_UNSPEC:
 	case __OVS_ACTION_ATTR_MAX:
 	default:
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 731c376..a74e1ac 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -4159,6 +4159,8 @@ xlate_fixup_actions(struct ofpbuf *b, const struct nlattr *actions,
         case OVS_ACTION_ATTR_CT:
         case OVS_ACTION_ATTR_PUSH_ETH:
         case OVS_ACTION_ATTR_POP_ETH:
+        case OVS_ACTION_ATTR_ENCAP:
+        case OVS_ACTION_ATTR_DECAP:
         case OVS_ACTION_ATTR_METER:
             ofpbuf_put(b, a, nl_attr_len_pad(a, left));
             break;
@@ -5264,6 +5266,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:
@@ -5522,6 +5526,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:
@@ -5937,6 +5943,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             xlate_clone(ctx, ofpact_get_CLONE(a));
             break;
 
+        case OFPACT_ENCAP:
+            /* TODO */
+            break;
+
+        case OFPACT_DECAP:
+            /* TODO */
+            break;
+
         case OFPACT_CT:
             compose_conntrack_action(ctx, ofpact_get_CT(a));
             break;
