diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 5806aba..bc6c94b 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -793,6 +793,25 @@ struct ovs_action_push_eth {
 	struct ovs_key_ethernet addresses;
 };
 
+#define OVS_ENCAP_NSH_MAX_MD_LEN 16
+/*
+ * struct ovs_action_encap_nsh - %OVS_ACTION_ATTR_ENCAP_NSH
+ * @flags: NSH header flags.
+ * @mdtype: NSH metadata type.
+ * @mdlen: Length of NSH metadata in bytes.
+ * @np: NSH next_protocol: Inner packet type.
+ * @path_hdr: NSH service path id and service index.
+ * @metadata: NSH metadata for MD type 1 or 2
+ */
+struct ovs_action_encap_nsh {
+    uint8_t flags;
+    uint8_t mdtype;
+    uint8_t mdlen;
+    uint8_t np;
+    __be32 path_hdr;
+    uint8_t metadata[OVS_ENCAP_NSH_MAX_MD_LEN];
+};
+
 /**
  * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
  *
@@ -868,6 +887,8 @@ 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_NSH: encap NSH action to push NSH header.
+ * @OVS_ACTION_ATTR_DECAP_NSH: decap NSH action to remove NSH header.
  *
  * 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
@@ -909,6 +930,8 @@ enum ovs_action_attr {
 	OVS_ACTION_ATTR_TUNNEL_POP,    /* u32 port number. */
 	OVS_ACTION_ATTR_CLONE,         /* Nested OVS_CLONE_ATTR_*.  */
 	OVS_ACTION_ATTR_METER,         /* u32 meter number. */
+	OVS_ACTION_ATTR_ENCAP_NSH,    /* struct ovs_action_encap_nsh. */
+	OVS_ACTION_ATTR_DECAP_NSH,    /* No argument. */
 #endif
 	__OVS_ACTION_ATTR_MAX,	      /* Nothing past this will be accepted
 				       * from userspace. */
diff --git a/include/openvswitch/ofp-ed-props.h b/include/openvswitch/ofp-ed-props.h
index cf2fa62..306c6fe 100644
--- a/include/openvswitch/ofp-ed-props.h
+++ b/include/openvswitch/ofp-ed-props.h
@@ -27,9 +27,10 @@ extern "C" {
 
 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_MPLS  = 1,            /* MPLS property class. */
+    OFPPPC_GRE   = 2,            /* GRE property class. */
+    OFPPPC_GTP   = 3,            /* GTP property class. */
+    OFPPPC_NSH   = 4,            /* NSH property class */
 
     /* Experimenter property class.
      *
@@ -39,6 +40,12 @@ enum ofp_ed_prop_class {
     OFPPPC_EXPERIMENTER=0xffff
 };
 
+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 */
+};
+
 /*
  * External representation of encap/decap properties.
  * These must be padded to a multiple of 8 bytes.
@@ -49,6 +56,22 @@ struct ofp_ed_prop_header {
     uint8_t len;
 };
 
+struct ofp_ed_prop_nsh_md_type {
+    struct ofp_ed_prop_header header;
+    uint8_t md_type;         /* NSH MD type .*/
+    uint8_t pad[3];          /* Padding to 8 bytes. */
+};
+
+struct ofp_ed_prop_nsh_tlv {
+    struct ofp_ed_prop_header header;
+    ovs_be16 tlv_class;      /* Metadata class. */
+    uint8_t tlv_type;        /* Metadata type including C bit. */
+    uint8_t tlv_len;         /* Metadata value length (0-127). */
+
+    /* tlv_len octets of metadata value, padded to a multiple of 8 bytes. */
+    uint8_t data[0];
+};
+
 /*
  * Internal representation of encap/decap properties
  */
@@ -58,6 +81,21 @@ struct ofpact_ed_prop {
     uint8_t len;
 };
 
+struct ofpact_ed_prop_nsh_md_type {
+    struct ofpact_ed_prop header;
+    uint8_t md_type;         /* NSH MD type .*/
+    uint8_t pad[3];          /* Padding to 8 bytes. */
+};
+
+struct ofpact_ed_prop_nsh_tlv {
+    struct ofpact_ed_prop header;
+    ovs_be16 tlv_class;      /* Metadata class. */
+    uint8_t tlv_type;        /* Metadata type including C bit. */
+    uint8_t tlv_len;         /* Metadata value length (0-127). */
+
+    /* tlv_len octets of metadata value, padded to a multiple of 8 bytes. */
+    uint8_t data[0];
+};
 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,
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 8ecc9d1..e2cd931 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -5407,6 +5407,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_NSH:
+    case OVS_ACTION_ATTR_DECAP_NSH:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/dpif.c b/lib/dpif.c
index 1af53f5..e71f6a3 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1255,6 +1255,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_NSH:
+    case OVS_ACTION_ATTR_DECAP_NSH:
     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 e631c68..5f4d23a 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -289,10 +289,9 @@ odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,
                 }
                 break;
             case NSH_M_TYPE2:
-                /* TODO */
-                break;
             default:
-                OVS_NOT_REACHED();
+                /* No support for setting any other metadata format yet. */
+                break;
         }
     } else {
         uint8_t flags = (ntohs(nsh->ver_flags_len) & NSH_FLAGS_MASK) >>
@@ -314,10 +313,9 @@ odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,
                 }
                 break;
             case NSH_M_TYPE2:
-                /* TODO */
-                break;
             default:
-                OVS_NOT_REACHED();
+                /* No support for setting any other metadata format yet. */
+                break;
         }
     }
 }
@@ -654,6 +652,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_NSH:
+    case OVS_ACTION_ATTR_DECAP_NSH:
         return false;
 
     case OVS_ACTION_ATTR_UNSPEC:
@@ -818,6 +818,26 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
             }
             break;
 
+        case OVS_ACTION_ATTR_ENCAP_NSH: {
+            const struct ovs_action_encap_nsh *enc_nsh = nl_attr_get(a);
+            DP_PACKET_BATCH_FOR_EACH (packet, batch) {
+                encap_nsh(packet, enc_nsh);
+            }
+            break;
+        }
+        case OVS_ACTION_ATTR_DECAP_NSH: {
+            size_t i, num = batch->count;
+
+            DP_PACKET_BATCH_REFILL_FOR_EACH (i, num, packet, batch) {
+                if (decap_nsh(packet)) {
+                    dp_packet_batch_refill(batch, packet, i);
+                } else {
+                    dp_packet_delete(packet);
+                }
+            }
+            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 fa4871c..ef8b39d 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -129,6 +129,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_NSH: return ATTR_LEN_VARIABLE;
+    case OVS_ACTION_ATTR_DECAP_NSH: return 0;
 
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
@@ -266,10 +268,9 @@ format_nsh_key(struct ds *ds, const struct ovs_key_nsh *key)
             }
             break;
         case NSH_M_TYPE2:
-            /* TODO */
-            break;
         default:
-            OVS_NOT_REACHED();
+            /* No support for matching other metadata formats yet. */
+            break;
     }
 }
 
@@ -340,6 +341,40 @@ format_nsh_key_mask(struct ds *ds, const struct ovs_key_nsh *key,
     }
 }
 
+static void
+format_odp_encap_nsh_action(struct ds *ds,
+                            const struct ovs_action_encap_nsh *encap_nsh)
+ {
+    uint32_t path_hdr = ntohl(encap_nsh->path_hdr);
+    uint32_t spi = (path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
+    uint8_t si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
+
+    ds_put_cstr(ds, "encap_nsh(");
+    ds_put_format(ds, "flags=%d", encap_nsh->flags);
+    ds_put_format(ds, ",mdtype=%d", encap_nsh->mdtype);
+    ds_put_format(ds, ",np=%d", encap_nsh->np);
+    ds_put_format(ds, ",spi=0x%x", spi);
+    ds_put_format(ds, ",si=%d", si);
+    switch (encap_nsh->mdtype) {
+    case NSH_M_TYPE1: {
+        struct nsh_md1_ctx *md1_ctx =
+            ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh->metadata);
+        for (int i = 0; i < 4; i++) {
+            ds_put_format(ds, ",c%d=0x%x", i + 1,
+                          ntohl(get_16aligned_be32(&md1_ctx->c[i])));
+        }
+        break;
+    }
+    case NSH_M_TYPE2:
+        ds_put_cstr(ds, ",md2=");
+        ds_put_hex(ds, encap_nsh->metadata, encap_nsh->mdlen);
+        break;
+    default:
+        OVS_NOT_REACHED();
+    }
+    ds_put_format(ds, ")");
+}
+
 static const char *
 slow_path_reason_to_string(uint32_t reason)
 {
@@ -635,7 +670,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
                       gnh->oam ? "oam," : "",
                       gnh->critical ? "crit," : "",
                       ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
- 
+
         if (gnh->opt_len) {
             ds_put_cstr(ds, ",options(");
             format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
@@ -1022,6 +1057,12 @@ format_odp_action(struct ds *ds, const struct nlattr *a,
     case OVS_ACTION_ATTR_CLONE:
         format_odp_clone_action(ds, a, portno_names);
         break;
+    case OVS_ACTION_ATTR_ENCAP_NSH:
+        format_odp_encap_nsh_action(ds, nl_attr_get(a));
+        break;
+    case OVS_ACTION_ATTR_DECAP_NSH:
+        ds_put_cstr(ds, "decap_nsh()");
+        break;
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
     default:
@@ -1740,6 +1781,113 @@ find_end:
 }
 
 static int
+parse_odp_encap_nsh_action(const char *s, struct ofpbuf *actions)
+{
+    int n = 0;
+    int ret = 0;
+    struct ovs_action_encap_nsh encap_nsh;
+    uint32_t spi;
+    uint8_t si;
+    uint32_t cd;
+
+    if (!ovs_scan_len(s, &n, "encap_nsh(")) {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    /* The default is NSH_M_TYPE1 */
+    encap_nsh.flags = 0;
+    encap_nsh.mdtype = NSH_M_TYPE1;
+    encap_nsh.mdlen = NSH_M_TYPE1_MDLEN;
+    encap_nsh.path_hdr = htonl(255);
+    memset(encap_nsh.metadata, 0, NSH_M_TYPE1_MDLEN);
+
+    for (;;) {
+        n += strspn(s + n, delimiters);
+        if (s[n] == ')') {
+            break;
+        }
+
+        if (ovs_scan_len(s, &n, "flags=%"SCNi8, &encap_nsh.flags)) {
+            continue;
+        }
+        if (ovs_scan_len(s, &n, "mdtype=%"SCNi8, &encap_nsh.mdtype)) {
+            switch (encap_nsh.mdtype) {
+            case NSH_M_TYPE1:
+                /* This is the default format. */;
+                break;
+            case NSH_M_TYPE2:
+                /* Length will be updated later. */
+                encap_nsh.mdlen = 0;
+                break;
+            default:
+                ret = -EINVAL;
+                goto out;
+            }
+            continue;
+        }
+        if (ovs_scan_len(s, &n, "np=%"SCNi8, &encap_nsh.np)) {
+            continue;
+        }
+        if (ovs_scan_len(s, &n, "spi=0x%"SCNx32, &spi)) {
+            encap_nsh.path_hdr =
+                    htonl(((spi << NSH_SPI_SHIFT) & NSH_SPI_MASK) |
+                            (ntohl(encap_nsh.path_hdr) & ~NSH_SPI_MASK));
+            continue;
+        }
+        if (ovs_scan_len(s, &n, "si=%"SCNi8, &si)) {
+            encap_nsh.path_hdr =
+                    htonl((si << NSH_SI_SHIFT) |
+                            (ntohl(encap_nsh.path_hdr) & ~NSH_SI_MASK));
+            continue;
+        }
+        if (encap_nsh.mdtype == NSH_M_TYPE1) {
+            struct nsh_md1_ctx *md1 =
+                ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh.metadata);
+            if (ovs_scan_len(s, &n, "c1=0x%"SCNx32, &cd)) {
+                put_16aligned_be32(&md1->c[0], htonl(cd));
+                continue;
+            }
+            if (ovs_scan_len(s, &n, "c2=0x%"SCNx32, &cd)) {
+                put_16aligned_be32(&md1->c[1], htonl(cd));
+                continue;
+            }
+            if (ovs_scan_len(s, &n, "c3=0x%"SCNx32, &cd)) {
+                put_16aligned_be32(&md1->c[2], htonl(cd));
+                continue;
+            }
+            if (ovs_scan_len(s, &n, "c4=0x%"SCNx32, &cd)) {
+                put_16aligned_be32(&md1->c[3], htonl(cd));
+                continue;
+            }
+        }
+        else if (encap_nsh.mdtype == NSH_M_TYPE2) {
+            struct ofpbuf b;
+            char buf[512];
+            size_t mdlen;
+            if (ovs_scan_len(s, &n, "md2=0x%511[0-9a-fA-F]", buf)) {
+                ofpbuf_use_stub(&b, encap_nsh.metadata,
+                                OVS_ENCAP_NSH_MAX_MD_LEN);
+                ofpbuf_put_hex(&b, buf, &mdlen);
+                encap_nsh.mdlen = mdlen;
+                ofpbuf_uninit(&b);
+            }
+            continue;
+        }
+    }
+out:
+    if (ret < 0) {
+        return ret;
+    } else {
+        size_t size = offsetof(struct ovs_action_encap_nsh, metadata)
+                + ROUND_UP(encap_nsh.mdlen, 4);
+        nl_msg_put_unspec(actions, OVS_ACTION_ATTR_ENCAP_NSH,
+                          &encap_nsh, size);
+        return n;
+    }
+}
+
+static int
 parse_action_list(const char *s, const struct simap *port_names,
                   struct ofpbuf *actions)
 {
@@ -1941,6 +2089,24 @@ parse_odp_action(const char *s, const struct simap *port_names,
     }
 
     {
+        if (!strncmp(s, "encap_nsh(", 10)) {
+            int retval = parse_odp_encap_nsh_action(s, actions);
+            if (retval < 0) {
+                return retval;
+            }
+            return retval + 1;
+        }
+    }
+
+    {
+        int n;
+        if (ovs_scan(s, "decap_nsh()%n", &n)) {
+            nl_msg_put_flag(actions, OVS_ACTION_ATTR_DECAP_NSH);
+            return n;
+        }
+    }
+
+    {
         uint32_t port;
         int n;
 
@@ -2454,6 +2620,7 @@ format_eth(struct ds *ds, const char *name, const struct eth_addr key,
     }
 }
 
+
 static void
 format_be64(struct ds *ds, const char *name, ovs_be64 key,
             const ovs_be64 *mask, bool verbose)
@@ -6463,7 +6630,8 @@ get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh, bool is_mask)
             }
             break;
         case NSH_M_TYPE2:
-            /* TODO: MD type 2 */
+        default:
+            /* No match support for other MD formats yet. */
             break;
         }
     }
@@ -6486,7 +6654,8 @@ put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow,
             }
             break;
         case NSH_M_TYPE2:
-            /* TODO: MD type 2 */
+        default:
+            /* No match support for other MD formats yet. */
             memset(flow->nsh.c, 0, sizeof flow->nsh.c);
             break;
     }
@@ -6619,11 +6788,58 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
 }
 
 static void
+odp_put_decap_nsh_action(struct ofpbuf *odp_actions)
+{
+    nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_DECAP_NSH);
+}
+
+static void
+odp_put_encap_nsh_action(struct ofpbuf *odp_actions,
+                         const struct flow *flow,
+                         struct ofpbuf *encap_data)
+{
+    struct ovs_action_encap_nsh encap_nsh;
+
+    encap_nsh.flags = flow->nsh.flags;
+    encap_nsh.mdtype = flow->nsh.mdtype;
+    encap_nsh.np = flow->nsh.np;
+    encap_nsh.path_hdr = htonl((ntohl(flow->nsh.spi) << NSH_SPI_SHIFT) |
+                                   flow->nsh.si);
+
+    switch (encap_nsh.mdtype) {
+    case NSH_M_TYPE1: {
+        struct nsh_md1_ctx *md1 =
+            ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh.metadata);
+        encap_nsh.mdlen = NSH_M_TYPE1_MDLEN;
+        for (int i = 0; i < 4; i++) {
+            put_16aligned_be32(&md1->c[i], flow->nsh.c[i]);
+        }
+        break;
+    }
+    case NSH_M_TYPE2:
+        if (encap_data) {
+            ovs_assert(encap_data->size < OVS_ENCAP_NSH_MAX_MD_LEN);
+            encap_nsh.mdlen = encap_data->size;
+            memcpy(encap_nsh.metadata, encap_data->data, encap_data->size);
+        } else {
+            encap_nsh.mdlen = 0;
+        }
+        break;
+    default:
+        encap_nsh.mdlen = 0;
+        break;
+    }
+    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_ENCAP_NSH,
+                      &encap_nsh, sizeof(encap_nsh));
+}
+
+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)
+                          bool pending_encap,
+                          struct ofpbuf *encap_data)
 {
     if (flow->packet_type == base_flow->packet_type) {
         return;
@@ -6640,22 +6856,40 @@ commit_packet_type_change(const struct flow *flow,
             base_flow->dl_dst = flow->dl_dst;
             break;
         }
+        case PT_NSH:
+            /* encap_nsh */
+            odp_put_encap_nsh_action(odp_actions, flow, encap_data);
+            base_flow->packet_type = flow->packet_type;
+            /* Update all packet headers in base_flow. */
+            memcpy(&base_flow->dl_dst, &flow->dl_dst,
+                   sizeof(*flow) - offsetof(struct flow, dl_dst));
+            break;
         default:
-            /* Only the above protocols are supported for encap. The check
-             * is done at action decoding. */
+            /* Only the above protocols are supported for encap.
+             * The check is done at action translation. */
             OVS_NOT_REACHED();
         }
     } else {
+        /* This is an explicit or implicit decap case. */
         if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE &&
             base_flow->packet_type == htonl(PT_ETH)) {
-            /* pop_eth */
+            /* Generate pop_eth and continue without recirculation. */
             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();
+            /* All other decap cases require recirculation.
+             * No need to update the base flow here. */
+            switch (ntohl(base_flow->packet_type)) {
+            case PT_NSH:
+                /* decap_nsh. */
+                odp_put_decap_nsh_action(odp_actions);
+                break;
+            default:
+                /* Checks are done during translation. */
+                OVS_NOT_REACHED();
+            }
         }
     }
 
@@ -6674,12 +6908,14 @@ commit_packet_type_change(const struct flow *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 pending_encap)
+                   bool use_masked, bool pending_encap,
+                   struct ofpbuf *encap_data)
 {
     enum slow_path_reason slow1, slow2;
     bool mpls_done = false;
 
-    commit_packet_type_change(flow, base, odp_actions, wc, pending_encap);
+    commit_packet_type_change(flow, base, odp_actions, wc,
+                              pending_encap, encap_data);
     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. */
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 111cd9c..27c2ab4 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -277,7 +277,8 @@ enum slow_path_reason commit_odp_actions(const struct flow *,
                                          struct ofpbuf *odp_actions,
                                          struct flow_wildcards *wc,
                                          bool use_masked,
-                                         bool pending_encap);
+                                         bool pending_encap,
+                                         struct ofpbuf *encap_data);
 
 /* ofproto-dpif interface.
  *
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 7076537..bfc8a80 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -4055,6 +4055,7 @@ decode_NXAST_RAW_ENCAP(const struct nx_action_encap *nae,
     encap->ofpact.raw = NXAST_RAW_ENCAP;
     switch (ntohl(nae->new_pkt_type)) {
     case PT_ETH:
+    case PT_NSH:
         /* Add supported encap header types here. */
         break;
     default:
@@ -4103,6 +4104,8 @@ parse_encap_header(const char *hdr, ovs_be32 *packet_type)
 {
     if (strcmp(hdr, "ethernet") == 0) {
         *packet_type = htonl(PT_ETH);
+    } else if (strcmp(hdr, "nsh") == 0) {
+        *packet_type = htonl(PT_NSH);
     } else {
         return false;
     }
@@ -4178,6 +4181,8 @@ format_encap_pkt_type(const ovs_be32 pkt_type)
     switch (ntohl(pkt_type)) {
     case PT_ETH:
         return "ethernet";
+    case PT_NSH:
+        return "nsh";
     default:
         return "UNKNOWN";
     }
diff --git a/lib/ofp-ed-props.c b/lib/ofp-ed-props.c
index a346138..eb168fe 100644
--- a/lib/ofp-ed-props.c
+++ b/lib/ofp-ed-props.c
@@ -30,6 +30,7 @@ decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop,
                size_t *remaining)
 {
     uint16_t prop_class = ntohs((*ofp_prop)->prop_class);
+    uint8_t prop_type = (*ofp_prop)->type;
     size_t len = (*ofp_prop)->len;
     size_t pad_len = ROUND_UP(len, 8);
 
@@ -38,6 +39,45 @@ decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop,
     }
 
     switch (prop_class) {
+    case OFPPPC_NSH: {
+        switch (prop_type) {
+        case OFPPPT_PROP_NSH_MDTYPE: {
+            struct ofp_ed_prop_nsh_md_type *opnmt =
+                ALIGNED_CAST(struct ofp_ed_prop_nsh_md_type *, *ofp_prop);
+            if (len > sizeof(*opnmt) || len > *remaining) {
+                return OFPERR_NXBAC_BAD_ED_PROP;
+            }
+            struct ofpact_ed_prop_nsh_md_type *pnmt =
+                    ofpbuf_put_uninit(out, sizeof(*pnmt));
+            pnmt->header.prop_class = prop_class;
+            pnmt->header.type = prop_type;
+            pnmt->header.len = len;
+            pnmt->md_type = opnmt->md_type;
+            break;
+        }
+        case OFPPPT_PROP_NSH_TLV: {
+            struct ofp_ed_prop_nsh_tlv *opnt =
+                ALIGNED_CAST(struct ofp_ed_prop_nsh_tlv *, *ofp_prop);
+            size_t tlv_pad_len = ROUND_UP(opnt->tlv_len, 8);
+            if (len != sizeof(*opnt) + tlv_pad_len || len > *remaining) {
+                return OFPERR_NXBAC_BAD_ED_PROP;
+            }
+            struct ofpact_ed_prop_nsh_tlv *pnt =
+                    ofpbuf_put_uninit(out, sizeof(*pnt));
+            pnt->header.prop_class = prop_class;
+            pnt->header.type = prop_type;
+            pnt->header.len = len;
+            pnt->tlv_class = opnt->tlv_class;
+            pnt->tlv_type = opnt->tlv_type;
+            pnt->tlv_len = opnt->tlv_len;
+            ofpbuf_put(out, opnt->data, tlv_pad_len);
+            break;
+        }
+        default:
+            return OFPERR_NXBAC_UNKNOWN_ED_PROP;
+        }
+        break;
+    }
     default:
         return OFPERR_NXBAC_UNKNOWN_ED_PROP;
     }
@@ -55,6 +95,43 @@ encode_ed_prop(const struct ofpact_ed_prop **prop,
     size_t prop_len;
 
     switch ((*prop)->prop_class) {
+    case OFPPPC_NSH: {
+        switch ((*prop)->type) {
+        case OFPPPT_PROP_NSH_MDTYPE: {
+            struct ofpact_ed_prop_nsh_md_type *pnmt =
+                ALIGNED_CAST(struct ofpact_ed_prop_nsh_md_type *, *prop);
+            struct ofp_ed_prop_nsh_md_type *opnmt =
+                    ofpbuf_put_uninit(out, sizeof(*opnmt));
+            opnmt->header.prop_class = htons((*prop)->prop_class);
+            opnmt->header.type = (*prop)->type;
+            opnmt->header.len =
+                    offsetof(struct ofp_ed_prop_nsh_md_type, pad);
+            opnmt->md_type = pnmt->md_type;
+            prop_len = sizeof(*pnmt);
+            break;
+        }
+        case OFPPPT_PROP_NSH_TLV: {
+            struct ofpact_ed_prop_nsh_tlv *pnt =
+                ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *, *prop);
+            struct ofp_ed_prop_nsh_tlv *opnt;
+            size_t tlv_pad_len = ROUND_UP(pnt->tlv_len, 8);
+            size_t len = sizeof(*opnt) + tlv_pad_len;
+            opnt = ofpbuf_put_uninit(out, len);
+            opnt->header.prop_class = htons((*prop)->prop_class);
+            opnt->header.type = (*prop)->type;
+            opnt->header.len = len;
+            opnt->tlv_class = pnt->tlv_class;
+            opnt->tlv_type = pnt->tlv_type;
+            opnt->tlv_len = pnt->tlv_len;
+            memcpy(opnt->data, pnt->data, tlv_pad_len);
+            prop_len = sizeof(*pnt) + tlv_pad_len;
+            break;
+        }
+        default:
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+        break;
+    }
     default:
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
@@ -78,6 +155,8 @@ parse_ed_prop_class(const char *str OVS_UNUSED,
         *prop_class = OFPPPC_GRE;
     } else if (!strcmp(str,"gtp")) {
         *prop_class = OFPPPC_GTP;
+    } else if (!strcmp(str,"nsh")) {
+        *prop_class = OFPPPC_NSH;
     } else {
         return false;
     }
@@ -90,6 +169,16 @@ parse_ed_prop_type(uint16_t prop_class,
                    uint8_t *type OVS_UNUSED)
 {
     switch (prop_class) {
+    case OFPPPC_NSH:
+        if (!strcmp(str, "md_type")) {
+            *type = OFPPPT_PROP_NSH_MDTYPE;
+            return true;
+        } else if (!strcmp(str, "tlv")) {
+            *type = OFPPPT_PROP_NSH_TLV;
+            return true;
+        } else {
+            return false;
+        }
     default:
         return false;
     }
@@ -106,12 +195,68 @@ char *
 parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type OVS_UNUSED,
                     const char *value, struct ofpbuf *out OVS_UNUSED)
 {
+    char *error = NULL;
 
     if (value == NULL || *value == '\0') {
         return xstrdup("Value missing for encap property");
     }
 
     switch (prop_class) {
+    case OFPPPC_NSH:
+        switch (prop_type) {
+        case OFPPPT_PROP_NSH_MDTYPE: {
+            /* Format: "<md_type>:uint8_t". */
+            uint8_t md_type;
+            error = str_to_u8(value, "md_type", &md_type);
+            if (error != NULL) {
+                return error;
+            }
+            if (md_type < 1 || md_type > 2) {
+                return xstrdup("invalid md_type");
+            }
+            struct ofpact_ed_prop_nsh_md_type *pnmt =
+                    ofpbuf_put_uninit(out, sizeof(*pnmt));
+            pnmt->header.prop_class = prop_class;
+            pnmt->header.type = prop_type;
+            pnmt->header.len =
+                    offsetof(struct ofp_ed_prop_nsh_md_type, pad);
+            pnmt->md_type = md_type;
+            break;
+        }
+        case OFPPPT_PROP_NSH_TLV: {
+            /* Format: "<class>:ovs_be16,<type>:uint8_t,<val>:hex_string" */
+            struct ofpact_ed_prop_nsh_tlv *pnt;
+            uint16_t tlv_class;
+            uint8_t tlv_type;
+            char buf[256];
+            size_t tlv_value_len, padding;
+            size_t start_ofs = out->size;
+
+            if (!ovs_scan(value, "0x%"SCNx16",%"SCNu8",0x%251[0-9a-fA-F]",
+                          &tlv_class, &tlv_type, buf)) {
+                return xasprintf("Invalid NSH TLV header: %s", value);
+            }
+            ofpbuf_put_uninit(out, sizeof(*pnt));
+            ofpbuf_put_hex(out, buf, &tlv_value_len);
+            pnt = ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *,
+                               ((char *)out->data + start_ofs));
+            padding = ROUND_UP(tlv_value_len, 8) - tlv_value_len;
+            pnt->header.prop_class = prop_class;
+            pnt->header.type = prop_type;
+            pnt->header.len = sizeof(*pnt) + tlv_value_len + padding;
+            pnt->tlv_class = htons(tlv_class);
+            pnt->tlv_type = tlv_type;
+            pnt->tlv_len = tlv_value_len;
+            if (padding > 0) {
+                ofpbuf_put_zeros(out, padding);
+            }
+            break;
+        }
+        default:
+            /* Unsupported property types rejected before. */
+            OVS_NOT_REACHED();
+        }
+        break;
     default:
         /* Unsupported property classes rejected before. */
         OVS_NOT_REACHED();
@@ -132,6 +277,8 @@ format_ed_prop_class(const struct ofpact_ed_prop *prop)
         return "gre";
     case OFPPPC_GTP:
         return "gtp";
+    case OFPPPC_NSH:
+        return "nsh";
     default:
         OVS_NOT_REACHED();
     }
@@ -141,6 +288,16 @@ char *
 format_ed_prop_type(const struct ofpact_ed_prop *prop)
 {
     switch (prop->prop_class) {
+    case OFPPPC_NSH:
+        switch (prop->type) {
+        case OFPPPT_PROP_NSH_MDTYPE:
+            return "md_type";
+        case OFPPPT_PROP_NSH_TLV:
+            return "tlv";
+        default:
+            OVS_NOT_REACHED();
+        }
+        break;
     default:
         OVS_NOT_REACHED();
     }
@@ -151,6 +308,28 @@ format_ed_prop(struct ds *s OVS_UNUSED,
                      const struct ofpact_ed_prop *prop)
 {
     switch (prop->prop_class) {
+    case OFPPPC_NSH:
+        switch (prop->type) {
+        case OFPPPT_PROP_NSH_MDTYPE: {
+            struct ofpact_ed_prop_nsh_md_type *pnmt =
+                ALIGNED_CAST(struct ofpact_ed_prop_nsh_md_type *, prop);
+            ds_put_format(s, "%s=%d", format_ed_prop_type(prop),
+                          pnmt->md_type);
+            return;
+        }
+        case OFPPPT_PROP_NSH_TLV: {
+            struct ofpact_ed_prop_nsh_tlv *pnt =
+                ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *, prop);
+            ds_put_format(s, "%s(0x%04x,%d,",
+                          format_ed_prop_type(prop),
+                          ntohs(pnt->tlv_class), pnt->tlv_type);
+            ds_put_hex(s, pnt->data, pnt->tlv_len);
+            ds_put_cstr(s,")");
+            return;
+        }
+        default:
+            OVS_NOT_REACHED();
+        }
     default:
         OVS_NOT_REACHED();
     }
diff --git a/lib/packets.c b/lib/packets.c
index 7a9071c..a304853 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -402,6 +402,88 @@ pop_mpls(struct dp_packet *packet, ovs_be16 ethtype)
     }
 }
 
+void
+encap_nsh(struct dp_packet *packet, const struct ovs_action_encap_nsh *encap)
+{
+    struct nsh_hdr *nsh;
+    size_t length = NSH_BASE_HDR_LEN + encap->mdlen;
+    uint8_t next_proto;
+
+    switch (ntohl(packet->packet_type)) {
+        case PT_ETH:
+            next_proto = NSH_P_ETHERNET;
+            break;
+        case PT_IPV4:
+            next_proto = NSH_P_IPV4;
+            break;
+        case PT_IPV6:
+            next_proto = NSH_P_IPV6;
+            break;
+        case PT_NSH:
+            next_proto = NSH_P_NSH;
+            break;
+        default:
+            OVS_NOT_REACHED();
+    }
+
+    nsh = (struct nsh_hdr *) dp_packet_push_uninit(packet, length);
+    nsh->ver_flags_len = htons(encap->flags << NSH_FLAGS_SHIFT | length >> 2);
+    nsh->next_proto = next_proto;
+    put_16aligned_be32(&nsh->path_hdr, encap->path_hdr);
+    nsh->md_type = encap->mdtype;
+    switch (nsh->md_type) {
+        case NSH_M_TYPE1:
+            nsh->md1 = *ALIGNED_CAST(struct nsh_md1_ctx *, encap->metadata);
+            break;
+        case NSH_M_TYPE2: {
+            /* The MD2 metadata in encap is already padded to 4 bytes. */
+            size_t len = ROUND_UP(encap->mdlen, 4);
+            memcpy(nsh->md2, encap->metadata, len);
+            break;
+        }
+        default:
+            OVS_NOT_REACHED();
+    }
+
+    packet->packet_type = htonl(PT_NSH);
+    dp_packet_reset_offsets(packet);
+    packet->l3_ofs = 0;
+}
+
+bool
+decap_nsh(struct dp_packet *packet)
+{
+    struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet);
+    size_t length;
+    uint32_t next_pt;
+
+    if (packet->packet_type == htonl(PT_NSH) && nsh) {
+        switch (nsh->next_proto) {
+            case NSH_P_ETHERNET:
+                next_pt = PT_ETH;
+                break;
+            case NSH_P_IPV4:
+                next_pt = PT_IPV4;
+                break;
+            case NSH_P_IPV6:
+                next_pt = PT_IPV6;
+                break;
+            case NSH_P_NSH:
+                next_pt = PT_NSH;
+                break;
+            default:
+                /* Unknown inner packet type. Drop packet. */
+                return false;
+        }
+
+        length = nsh_hdr_len(nsh);
+        dp_packet_reset_packet(packet, length);
+        packet->packet_type = htonl(next_pt);
+        /* Packet must be recirculated for further processing. */
+    }
+    return true;
+}
+
 /* Converts hex digits in 'hex' to an Ethernet packet in '*packetp'.  The
  * caller must free '*packetp'.  On success, returns NULL.  On failure, returns
  * an error message and stores NULL in '*packetp'.
diff --git a/lib/packets.h b/lib/packets.h
index 5083e6a..705d0b2 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -434,6 +434,10 @@ void push_eth(struct dp_packet *packet, const struct eth_addr *dst,
               const struct eth_addr *src);
 void pop_eth(struct dp_packet *packet);
 
+void encap_nsh(struct dp_packet *packet,
+               const struct ovs_action_encap_nsh *encap_nsh);
+bool decap_nsh(struct dp_packet *packet);
+
 #define LLC_DSAP_SNAP 0xaa
 #define LLC_SSAP_SNAP 0xaa
 #define LLC_CNTL_SNAP 3
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 605c20a..5549b49 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -2824,6 +2824,8 @@ dpif_ipfix_read_actions(const struct flow *flow,
         case OVS_ACTION_ATTR_POP_MPLS:
         case OVS_ACTION_ATTR_PUSH_ETH:
         case OVS_ACTION_ATTR_POP_ETH:
+        case OVS_ACTION_ATTR_ENCAP_NSH:
+        case OVS_ACTION_ATTR_DECAP_NSH:
         case OVS_ACTION_ATTR_UNSPEC:
         case __OVS_ACTION_ATTR_MAX:
         default:
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index e4cca65..65a2003 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_NSH:
+        case OVS_ACTION_ATTR_DECAP_NSH:
 	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 79decdb..973e760 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -55,6 +55,7 @@
 #include "openvswitch/meta-flow.h"
 #include "openvswitch/list.h"
 #include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofp-ed-props.h"
 #include "openvswitch/vlog.h"
 #include "ovs-lldp.h"
 #include "ovs-router.h"
@@ -234,8 +235,10 @@ 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. */
+    bool pending_encap;         /* True when waiting to commit a pending
+                                 * encap action. */
+    struct ofpbuf *encap_data;  /* May contain a pointer to an ofpbuf with
+                                 * context for the datapath encap action.*/
 
     uint8_t table_id;           /* OpenFlow table ID where flow was found. */
     ovs_be64 rule_cookie;       /* Cookie of the rule being translated. */
@@ -3468,8 +3471,11 @@ 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, ctx->pending_encap);
+                                          use_masked, ctx->pending_encap,
+                                          ctx->encap_data);
     ctx->pending_encap = false;
+    ofpbuf_delete(ctx->encap_data);
+    ctx->encap_data = NULL;
 }
 
 static void
@@ -4386,6 +4392,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_NSH:
+        case OVS_ACTION_ATTR_DECAP_NSH:
         case OVS_ACTION_ATTR_METER:
             ofpbuf_put(b, a, nl_attr_len_pad(a, left));
             break;
@@ -5786,20 +5794,131 @@ rewrite_flow_encap_ethernet(struct xlate_ctx *ctx,
         flow->dl_dst = eth_addr_zero;
         flow->dl_type = ethertype;
     } else {
+        /* Error handling: drop packet. */
         xlate_report_debug(ctx, OFT_ACTION,
-                           "encap(ethernet) unsupported for packet type "
-                           "ethernet");
-        /* TODO: Error handling: drop packet. */
+                           "Dropping packet as encap(ethernet) is not "
+                           "supported for packet type ethernet.");
         ctx->error = 1;
     }
 }
 
+/* For an MD2 NSH header returns a pointer to an ofpbuf with the encoded
+ * MD2 TLVs provided as encap properties to the encap operation. This
+ * will be stored as encap_data in the ctx and copied into the encap_nsh
+ * action at the next commit. */
+static struct ofpbuf *
+rewrite_flow_encap_nsh(struct xlate_ctx *ctx,
+                       const struct ofpact_encap *encap,
+                       struct flow *flow,
+                       struct flow_wildcards *wc)
+{
+    ovs_be32 packet_type = flow->packet_type;
+    const char *ptr = (char *) encap->props;
+    struct ofpbuf *buf = ofpbuf_new(OVS_ENCAP_NSH_MAX_MD_LEN);
+    uint8_t md_type = NSH_M_TYPE1;
+    uint8_t np = 0;
+    int i;
+
+    /* Scan the optional NSH encap TLV properties, if any. */
+    for (i = 0; i < encap->n_props; i++) {
+        struct ofpact_ed_prop *prop_ptr =
+            ALIGNED_CAST(struct ofpact_ed_prop *, ptr);
+        if (prop_ptr->prop_class == OFPPPC_NSH) {
+            switch (prop_ptr->type) {
+                case OFPPPT_PROP_NSH_MDTYPE: {
+                    struct ofpact_ed_prop_nsh_md_type *prop_md_type =
+                        ALIGNED_CAST(struct ofpact_ed_prop_nsh_md_type *,
+                                     prop_ptr);
+                    md_type = prop_md_type->md_type;
+                    break;
+                }
+                case OFPPPT_PROP_NSH_TLV: {
+                    struct ofpact_ed_prop_nsh_tlv *tlv_prop =
+                        ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *,
+                                     prop_ptr);
+                    struct nsh_md2_tlv *md2_ctx =
+                            ofpbuf_put_uninit(buf, sizeof(*md2_ctx));
+                    md2_ctx->md_class = tlv_prop->tlv_class;
+                    md2_ctx->type = tlv_prop->tlv_type;
+                    md2_ctx->length = tlv_prop->tlv_len;
+                    size_t len = ROUND_UP(md2_ctx->length, 4);
+                    size_t padding = len - md2_ctx->length;
+                    ofpbuf_put(buf, tlv_prop->data, md2_ctx->length);
+                    ofpbuf_put_zeros(buf, padding);
+                    break;
+                }
+                default:
+                    /* No other NSH encap properties defined yet. */
+                    break;
+            }
+        }
+        ptr += ROUND_UP(prop_ptr->len, 8);
+    }
+    if (buf->size == 0 || buf->size > OVS_ENCAP_NSH_MAX_MD_LEN) {
+        ofpbuf_delete(buf);
+        buf = NULL;
+    }
+
+    /* Determine the Next Protocol field for NSH header. */
+    switch (ntohl(packet_type)) {
+        case PT_ETH:
+            np = NSH_P_ETHERNET;
+            break;
+        case PT_IPV4:
+            np = NSH_P_IPV4;
+            break;
+        case PT_IPV6:
+            np = NSH_P_IPV6;
+            break;
+        case PT_NSH:
+            np = NSH_P_NSH;
+            break;
+        default:
+            /* Error handling: drop packet. */
+            xlate_report_debug(ctx, OFT_ACTION,
+                               "Dropping packet as encap(nsh) is not "
+                               "supported for packet type (%d,0x%x)",
+                               pt_ns(packet_type), pt_ns_type(packet_type));
+            ctx->error = 1;
+            return buf;
+    }
+    /* Note that we have matched on packet_type! */
+    wc->masks.packet_type = OVS_BE32_MAX;
+
+    /* Reset all current flow packet headers. */
+    memset(&flow->dl_dst, 0,
+           sizeof(struct flow) - offsetof(struct flow, dl_dst));
+
+    /* Populate the flow with the new NSH header. */
+    flow->packet_type = htonl(PT_NSH);
+    flow->dl_type = htons(ETH_TYPE_NSH);
+    flow->nsh.flags = 0;    /* */
+    flow->nsh.np = np;
+    flow->nsh.spi = 0;
+    flow->nsh.si = 255;
+
+    if (md_type == NSH_M_TYPE1) {
+        flow->nsh.mdtype = NSH_M_TYPE1;
+        memset(flow->nsh.c, 0, sizeof flow->nsh.c);
+        if (buf) {
+            /* Drop any MD2 context TLVs. */
+            ofpbuf_delete(buf);
+            buf = NULL;
+        }
+    } else if (md_type == NSH_M_TYPE2) {
+        flow->nsh.mdtype = NSH_M_TYPE2;
+    }
+
+    return buf;
+}
+
 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;
+    struct ofpbuf *encap_data = NULL;
 
     /* Ensure that any pending actions on the inner packet are applied before
      * rewriting the flow */
@@ -5810,15 +5929,19 @@ xlate_generic_encap_action(struct xlate_ctx *ctx,
         case PT_ETH:
             rewrite_flow_encap_ethernet(ctx, flow, wc);
             break;
+        case PT_NSH:
+            encap_data = rewrite_flow_encap_nsh(ctx, encap, flow, wc);
+            break;
         default:
-            /* TODO: Error handling: Should not happen if the PT is checked
-             * at decoding */
+            /* New packet type was checked during decoding. */
+            OVS_NOT_REACHED();
             break;
     }
 
     if (!ctx->error) {
         /* The actual encap datapath action will be generated at next commit. */
         ctx->pending_encap = true;
+        ctx->encap_data = encap_data;
     }
 }
 
@@ -5849,11 +5972,42 @@ xlate_generic_decap_action(struct xlate_ctx *ctx,
                 ctx->wc->masks.dl_type = OVS_BE16_MAX;
             }
             return false;
+        case PT_NSH:
+            /* The decap_nsh action is generated at the commit executed as
+             * part of freezing the ctx for recirculation. Here we just set
+             * the new packet type based on the NSH next protocol field. */
+            switch (flow->nsh.np) {
+            case NSH_P_ETHERNET:
+                flow->packet_type = htonl(PT_ETH);
+                break;
+            case NSH_P_IPV4:
+                flow->packet_type = htonl(PT_IPV4);
+                break;
+            case NSH_P_IPV6:
+                flow->packet_type = htonl(PT_IPV6);
+                break;
+            case NSH_P_NSH:
+                flow->packet_type = htonl(PT_NSH);
+                break;
+            default:
+                /* Error handling: drop packet. */
+                xlate_report_debug(ctx, OFT_ACTION,
+                                   "Dropping packet as NSH next protocol %d "
+                                   "is not supported", flow->nsh.np);
+                ctx->error = 1;
+                return false;
+                break;
+            }
+            ctx->wc->masks.nsh.np = UINT8_MAX;
+            /* Trigger recirculation. */
+            return true;
         default:
-            xlate_report_debug(ctx, OFT_ACTION,
-                               "decap() for unsupported packet type %x",
-                               ntohl(flow->packet_type));
-            /* TODO: Error handling: drop packet. */
+            /* Error handling: drop packet. */
+            xlate_report_debug(
+                    ctx, OFT_ACTION,
+                    "Dropping packet as the decap() does not support "
+                    "packet type (%d,0x%x)",
+                    pt_ns(flow->packet_type), pt_ns_type(flow->packet_type));
             ctx->error = 1;
             return false;
     }
@@ -6699,6 +6853,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         .in_action_set = false,
         .in_packet_out = xin->in_packet_out,
         .pending_encap = false,
+        .encap_data = NULL,
 
         .table_id = 0,
         .rule_cookie = OVS_BE64_MAX,
@@ -7046,6 +7201,7 @@ exit:
     ofpbuf_uninit(&ctx.action_set);
     ofpbuf_uninit(&ctx.frozen_actions);
     ofpbuf_uninit(&scratch_actions);
+    ofpbuf_delete(ctx.encap_data);
 
     /* Make sure we return a "drop flow" in case of an error. */
     if (ctx.error) {
