diff --git a/datapath/flow.h b/datapath/flow.h
index 2433436..78774a1 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -39,9 +39,18 @@ struct sk_buff;
 #define OVS_TUNNEL_KEY_SIZE					\
 	(offsetof(struct ovs_key_ipv4_tunnel, tp_dst) +		\
 	 FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, tp_dst))
+/* Used for masking nsp and nsi values in field nsp below */
+#define NSH_M_NSP   0xFFFFFF00
+#define NSH_M_NSI   0x000000FF
+
 
 struct ovs_key_ipv4_tunnel {
 	__be64 tun_id;
+    __be32 nsp;      /* it contains (nsp - 24 bits | nsi - 8 bits) here */
+    __be32 nshc1;    /* NSH context headers */
+    __be32 nshc2;
+    __be32 nshc3;
+    __be32 nshc4;
 	__be32 ipv4_src;
 	__be32 ipv4_dst;
 	__be16 tun_flags;
@@ -72,11 +81,21 @@ static inline void __ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
 					    __be16 tp_src,
 					    __be16 tp_dst,
 					    __be64 tun_id,
+                        __be32 nsp,
+                        __be32 nshc1,
+                        __be32 nshc2,
+                        __be32 nshc3,
+                        __be32 nshc4,
 					    __be16 tun_flags,
 					    const void *opts,
 					    u8 opts_len)
 {
 	tun_info->tunnel.tun_id = tun_id;
+    tun_info->tunnel.nsp = nsp;
+    tun_info->tunnel.nshc1 = nshc1;
+    tun_info->tunnel.nshc2 = nshc2;
+    tun_info->tunnel.nshc3 = nshc3;
+    tun_info->tunnel.nshc4 = nshc4;
 	tun_info->tunnel.ipv4_src = saddr;
 	tun_info->tunnel.ipv4_dst = daddr;
 	tun_info->tunnel.ipv4_tos = tos;
@@ -104,6 +123,11 @@ static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
 					  __be16 tp_src,
 					  __be16 tp_dst,
 					  __be64 tun_id,
+                      __be32 nsp,
+                      __be32 nshc1,
+                      __be32 nshc2,
+                      __be32 nshc3,
+                      __be32 nshc4,
 					  __be16 tun_flags,
 					  const void *opts,
 					  u8 opts_len)
@@ -111,8 +135,10 @@ static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
 	__ovs_flow_tun_info_init(tun_info, iph->saddr, iph->daddr,
 				 iph->tos, iph->ttl,
 				 tp_src, tp_dst,
-				 tun_id, tun_flags,
-				 opts, opts_len);
+                 tun_id, nsp,
+                 nshc1, nshc2,
+                 nshc3, nshc4,
+                 tun_flags,opts, opts_len);
 }
 
 #define OVS_SW_FLOW_KEY_METADATA_SIZE			\
diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index d835a00..c979156 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -51,6 +51,8 @@
 #include "flow_netlink.h"
 #include "vport-vxlan.h"
 
+#define NSH_M_NSI 0x000000FF
+
 struct ovs_len_tbl {
 	int len;
 	const struct ovs_len_tbl *next;
@@ -269,6 +271,12 @@ size_t ovs_tun_key_attr_size(void)
 		+ nla_total_size(0)    /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
 		+ nla_total_size(0)    /* OVS_TUNNEL_KEY_ATTR_CSUM */
 		+ nla_total_size(0)    /* OVS_TUNNEL_KEY_ATTR_OAM */
+        + nla_total_size(4)    /* OVS_TUNNEL_KEY_ATTR_NSP */
+        + nla_total_size(1)    /* OVS_TUNNEL_KEY_ATTR_NSI */
+        + nla_total_size(4)    /* OVS_TUNNEL_KEY_ATTR_NC1 */
+        + nla_total_size(4)    /* OVS_TUNNEL_KEY_ATTR_NC2 */
+        + nla_total_size(4)    /* OVS_TUNNEL_KEY_ATTR_NC3 */
+        + nla_total_size(4)    /* OVS_TUNNEL_KEY_ATTR_NC4 */
 		+ nla_total_size(256)  /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
 		/* OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS is mutually exclusive with
 		 * OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS and covered by it.
@@ -316,6 +324,12 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
 	[OVS_TUNNEL_KEY_ATTR_TP_SRC]	    = { .len = sizeof(u16) },
 	[OVS_TUNNEL_KEY_ATTR_TP_DST]	    = { .len = sizeof(u16) },
 	[OVS_TUNNEL_KEY_ATTR_OAM]	    = { .len = 0 },
+    [OVS_TUNNEL_KEY_ATTR_NSP] = sizeof(u32),
+    [OVS_TUNNEL_KEY_ATTR_NSI] = 1,
+    [OVS_TUNNEL_KEY_ATTR_NSH_C1] = sizeof(u32),
+    [OVS_TUNNEL_KEY_ATTR_NSH_C2] = sizeof(u32),
+    [OVS_TUNNEL_KEY_ATTR_NSH_C3] = sizeof(u32),
+    [OVS_TUNNEL_KEY_ATTR_NSH_C4] = sizeof(u32),
 	[OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS]   = { .len = OVS_ATTR_VARIABLE },
 	[OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS]    = { .len = OVS_ATTR_NESTED,
 						.next = ovs_vxlan_ext_key_lens },
@@ -543,6 +557,12 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
 	bool ttl = false;
 	__be16 tun_flags = 0;
 	int opts_type = 0;
+    __be32 nsp = 0;
+    __be32 nshc1 = 0;
+    __be32 nshc2 = 0;
+    __be32 nshc3 = 0;
+    __be32 nshc4 = 0;
+
 
 	nla_for_each_nested(a, attr, rem) {
 		int type = nla_type(a);
@@ -601,6 +621,30 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
 		case OVS_TUNNEL_KEY_ATTR_OAM:
 			tun_flags |= TUNNEL_OAM;
 			break;
+        case OVS_TUNNEL_KEY_ATTR_NSP:
+            nsp |= htonl(be32_to_cpu(nla_get_be32(a)) << 8);
+            tun_flags |= TUNNEL_NSP;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSI:
+            nsp |= htonl(nla_get_u8(a));
+            tun_flags |= TUNNEL_NSI;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C1:
+            nshc1 = nla_get_be32(a);
+            tun_flags |= TUNNEL_NSHC;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C2:
+            nshc2 = nla_get_be32(a);
+            tun_flags |= TUNNEL_NSHC;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C3:
+            nshc3 = nla_get_be32(a);
+            tun_flags |= TUNNEL_NSHC;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C4:
+            nshc4 = nla_get_be32(a);
+            tun_flags |= TUNNEL_NSHC;
+            break;
 		case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
 			if (opts_type) {
 				OVS_NLERR(log, "Multiple metadata blocks provided");
@@ -634,6 +678,11 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
 		}
 	}
 
+    SW_FLOW_KEY_PUT(match, tun_key.nsp, nsp, is_mask);
+    SW_FLOW_KEY_PUT(match, tun_key.nshc1, nshc1, is_mask);
+    SW_FLOW_KEY_PUT(match, tun_key.nshc2, nshc2, is_mask);
+    SW_FLOW_KEY_PUT(match, tun_key.nshc3, nshc3, is_mask);
+    SW_FLOW_KEY_PUT(match, tun_key.nshc4, nshc4, is_mask);
 	SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask);
 
 	if (rem > 0) {
@@ -678,6 +727,9 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
 				const struct ovs_key_ipv4_tunnel *output,
 				const void *tun_opts, int swkey_tun_opts_len)
 {
+    __be32 nsp = cpu_to_be32(ntohl(output->nsp) >> 8);
+    u8 nsi = ntohl(output->nsp) & NSH_M_NSI;
+
 	if (output->tun_flags & TUNNEL_KEY &&
 	    nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
 		return -EMSGSIZE;
@@ -707,6 +759,24 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
 	if ((output->tun_flags & TUNNEL_OAM) &&
 	    nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_OAM))
 		return -EMSGSIZE;
+    if (output->tun_flags & TUNNEL_NSP &&
+        nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_NSP, nsp))
+        return -EMSGSIZE;
+    if (output->tun_flags & TUNNEL_NSI &&
+        nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_NSI, nsi))
+        return -EMSGSIZE;
+    if (output->tun_flags & TUNNEL_NSHC &&
+        nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_NSH_C1, output->nshc1))
+        return -EMSGSIZE;
+    if (output->tun_flags & TUNNEL_NSHC &&
+        nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_NSH_C2, output->nshc2))
+        return -EMSGSIZE;
+    if (output->tun_flags & TUNNEL_NSHC &&
+        nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_NSH_C3, output->nshc3))
+        return -EMSGSIZE;
+    if (output->tun_flags & TUNNEL_NSHC &&
+        nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_NSH_C4, output->nshc4))
+        return -EMSGSIZE;
 	if (tun_opts) {
 		if (output->tun_flags & TUNNEL_GENEVE_OPT &&
 		    nla_put(skb, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,
diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk
index 96c3d55..98e95b2 100644
--- a/datapath/linux/Modules.mk
+++ b/datapath/linux/Modules.mk
@@ -79,5 +79,6 @@ openvswitch_headers += \
 	linux/compat/include/net/sock.h \
 	linux/compat/include/net/stt.h \
 	linux/compat/include/net/vxlan.h \
+    linux/compat/include/net/nsh.h \
 	linux/compat/include/net/sctp/checksum.h
 EXTRA_DIST += linux/compat/build-aux/export-check-whitelist
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 578cd88..aa5dfde 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -366,6 +366,12 @@ enum ovs_tunnel_key_attr {
 	OVS_TUNNEL_KEY_ATTR_TP_SRC,		/* be16 src Transport Port. */
 	OVS_TUNNEL_KEY_ATTR_TP_DST,		/* be16 dst Transport Port. */
 	OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS,		/* Nested OVS_VXLAN_EXT_* */
+    OVS_TUNNEL_KEY_ATTR_NSP,        /* be32 NSH svc path (lower 24 bits) */
+    OVS_TUNNEL_KEY_ATTR_NSI,        /* u8 NSH service index*/
+    OVS_TUNNEL_KEY_ATTR_NSH_C1,    /* be32 nshc1 */
+    OVS_TUNNEL_KEY_ATTR_NSH_C2,    /* be32 nshc2 */
+    OVS_TUNNEL_KEY_ATTR_NSH_C3,    /* be32 nshc3 */
+    OVS_TUNNEL_KEY_ATTR_NSH_C4,    /* be32 nshc4 */
 	__OVS_TUNNEL_KEY_ATTR_MAX
 };
 
diff --git a/datapath/linux/compat/include/net/ip_tunnels.h b/datapath/linux/compat/include/net/ip_tunnels.h
index 3ed6f91..f091209 100644
--- a/datapath/linux/compat/include/net/ip_tunnels.h
+++ b/datapath/linux/compat/include/net/ip_tunnels.h
@@ -80,6 +80,14 @@ struct tnl_ptk_info {
 #undef TUNNEL_OPTIONS_PRESENT
 #define TUNNEL_OPTIONS_PRESENT (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT)
 
+#ifndef TUNNEL_NSP
+#define TUNNEL_NSP    __cpu_to_be16(0x2000)
+#define TUNNEL_NSI    __cpu_to_be16(0x4000)
+#endif
+#ifndef TUNNEL_NSHC
+#define TUNNEL_NSHC    __cpu_to_be16(0x8000)
+#endif
+
 #define skb_is_encapsulated ovs_skb_is_encapsulated
 bool ovs_skb_is_encapsulated(struct sk_buff *skb);
 
diff --git a/datapath/linux/compat/include/net/nsh.h b/datapath/linux/compat/include/net/nsh.h
new file mode 100644
index 0000000..a143a83
--- /dev/null
+++ b/datapath/linux/compat/include/net/nsh.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2013, 2014 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef NSH_H
+#define NSH_H 1
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+
+/**
+ * struct nsh_bhdr - Network Service Base Header.
+ * @o: Operations and Management Packet indicator bit
+ * @c: If this bit is set then one or more contexts are in use.
+ * @proto: IEEE Ethertypes to indicate the frame within.
+ * @svc_idx: TTL functionality and location within service path.
+ * @svc_path: To uniquely identify service path.
+ */
+struct nsh_base {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+    __u8    res1:4;
+    __u8    c:1;
+    __u8    o:1;
+    __u8    ver:2;
+
+    __u8    len:6;
+    __u8    res2:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+    __u8    ver:2;
+    __u8    o:1;
+    __u8    c:1;
+    __u8    res1:4;
+
+    __u8    res2:2;
+    __u8    len:6;
+#else
+#error "Bitfield Endianess not defined."
+#endif
+    __u8    mdtype;
+    __u8    proto;
+    union {
+        struct {
+            __u8    svc_path[3];
+            __u8    svc_idx;
+        };
+        __be32 b2;
+    };
+};
+
+/**
+ * struct nsh_ctx - Keeps track of NSH context data
+ * @c<1-4>: NSH Contexts.
+ */
+struct nsh_ctx {
+    __be32 c1;
+    __be32 c2;
+    __be32 c3;
+    __be32 c4;
+};
+
+/**
+ * struct nshdr - Network Service header
+ * @nsh_base: Network Service Base Header.
+ * @nsh_ctx: Network Service Context Header.
+ */
+struct nshhdr {
+    struct nsh_base b;
+    struct nsh_ctx c;
+};
+
+
+#define ETH_P_NSH        0x894F   /* Ethertype for NSH */
+
+/* NSH Base Header Next Protocol */
+#define NSH_P_IPV4        0x01
+#define NSH_P_IPV6        0x02
+#define NSH_P_ETHERNET    0x03
+
+/* MD Type Registry */
+#define NSH_M_TYPE1     0x01
+#define NSH_M_TYPE2     0x02
+#define NSH_M_EXP1      0xFE
+#define NSH_M_EXP2      0xFF
+
+#define NSH_DST_PORT    4790   /* UDP Port for NSH on VXLAN */
+
+
+#endif /* nsh.h */
diff --git a/datapath/linux/compat/include/net/vxlan.h b/datapath/linux/compat/include/net/vxlan.h
index cafff79..ff63260 100644
--- a/datapath/linux/compat/include/net/vxlan.h
+++ b/datapath/linux/compat/include/net/vxlan.h
@@ -4,8 +4,10 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/udp.h>
+#include <net/nsh.h>
 #include <net/gre.h>
 
+
 #include <linux/version.h>
 
 #ifdef HAVE_VXLAN_METADATA
@@ -17,6 +19,14 @@
 #ifndef VXLAN_HLEN
 /* VXLAN header flags. */
 #define VXLAN_HF_VNI 0x08000000
+/* VXLAN-GPE header flags. */
+#define VXLAN_GPE_HF_P 0x04000000
+#define VXLAN_GPE_HF_O 0x01000000
+#define VXLAN_GPE_HF_VER 0x00C00000
+#define VXLAN_GPE_HF_NP 0x0000000F
+
+#define VXLAN_GPE_NP_IS_NSH 4
+
 #ifndef VXLAN_HF_GBP
 #define VXLAN_HF_GBP 0x80000000
 #endif
@@ -24,6 +34,10 @@
 #define VXLAN_N_VID     (1u << 24)
 #define VXLAN_VID_MASK  (VXLAN_N_VID - 1)
 #define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr))
+#define NSH_HLEN (sizeof(struct udphdr) + \
+          sizeof(struct vxlanhdr) + \
+          sizeof(struct nshhdr))
+
 #endif
 
 #ifndef VXLAN_GBP_USED_BITS
@@ -119,7 +133,8 @@ struct rpl_vxlan_sock;
 
 #define vxlan_rcv_t rpl_vxlan_rcv_t
 typedef void (vxlan_rcv_t)(struct vxlan_sock *vh, struct sk_buff *skb,
-			   struct vxlan_metadata *md);
+               struct vxlan_metadata *md, __be32 nsp, __be32 nshc1, __be32 nshc2,
+               __be32 nshc3, __be32 nshc4);
 
 /* per UDP socket information */
 struct vxlan_sock {
@@ -144,7 +159,8 @@ void rpl_vxlan_sock_release(struct vxlan_sock *vs);
 int rpl_vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
 		       __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
 		       __be16 src_port, __be16 dst_port,
-		       struct vxlan_metadata *md, bool xnet, u32 vxflags);
+               struct vxlan_metadata *md, bool xnet, u32 vxflags,
+               __be32 nsp, __be32 nshc1, __be32 nshc2, __be32 nshc3, __be32 nshc4);
 
 #endif /* !HAVE_VXLAN_METADATA */
 #endif
diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c
index fd454ae..41e202b 100644
--- a/datapath/linux/compat/vxlan.c
+++ b/datapath/linux/compat/vxlan.c
@@ -54,6 +54,7 @@
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 #include <net/vxlan.h>
+#include <net/nsh.h>
 
 #include "compat.h"
 #include "datapath.h"
@@ -68,6 +69,16 @@ struct vxlanhdr {
 	__be32 vx_vni;
 };
 
+static inline struct vxlanhdr *vxlan_hdr(const struct sk_buff *skb)
+{
+    return (struct vxlanhdr *)(udp_hdr(skb) + 1);
+}
+
+static inline struct nshhdr *nsh_hdr(const struct sk_buff *skb)
+{
+    return (struct nshhdr *)(vxlan_hdr(skb) + 1);
+}
+
 /* Callback from net/ipv4/udp.c to receive packets */
 static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 {
@@ -75,23 +86,62 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	struct vxlanhdr *vxh;
 	u32 flags, vni;
 	struct vxlan_metadata md = {0};
+    struct udphdr *udp;
+    bool isnsh = false;
+    __be32 nsp = 0;
+    __be32 c1 = 0;
+    __be32 c2 = 0;
+    __be32 c3 = 0;
+    __be32 c4 = 0;
+
+    udp = (struct udphdr *)udp_hdr(skb);
+    if (udp->dest == htons(NSH_DST_PORT))
+        isnsh = true;
 
 	/* Need Vxlan and inner Ethernet header to be present */
-	if (!pskb_may_pull(skb, VXLAN_HLEN))
+    if (!pskb_may_pull(skb, isnsh ? NSH_HLEN : VXLAN_HLEN))
 		goto error;
 
-	vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1);
+    vxh = vxlan_hdr(skb);
 	flags = ntohl(vxh->vx_flags);
 	vni = ntohl(vxh->vx_vni);
 
-	if (flags & VXLAN_HF_VNI) {
-		flags &= ~VXLAN_HF_VNI;
-	} else {
+    if (isnsh) {
+        if((flags & VXLAN_GPE_HF_P) && ((flags & VXLAN_GPE_HF_NP) == VXLAN_GPE_NP_IS_NSH)){
+            flags &= ~(VXLAN_HF_VNI | VXLAN_GPE_HF_P | VXLAN_GPE_HF_O | VXLAN_GPE_HF_VER | VXLAN_GPE_HF_NP);
+        }
+        else {
+            /* need to set vxlan-gpe header flag for nsh correctly */
+            goto bad_flags;
+        }
+    }
+    else {
+	    if (flags & VXLAN_HF_VNI) {
+		    flags &= ~VXLAN_HF_VNI;
+        }
+        else {
 		/* VNI flag always required to be set */
 		goto bad_flags;
-	}
-
-	if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB)))
+	    }
+    }
+
+    if (isnsh) {
+        struct nshhdr *nsh = nsh_hdr(skb);
+        if (unlikely(nsh->b.svc_idx == 0 || nsh->b.ver ||
+                  nsh->b.len != 6 || nsh->b.mdtype != 0x01 ||
+                  nsh->b.proto != NSH_P_ETHERNET)) {
+            pr_warn("NSH service index reached zero or not supported\n");
+            goto drop;
+        }
+
+        nsp = nsh->b.b2; /* same as svc_path | htonl(svc_idx) */
+         c1 = nsh->c.c1;  /* NSH Contexts */
+         c2 = nsh->c.c2;
+         c3 = nsh->c.c3;
+         c4 = nsh->c.c4;
+    }
+
+    if (iptunnel_pull_header(skb, isnsh ? NSH_HLEN : VXLAN_HLEN, htons(ETH_P_TEB)))
 		goto drop;
 
 	vs = rcu_dereference_sk_user_data(sk);
@@ -101,7 +151,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	/* For backwards compatibility, only allow reserved fields to be
 	* used by VXLAN extensions if explicitly requested.
 	*/
-	if ((flags & VXLAN_HF_GBP) && (vs->flags & VXLAN_F_GBP)) {
+    if (!isnsh && (flags & VXLAN_HF_GBP) && (vs->flags & VXLAN_F_GBP)) {
 		struct vxlanhdr_gbp *gbp;
 
 		gbp = (struct vxlanhdr_gbp *)vxh;
@@ -130,7 +180,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	}
 
 	md.vni = vxh->vx_vni;
-	vs->rcv(vs, skb, &md);
+    vs->rcv(vs, skb, &md,nsp, c1, c2, c3, c4);
 	return 0;
 
 drop:
@@ -183,15 +233,17 @@ static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags,
 int rpl_vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
 		       __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
 		       __be16 src_port, __be16 dst_port,
-		       struct vxlan_metadata *md, bool xnet, u32 vxflags)
+               struct vxlan_metadata *md, bool xnet, u32 vxflags,
+               __be32 nsp, __be32 nshc1, __be32 nshc2, __be32 nshc3, __be32 nshc4)
 {
+    bool isnsh = (dst_port == htons(NSH_DST_PORT));
 	struct vxlanhdr *vxh;
 	int min_headroom;
 	int err;
 	bool udp_sum = !!(vxflags & VXLAN_F_UDP_CSUM);
 
 	min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len
-			+ VXLAN_HLEN + sizeof(struct iphdr)
+            + (isnsh ? NSH_HLEN : VXLAN_HLEN) + sizeof(struct iphdr)
 			+ (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0);
 
 	/* Need space for new headers (invalidates iph ptr) */
@@ -208,9 +260,28 @@ int rpl_vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
 	skb = udp_tunnel_handle_offloads(skb, udp_sum, true);
 	if (IS_ERR(skb))
 		return PTR_ERR(skb);
+    if (isnsh) {
+        struct nshhdr *nsh;
+        uint8_t nsi = ntohl(nsp) & NSH_M_NSI;
+        nsh = (struct nshhdr *) __skb_push(skb, sizeof(*nsh));
+               memset(&nsh->b, 0, sizeof nsh->b);
+        nsh->b.len = 6;
+        nsh->b.mdtype = NSH_M_TYPE1;
+        nsh->b.proto = NSH_P_ETHERNET;
+        /* b2 should precede svc_idx, else svc_idx will be zero */
+        nsh->b.b2 = nsp & htonl(NSH_M_NSP);
+        nsh->b.svc_idx = nsi ? nsi : 0x01;
+        nsh->c.c1 = nshc1;
+        nsh->c.c2 = nshc2;
+        nsh->c.c3 = nshc3;
+        nsh->c.c4 = nshc4;
+    }
 
 	vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh));
-	vxh->vx_flags = htonl(VXLAN_HF_VNI);
+    if(isnsh)
+        vxh->vx_flags = htonl(VXLAN_GPE_HF_P | (VXLAN_GPE_HF_NP & VXLAN_GPE_NP_IS_NSH)); //set vxlan_gpe nsh header flag
+    else
+	    vxh->vx_flags = htonl(VXLAN_HF_VNI);
 	vxh->vx_vni = md->vni;
 
 	if (vxflags & VXLAN_F_GBP)
diff --git a/datapath/vport-geneve.c b/datapath/vport-geneve.c
index 4ab224d..7ff24c5 100644
--- a/datapath/vport-geneve.c
+++ b/datapath/vport-geneve.c
@@ -92,7 +92,7 @@ static void geneve_rcv(struct geneve_sock *gs, struct sk_buff *skb)
 
 	ovs_flow_tun_info_init(&tun_info, ip_hdr(skb),
 			       udp_hdr(skb)->source, udp_hdr(skb)->dest,
-			       key, flags,
+                   key, 0, 0, 0, 0, 0, flags,
 			       geneveh->options, opts_len);
 
 	ovs_vport_receive(vport, skb, &tun_info);
diff --git a/datapath/vport-gre.c b/datapath/vport-gre.c
index 0328fe5..2fd4ea6 100644
--- a/datapath/vport-gre.c
+++ b/datapath/vport-gre.c
@@ -111,7 +111,7 @@ static int gre_rcv(struct sk_buff *skb,
 		return PACKET_REJECT;
 
 	key = key_to_tunnel_id(tpi->key, tpi->seq);
-	ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), 0, 0, key,
+    ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), 0, 0, key, 0, 0, 0, 0, 0,
 			       filter_tnl_flags(tpi->flags), NULL, 0);
 
 	ovs_vport_receive(vport, skb, &tun_info);
diff --git a/datapath/vport-lisp.c b/datapath/vport-lisp.c
index 104a21d..1480ae7 100644
--- a/datapath/vport-lisp.c
+++ b/datapath/vport-lisp.c
@@ -249,7 +249,7 @@ static int lisp_rcv(struct sock *sk, struct sk_buff *skb)
 	iph = ip_hdr(skb);
 	ovs_flow_tun_info_init(&tun_info, iph,
 			       udp_hdr(skb)->source, udp_hdr(skb)->dest,
-			       key, TUNNEL_KEY, NULL, 0);
+                   key, 0, 0, 0, 0, 0, TUNNEL_KEY, NULL, 0);
 
 	/* Drop non-IP inner packets */
 	inner_iph = (struct iphdr *)(lisph + 1);
diff --git a/datapath/vport-stt.c b/datapath/vport-stt.c
index 4eb0282..e9e48e6 100644
--- a/datapath/vport-stt.c
+++ b/datapath/vport-stt.c
@@ -53,7 +53,7 @@ static void stt_rcv(struct stt_sock *stt_sock, struct sk_buff *skb)
 
 	ovs_flow_tun_info_init(&tun_info, ip_hdr(skb),
 			       tcp_hdr(skb)->source, tcp_hdr(skb)->dest,
-			       get_unaligned(&stth->key),
+                   get_unaligned(&stth->key), 0, 0, 0, 0, 0,
 			       TUNNEL_KEY | TUNNEL_CSUM,
 			       NULL, 0);
 	do {
diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
index fc9f350..9f93c52 100644
--- a/datapath/vport-vxlan.c
+++ b/datapath/vport-vxlan.c
@@ -63,7 +63,8 @@ static inline struct vxlan_port *vxlan_vport(const struct vport *vport)
 }
 
 static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
-		      struct vxlan_metadata *md)
+              struct vxlan_metadata *md, __be32 nsp, __be32 nshc1, __be32 nshc2,
+              __be32 nshc3, __be32 nshc4)
 {
 	struct ovs_tunnel_info tun_info;
 	struct vxlan_port *vxlan_port;
@@ -75,7 +76,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
 	__be64 key;
 	__be16 flags;
 
-	flags = TUNNEL_KEY | (udp_hdr(skb)->check != 0 ? TUNNEL_CSUM : 0);
+    flags = TUNNEL_KEY |TUNNEL_NSP |TUNNEL_NSI |TUNNEL_NSHC | (udp_hdr(skb)->check != 0 ? TUNNEL_CSUM : 0);
 	vxlan_port = vxlan_vport(vport);
 	if (vxlan_port->exts & VXLAN_F_GBP && md->gbp)
 		flags |= TUNNEL_VXLAN_OPT;
@@ -85,7 +86,8 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
 	key = cpu_to_be64(ntohl(md->vni) >> 8);
 	ovs_flow_tun_info_init(&tun_info, iph,
 			       udp_hdr(skb)->source, udp_hdr(skb)->dest,
-			       key, flags, &opts, sizeof(opts));
+                   key, nsp, nshc1, nshc2, nshc3, nshc4,
+                   flags, &opts, sizeof(opts));
 
 	ovs_vport_receive(vport, skb, &tun_info);
 }
@@ -265,7 +267,12 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
 			     tun_key->ipv4_tos,
 			     tun_key->ipv4_ttl, df,
 			     src_port, dst_port,
-			     &md, false, vxflags);
+                 &md, false, vxflags,
+                 tun_key->nsp,
+                 tun_key->nshc1,
+                 tun_key->nshc2,
+                 tun_key->nshc3,
+                 tun_key->nshc4);
 	if (err < 0)
 		ip_rt_put(rt);
 	return err;
diff --git a/datapath/vport.c b/datapath/vport.c
index 024491f..9e2bd9c 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -624,6 +624,11 @@ int ovs_tunnel_get_egress_info(struct ovs_tunnel_info *egress_tun_info,
 				 tun_key->ipv4_ttl,
 				 tp_src, tp_dst,
 				 tun_key->tun_id,
+                 tun_key->nsp,
+                 tun_key->nshc1,
+                 tun_key->nshc2,
+                 tun_key->nshc3,
+                 tun_key->nshc4,
 				 tun_key->tun_flags,
 				 tun_info->options,
 				 tun_info->options_len);
diff --git a/lib/flow.c b/lib/flow.c
index 84048e8..2cbfb6d 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -854,6 +854,18 @@ flow_tun_flag_to_string(uint32_t flags)
         return "key";
     case FLOW_TNL_F_OAM:
         return "oam";
+    case FLOW_TNL_F_NSP:
+        return "nsp";
+    case FLOW_TNL_F_NSI:
+        return "nsi";
+    case FLOW_TNL_F_NSH_C1:
+        return "nshc1";
+    case FLOW_TNL_F_NSH_C2:
+        return "nshc2";
+    case FLOW_TNL_F_NSH_C3:
+        return "nshc3";
+    case FLOW_TNL_F_NSH_C4:
+        return "nshc4";
     default:
         return NULL;
     }
@@ -1152,6 +1164,24 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
         if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
             WC_MASK_FIELD(wc, tunnel.tun_id);
         }
+        if (flow->tunnel.flags & FLOW_TNL_F_NSP) {
+            WC_MASK_FIELD(wc, tunnel.nsp);
+        }
+        if (flow->tunnel.flags & FLOW_TNL_F_NSI) {
+            WC_MASK_FIELD(wc, tunnel.nsi);
+        }
+        if (flow->tunnel.flags & FLOW_TNL_F_NSH_C1) {
+            WC_MASK_FIELD(wc, tunnel.nshc1);
+        }
+        if (flow->tunnel.flags & FLOW_TNL_F_NSH_C2) {
+            WC_MASK_FIELD(wc, tunnel.nshc2);
+        }
+        if (flow->tunnel.flags & FLOW_TNL_F_NSH_C3) {
+            WC_MASK_FIELD(wc, tunnel.nshc3);
+        }
+        if (flow->tunnel.flags & FLOW_TNL_F_NSH_C4) {
+            WC_MASK_FIELD(wc, tunnel.nshc4);
+        }
         WC_MASK_FIELD(wc, tunnel.ip_src);
         WC_MASK_FIELD(wc, tunnel.ip_dst);
         WC_MASK_FIELD(wc, tunnel.flags);
@@ -1177,6 +1207,24 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
         WC_MASK_FIELD(wc, tunnel.tun_id);
     }
 
+    if (flow->tunnel.nsp) {
+        WC_MASK_FIELD(wc, tunnel.nsp);
+    }
+    if (flow->tunnel.nsi) {
+        WC_MASK_FIELD(wc, tunnel.nsi);
+    }
+    if (flow->tunnel.nshc1) {
+        WC_MASK_FIELD(wc, tunnel.nshc1);
+    }
+    if (flow->tunnel.nshc2) {
+        WC_MASK_FIELD(wc, tunnel.nshc2);
+    }
+    if (flow->tunnel.nshc3) {
+        WC_MASK_FIELD(wc, tunnel.nshc3);
+    }
+    if (flow->tunnel.nshc4) {
+        WC_MASK_FIELD(wc, tunnel.nshc4);
+    }
     /* metadata, regs, and conj_id wildcarded. */
 
     WC_MASK_FIELD(wc, skb_priority);
diff --git a/lib/match.c b/lib/match.c
index 9e465d8..7f7bd4d 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -736,6 +736,86 @@ match_set_nd_target_masked(struct match *match,
     match->wc.masks.nd_target = *mask;
 }
 
+void
+match_set_nsp_masked(struct match *match, ovs_be32 nsp, ovs_be32 mask)
+{
+    match->wc.masks.tunnel.nsp = mask;
+    match->flow.tunnel.nsp = nsp & mask;
+}
+
+void
+match_set_nsi_masked(struct match *match, uint8_t nsi, uint8_t mask)
+{
+    match->wc.masks.tunnel.nsi = mask;
+    match->flow.tunnel.nsi = nsi & mask;
+}
+
+void
+match_set_nshc1_masked(struct match *match, ovs_be32 nshc1, ovs_be32 mask)
+{
+    match->wc.masks.tunnel.nshc1 = mask;
+    match->flow.tunnel.nshc1 = nshc1 & mask;
+}
+
+void
+match_set_nshc2_masked(struct match *match, ovs_be32 nshc2, ovs_be32 mask)
+{
+    match->wc.masks.tunnel.nshc2 = mask;
+    match->flow.tunnel.nshc2 = nshc2 & mask;
+}
+
+void
+match_set_nshc3_masked(struct match *match, ovs_be32 nshc3, ovs_be32 mask)
+{
+    match->wc.masks.tunnel.nshc3 = mask;
+    match->flow.tunnel.nshc3 = nshc3 & mask;
+}
+
+void
+match_set_nshc4_masked(struct match *match, ovs_be32 nshc4, ovs_be32 mask)
+{
+    match->wc.masks.tunnel.nshc4 = mask;
+    match->flow.tunnel.nshc4 = nshc4 & mask;
+}
+
+void
+match_set_nsp(struct match *match, ovs_be32 nsp)
+{
+    match_set_nsp_masked(match, nsp, OVS_BE32_MAX);
+}
+
+
+void
+match_set_nsi(struct match *match, uint8_t nsi)
+{
+    match_set_nsi_masked(match, nsi, UINT8_MAX);
+}
+
+void
+match_set_nshc1(struct match *match, ovs_be32 nshc1)
+{
+    match_set_nshc1_masked(match, nshc1, OVS_BE32_MAX);
+}
+
+void
+match_set_nshc2(struct match *match, ovs_be32 nshc2)
+{
+    match_set_nshc2_masked(match, nshc2, OVS_BE32_MAX);
+}
+
+void
+match_set_nshc3(struct match *match, ovs_be32 nshc3)
+{
+    match_set_nshc3_masked(match, nshc3, OVS_BE32_MAX);
+}
+
+void
+match_set_nshc4(struct match *match, ovs_be32 nshc4)
+{
+    match_set_nshc4_masked(match, nshc4, OVS_BE32_MAX);
+}
+
+
 /* Returns true if 'a' and 'b' wildcard the same fields and have the same
  * values for fixed fields, otherwise false. */
 bool
@@ -880,6 +960,16 @@ format_flow_tunnel(struct ds *s, const struct match *match)
     const struct flow_tnl *tnl = &match->flow.tunnel;
 
     format_be64_masked(s, "tun_id", tnl->tun_id, wc->masks.tunnel.tun_id);
+    format_be32_masked(s, "nsp", tnl->nsp, wc->masks.tunnel.nsp);
+    format_be32_masked(s, "nshc1", tnl->nshc1, wc->masks.tunnel.nshc1);
+    format_be32_masked(s, "nshc2", tnl->nshc2, wc->masks.tunnel.nshc2);
+    format_be32_masked(s, "nshc3", tnl->nshc3, wc->masks.tunnel.nshc3);
+    format_be32_masked(s, "nshc4", tnl->nshc4, wc->masks.tunnel.nshc4);
+
+    if (wc->masks.tunnel.nsi) {
+        ds_put_format(s, "nsi=%"PRIu8",", tnl->nsi);
+    }
+
     format_ip_netmask(s, "tun_src", tnl->ip_src, wc->masks.tunnel.ip_src);
     format_ip_netmask(s, "tun_dst", tnl->ip_dst, wc->masks.tunnel.ip_dst);
 
diff --git a/lib/match.h b/lib/match.h
index 3e133e5..3d58c8e 100644
--- a/lib/match.h
+++ b/lib/match.h
@@ -146,6 +146,20 @@ void match_set_nd_target(struct match *, const struct in6_addr *);
 void match_set_nd_target_masked(struct match *, const struct in6_addr *,
                                 const struct in6_addr *);
 
+void match_set_nsp_masked(struct match *, ovs_be32 nsp, ovs_be32 mask);
+void match_set_nsi_masked(struct match *match, uint8_t nsi, uint8_t mask);
+void match_set_nshc1_masked(struct match *, ovs_be32 nshc1, ovs_be32 mask);
+void match_set_nshc2_masked(struct match *, ovs_be32 nshc2, ovs_be32 mask);
+void match_set_nshc3_masked(struct match *, ovs_be32 nshc3, ovs_be32 mask);
+void match_set_nshc4_masked(struct match *, ovs_be32 nshc4, ovs_be32 mask);
+
+void match_set_nsp(struct match *, ovs_be32 nsp);
+void match_set_nsi(struct match *match, uint8_t nsi);
+void match_set_nshc1(struct match *, ovs_be32 nshc1);
+void match_set_nshc2(struct match *, ovs_be32 nshc2);
+void match_set_nshc3(struct match *, ovs_be32 nshc3);
+void match_set_nshc4(struct match *, ovs_be32 nshc4);
+
 bool match_equal(const struct match *, const struct match *);
 uint32_t match_hash(const struct match *, uint32_t basis);
 
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 224ba53..0814a57 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -205,6 +205,18 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     CASE_MFF_TUN_METADATA:
         return !ULLONG_GET(wc->masks.tunnel.metadata.present.map,
                            mf->id - MFF_TUN_METADATA0);
+    case MFF_NSP:
+        return !wc->masks.tunnel.nsp;
+    case MFF_NSI:
+        return !wc->masks.tunnel.nsi;
+    case MFF_NSH_C1:
+        return !wc->masks.tunnel.nshc1;
+    case MFF_NSH_C2:
+        return !wc->masks.tunnel.nshc2;
+    case MFF_NSH_C3:
+        return !wc->masks.tunnel.nshc3;
+    case MFF_NSH_C4:
+        return !wc->masks.tunnel.nshc4;
     case MFF_METADATA:
         return !wc->masks.metadata;
     case MFF_IN_PORT:
@@ -526,6 +538,12 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_ND_TARGET:
     case MFF_ND_SLL:
     case MFF_ND_TLL:
+    case MFF_NSP:
+    case MFF_NSI:
+    case MFF_NSH_C1:
+    case MFF_NSH_C2:
+    case MFF_NSH_C3:
+    case MFF_NSH_C4:
         return true;
 
     case MFF_IN_PORT_OXM:
@@ -788,6 +806,27 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         value->ipv6 = flow->nd_target;
         break;
 
+    case MFF_NSP:
+        value->be32 = flow->tunnel.nsp;
+        break;
+
+    case MFF_NSI:
+        value->u8 = flow->tunnel.nsi;
+        break;
+
+    case MFF_NSH_C1:
+        value->be32 = flow->tunnel.nshc1;
+        break;
+    case MFF_NSH_C2:
+        value->be32 = flow->tunnel.nshc2;
+        break;
+    case MFF_NSH_C3:
+        value->be32 = flow->tunnel.nshc3;
+        break;
+    case MFF_NSH_C4:
+        value->be32 = flow->tunnel.nshc4;
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -1020,6 +1059,30 @@ mf_set_value(const struct mf_field *mf,
         match_set_nd_target(match, &value->ipv6);
         break;
 
+    case MFF_NSP:
+        match_set_nsp(match, value->be32);
+        break;
+
+    case MFF_NSI:
+        match_set_nsi(match, value->u8);
+        break;
+
+    case MFF_NSH_C1:
+        match_set_nshc1(match, value->be32);
+        break;
+
+    case MFF_NSH_C2:
+        match_set_nshc2(match, value->be32);
+        break;
+
+    case MFF_NSH_C3:
+        match_set_nshc3(match, value->be32);
+        break;
+
+    case MFF_NSH_C4:
+        match_set_nshc4(match, value->be32);
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -1307,6 +1370,30 @@ mf_set_flow_value(const struct mf_field *mf,
         flow->nd_target = value->ipv6;
         break;
 
+    case MFF_NSP:
+        flow->tunnel.nsp = value->be32;
+        break;
+
+    case MFF_NSI:
+        flow->tunnel.nsi = value->u8;
+        break;
+
+    case MFF_NSH_C1:
+        flow->tunnel.nshc1 = value->be32;
+        break;
+
+    case MFF_NSH_C2:
+        flow->tunnel.nshc2 = value->be32;
+        break;
+
+    case MFF_NSH_C3:
+        flow->tunnel.nshc3 = value->be32;
+        break;
+
+    case MFF_NSH_C4:
+        flow->tunnel.nshc4 = value->be32;
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -1595,6 +1682,30 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
         memset(&match->flow.nd_target, 0, sizeof match->flow.nd_target);
         break;
 
+    case MFF_NSP:
+        match_set_nsp_masked(match, htonl(0), htonl(0));
+        break;
+
+    case MFF_NSI:
+        match_set_nsi_masked(match, 0, 0);
+        break;
+
+    case MFF_NSH_C1:
+        match_set_nshc1_masked(match, htonl(0), htonl(0));
+        break;
+
+    case MFF_NSH_C2:
+        match_set_nshc2_masked(match, htonl(0), htonl(0));
+        break;
+
+    case MFF_NSH_C3:
+        match_set_nshc3_masked(match, htonl(0), htonl(0));
+        break;
+
+    case MFF_NSH_C4:
+        match_set_nshc4_masked(match, htonl(0), htonl(0));
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -1793,6 +1904,30 @@ mf_set(const struct mf_field *mf,
         match_set_tcp_flags_masked(match, value->be16, mask->be16);
         break;
 
+    case MFF_NSP:
+        match_set_nsp_masked(match, value->be32, mask->be32);
+        break;
+
+    case MFF_NSI:
+        match_set_nsi_masked(match, value->u8, mask->u8);
+        break;
+
+    case MFF_NSH_C1:
+        match_set_nshc1_masked(match, value->be32, mask->be32);
+        break;
+
+    case MFF_NSH_C2:
+        match_set_nshc2_masked(match, value->be32, mask->be32);
+        break;
+
+    case MFF_NSH_C3:
+        match_set_nshc3_masked(match, value->be32, mask->be32);
+        break;
+
+    case MFF_NSH_C4:
+        match_set_nshc4_masked(match, value->be32, mask->be32);
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index 02272ef..948b403 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -1566,6 +1566,108 @@ enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_ND_TLL,
 
+    /* "nsp".
+     *
+     * For a packet received via a VXLAN tunnel including a (32-bit)
+     * network service header service path (nsp), the nsp is stored
+     * in the low 24-bits and the high bits are zeroed.  For
+     * other packets, the value is 0.
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSP(105) since v1.1.
+     * OXM: none.
+     * Prefix lookup member: tunnel.nsp.
+     */
+    MFF_NSP,
+
+    /* "nsi".
+     *
+     * For a packet received via a VXLAN tunnel, it includes a (8-bit)
+     * network service header service index (nsi).
+     *
+     * Type: u8.
+     * Maskable: bitwise.
+     * Formatting: decimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSI(106) since v1.1.
+     * OXM: none.
+     * Prefix lookup member: tunnel.nsi.
+     */
+    MFF_NSI,
+
+    /* "nshc1".
+     *
+     * For a packet received via a VXLAN tunnel including a (32-bit)
+     * Network Platform Context (nshc1), the nshc1 is stored
+     * in the 32-bits.  For other packets, the value is 0.
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_C1(107) since v1.1.
+     * OXM: none.
+     * Prefix lookup member: tunnel.nshc1.
+     */
+    MFF_NSH_C1,
+
+    /* "nshc2".
+     *
+     * For a packet received via a VXLAN tunnel including a (32-bit)
+     * Network Shared Context (nshc2), the nshc2 is stored
+     * in the 32-bits.  For other packets, the value is 0.
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_C2(108) since v1.1.
+     * OXM: none.
+     * Prefix lookup member: tunnel.nshc2.
+     */
+    MFF_NSH_C2,
+
+    /* "nshc3".
+     *
+     * For a packet received via a VXLAN tunnel including a (32-bit)
+     * Service Platform Context (nshc3), the nshc3 is stored
+     * in the 32-bits.  For other packets, the value is 0.
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_C3(109) since v1.1.
+     * OXM: none.
+     * Prefix lookup member: tunnel.nshc3.
+     */
+    MFF_NSH_C3,
+
+    /* "nshc4".
+     *
+     * For a packet received via a VXLAN tunnel including a (32-bit)
+     * Service Shared Context (nshc4), the nshc4 is stored
+     * in the 32-bits.  For other packets, the value is 0.
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_C4(110) since v1.1.
+     * OXM: none.
+     * Prefix lookup member: tunnel.nshc4.
+     */
+    MFF_NSH_C4,
+
     MFF_N_IDS
 };
 
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index ff50563..3f85386 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -424,6 +424,86 @@ parse_key(const struct smap *args, const char *name,
     }
 }
 
+static ovs_be32
+parse_nsp(const struct smap *args, const char *name,
+          bool *present, bool *flow)
+{
+    const char *s;
+
+    *present = false;
+    *flow = false;
+
+    s = smap_get(args, name);
+    if (!s) {
+        s = smap_get(args, "nsp");
+        if (!s) {
+            return 0;
+        }
+    }
+
+    *present = true;
+
+    if (!strcmp(s, "flow")) {
+        *flow = true;
+        return 0;
+    } else {
+        return htonl(strtoul(s, NULL, 0));
+    }
+}
+static uint8_t
+parse_nsi(const struct smap *args, const char *name,
+          bool *present, bool *flow)
+{
+    const char *s;
+
+    *present = false;
+    *flow = false;
+
+    s = smap_get(args, name);
+    if (!s) {
+        s = smap_get(args, "nsi");
+        if (!s) {
+            return 0;
+        }
+    }
+
+    *present = true;
+
+    if (!strcmp(s, "flow")) {
+        *flow = true;
+        return 0;
+    } else {
+        return strtoul(s, NULL, 0);
+    }
+}
+
+static ovs_be32
+parse_nshc(const struct smap *args, const char *nsh_ele,const char *name,
+            bool *present, bool *flow)
+{
+    const char *s;
+
+    *present = false;
+    *flow = false;
+
+    s = smap_get(args, name);
+    if (!s) {
+        s = smap_get(args, nsh_ele);
+        if (!s) {
+            return 0;
+        }
+    }
+
+    *present = true;
+
+    if (!strcmp(s, "flow")) {
+        *flow = true;
+        return 0;
+    } else {
+        return htonl(strtoul(s, NULL, 0));
+    }
+}
+
 static int
 set_tunnel_config(struct netdev *dev_, const struct smap *args)
 {
@@ -563,6 +643,30 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
             }
 
             free(str);
+        } else if (!strcmp(node->key, "nsp") ||
+                   !strcmp(node->key, "in_nsp") ||
+                   !strcmp(node->key, "out_nsp")) {
+            /* Handled separately below. */
+        } else if (!strcmp(node->key, "nsi") ||
+                   !strcmp(node->key, "in_nsi") ||
+                   !strcmp(node->key, "out_nsi")) {
+            /* Handled separately below. */
+        } else if (!strcmp(node->key, "nshc1") ||
+                   !strcmp(node->key, "in_nshc1") ||
+                   !strcmp(node->key, "out_nshc1")) {
+            /* Handled separately below. */
+        } else if (!strcmp(node->key, "nshc2") ||
+                   !strcmp(node->key, "in_nshc2") ||
+                   !strcmp(node->key, "out_nshc2")) {
+            /* Handled separately below. */
+        } else if (!strcmp(node->key, "nshc3") ||
+                   !strcmp(node->key, "in_nshc3") ||
+                   !strcmp(node->key, "out_nshc3")) {
+            /* Handled separately below. */
+        } else if (!strcmp(node->key, "nshc4") ||
+                   !strcmp(node->key, "in_nshc4") ||
+                   !strcmp(node->key, "out_nshc4")) {
+            /* Handled separately below. */
         } else {
             VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->key);
         }
@@ -623,6 +727,65 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
                                &tnl_cfg.out_key_present,
                                &tnl_cfg.out_key_flow);
 
+    if (tnl_cfg.dst_port == htons(VXGPE_DST_PORT)) {
+        tnl_cfg.in_nsp = parse_nsp(args, "in_nsp",
+                                   &tnl_cfg.in_nsp_present,
+                                   &tnl_cfg.in_nsp_flow);
+
+        tnl_cfg.out_nsp = parse_nsp(args, "out_nsp",
+                                    &tnl_cfg.out_nsp_present,
+                                    &tnl_cfg.out_nsp_flow);
+
+        tnl_cfg.in_nsi = parse_nsi(args, "in_nsi",
+                                   &tnl_cfg.in_nsi_present,
+                                   &tnl_cfg.in_nsi_flow);
+
+        tnl_cfg.out_nsi = parse_nsi(args, "out_nsi",
+                                    &tnl_cfg.out_nsi_present,
+                                    &tnl_cfg.out_nsi_flow);
+
+        tnl_cfg.in_nshc1 = parse_nshc(args, "nshc1", "in_nshc1",
+                                     &tnl_cfg.in_nshc1_present,
+                                     &tnl_cfg.in_nshc1_flow);
+
+        tnl_cfg.out_nshc1 = parse_nshc(args, "nshc1", "out_nshc1",
+                                      &tnl_cfg.out_nshc1_present,
+                                      &tnl_cfg.out_nshc1_flow);
+
+        tnl_cfg.in_nshc2 = parse_nshc(args, "nshc2", "in_nshc2",
+                                     &tnl_cfg.in_nshc2_present,
+                                     &tnl_cfg.in_nshc2_flow);
+
+        tnl_cfg.out_nshc2 = parse_nshc(args, "nshc2", "out_nshc2",
+                                      &tnl_cfg.out_nshc2_present,
+                                      &tnl_cfg.out_nshc2_flow);
+
+        tnl_cfg.in_nshc3 = parse_nshc(args, "nshc3", "in_nshc3",
+                                     &tnl_cfg.in_nshc3_present,
+                                     &tnl_cfg.in_nshc3_flow);
+
+        tnl_cfg.out_nshc3 = parse_nshc(args, "nshc3", "out_nshc3",
+                                      &tnl_cfg.out_nshc3_present,
+                                      &tnl_cfg.out_nshc3_flow);
+
+        tnl_cfg.in_nshc4 = parse_nshc(args, "nshc4", "in_nshc4",
+                                     &tnl_cfg.in_nshc4_present,
+                                     &tnl_cfg.in_nshc4_flow);
+
+        tnl_cfg.out_nshc4 = parse_nshc(args, "nshc4", "out_nshc4",
+                                      &tnl_cfg.out_nshc4_present,
+                                      &tnl_cfg.out_nshc4_flow);
+
+        /* Default nsh service index is 1, if lower packet is dropped */
+        if (!tnl_cfg.in_nsi) {
+            tnl_cfg.in_nsi = 1;
+        }
+
+        if (!tnl_cfg.out_nsi) {
+            tnl_cfg.out_nsi = 1;
+        }
+    }
+
     ovs_mutex_lock(&dev->mutex);
     if (memcmp(&dev->tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) {
         dev->tnl_cfg = tnl_cfg;
@@ -709,6 +872,130 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
         smap_add(args, "df_default", "false");
     }
 
+    if (tnl_cfg.in_nsp_flow && tnl_cfg.out_nsp_flow) {
+        smap_add(args, "nsp", "flow");
+    } else if (tnl_cfg.in_nsp_present && tnl_cfg.out_nsp_present
+               && tnl_cfg.in_nsp == tnl_cfg.out_nsp) {
+        smap_add_format(args, "nsp", "%#"PRIx32, ntohl(tnl_cfg.in_nsp));
+    } else {
+        if (tnl_cfg.in_nsp_flow) {
+            smap_add(args, "in_nsp", "flow");
+        } else if (tnl_cfg.in_nsp_present) {
+            smap_add_format(args, "in_nsp", "%#"PRIx32,
+                            ntohl(tnl_cfg.in_nsp));
+        }
+
+        if (tnl_cfg.out_nsp_flow) {
+            smap_add(args, "out_nsp", "flow");
+        } else if (tnl_cfg.out_nsp_present) {
+            smap_add_format(args, "out_nsp", "%#"PRIx32,
+                            ntohl(tnl_cfg.out_nsp));
+        }
+    }
+
+    if (tnl_cfg.in_nsi_flow && tnl_cfg.out_nsi_flow) {
+        smap_add(args, "nsi", "flow");
+    } else if (tnl_cfg.in_nsi_present && tnl_cfg.out_nsi_present
+               && tnl_cfg.in_nsi == tnl_cfg.out_nsi) {
+        smap_add_format(args, "nsi", "%"PRIu8, tnl_cfg.in_nsi);
+    } else {
+        if (tnl_cfg.in_nsi_flow) {
+            smap_add(args, "in_nsi", "flow");
+        } else if (tnl_cfg.in_nsi_present) {
+            smap_add_format(args, "in_nsi", "%"PRIu8, tnl_cfg.in_nsi);
+        }
+
+        if (tnl_cfg.out_nsi_flow) {
+            smap_add(args, "out_nsi", "flow");
+        } else if (tnl_cfg.out_nsi_present) {
+            smap_add_format(args, "out_nsi", "%"PRIu8, tnl_cfg.out_nsi);
+        }
+    }
+
+    if (tnl_cfg.in_nshc1_flow && tnl_cfg.out_nshc1_flow) {
+        smap_add(args, "nshc1", "flow");
+    } else if (tnl_cfg.in_nshc1_present && tnl_cfg.out_nshc1_present
+               && tnl_cfg.in_nshc1 == tnl_cfg.out_nshc1) {
+        smap_add_format(args, "nshc1", "%#"PRIx32, ntohl(tnl_cfg.in_nshc1));
+    } else {
+        if (tnl_cfg.in_nshc1_flow) {
+            smap_add(args, "in_nshc1", "flow");
+        } else if (tnl_cfg.in_nshc1_present) {
+            smap_add_format(args, "in_nshc1", "%#"PRIx32,
+                            ntohl(tnl_cfg.in_nshc1));
+        }
+
+        if (tnl_cfg.out_nshc1_flow) {
+            smap_add(args, "out_nshc1", "flow");
+        } else if (tnl_cfg.out_nshc1_present) {
+            smap_add_format(args, "out_nshc1", "%#"PRIx32,
+                            ntohl(tnl_cfg.out_nshc1));
+        }
+    }
+
+    if (tnl_cfg.in_nshc2_flow && tnl_cfg.out_nshc2_flow) {
+        smap_add(args, "nshc2", "flow");
+    } else if (tnl_cfg.in_nshc2_present && tnl_cfg.out_nshc2_present
+               && tnl_cfg.in_nshc2 == tnl_cfg.out_nshc2) {
+        smap_add_format(args, "nshc2", "%#"PRIx32, ntohl(tnl_cfg.in_nshc2));
+    } else {
+        if (tnl_cfg.in_nshc2_flow) {
+            smap_add(args, "in_nshc2", "flow");
+        } else if (tnl_cfg.in_nshc2_present) {
+            smap_add_format(args, "in_nshc2", "%#"PRIx32,
+                            ntohl(tnl_cfg.in_nshc2));
+        }
+
+        if (tnl_cfg.out_nshc2_flow) {
+            smap_add(args, "out_nshc2", "flow");
+        } else if (tnl_cfg.out_nshc2_present) {
+            smap_add_format(args, "out_nshc2", "%#"PRIx32,
+                            ntohl(tnl_cfg.out_nshc2));
+        }
+    }
+
+    if (tnl_cfg.in_nshc3_flow && tnl_cfg.out_nshc3_flow) {
+        smap_add(args, "nshc3", "flow");
+    } else if (tnl_cfg.in_nshc3_present && tnl_cfg.out_nshc3_present
+               && tnl_cfg.in_nshc3 == tnl_cfg.out_nshc3) {
+        smap_add_format(args, "nshc3", "%#"PRIx32, ntohl(tnl_cfg.in_nshc3));
+    } else {
+        if (tnl_cfg.in_nshc3_flow) {
+            smap_add(args, "in_nshc3", "flow");
+        } else if (tnl_cfg.in_nshc3_present) {
+            smap_add_format(args, "in_nshc3", "%#"PRIx32,
+                            ntohl(tnl_cfg.in_nshc3));
+        }
+
+        if (tnl_cfg.out_nshc3_flow) {
+            smap_add(args, "out_nshc3", "flow");
+        } else if (tnl_cfg.out_nshc3_present) {
+            smap_add_format(args, "out_nshc3", "%#"PRIx32,
+                            ntohl(tnl_cfg.out_nshc3));
+        }
+    }
+
+    if (tnl_cfg.in_nshc4_flow && tnl_cfg.out_nshc4_flow) {
+        smap_add(args, "nshc4", "flow");
+    } else if (tnl_cfg.in_nshc4_present && tnl_cfg.out_nshc4_present
+               && tnl_cfg.in_nshc4 == tnl_cfg.out_nshc4) {
+        smap_add_format(args, "nshc4", "%#"PRIx32, ntohl(tnl_cfg.in_nshc4));
+    } else {
+        if (tnl_cfg.in_nshc4_flow) {
+            smap_add(args, "in_nshc4", "flow");
+        } else if (tnl_cfg.in_nshc4_present) {
+            smap_add_format(args, "in_nshc4", "%#"PRIx32,
+                            ntohl(tnl_cfg.in_nshc4));
+        }
+
+        if (tnl_cfg.out_nshc4_flow) {
+            smap_add(args, "out_nshc4", "flow");
+        } else if (tnl_cfg.out_nshc4_present) {
+            smap_add_format(args, "out_nshc4", "%#"PRIx32,
+                            ntohl(tnl_cfg.out_nshc4));
+        }
+    }
+
     return 0;
 }
 
@@ -1356,7 +1643,6 @@ netdev_vport_range(struct unixctl_conn *conn, int argc,
     unixctl_command_reply(conn, "OK");
 }
 
-
 #define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG,             \
                         GET_TUNNEL_CONFIG, GET_STATUS,      \
                         BUILD_HEADER,                       \
@@ -1427,6 +1713,7 @@ netdev_vport_range(struct unixctl_conn *conn, int argc,
     NULL,                   /* rx_drain */
 
 
+
 #define TUNNEL_CLASS(NAME, DPIF_PORT, BUILD_HEADER, PUSH_HEADER, POP_HEADER)   \
     { DPIF_PORT,                                                               \
         { NAME, VPORT_FUNCTIONS(get_tunnel_config,                             \
diff --git a/lib/netdev.h b/lib/netdev.h
index 0fbcb65..4dadf1c 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -106,6 +106,22 @@ struct netdev_stats {
 
 /* Configuration specific to tunnels. */
 struct netdev_tunnel_config {
+    bool in_nsp_present;
+    bool in_nsp_flow;
+    ovs_be32 in_nsp;            /* incoming NSH service path */
+
+    bool out_nsp_present;
+    bool out_nsp_flow;
+    ovs_be32 out_nsp;           /* outgoing NSH service path */
+
+    bool in_nsi_present;
+    bool in_nsi_flow;
+    uint8_t in_nsi;             /* incoming NSH service index */
+
+    bool out_nsi_present;
+    bool out_nsi_flow;
+    uint8_t out_nsi;            /* outgoing NSH service index */
+
     bool in_key_present;
     bool in_key_flow;
     ovs_be64 in_key;
@@ -132,6 +148,39 @@ struct netdev_tunnel_config {
     bool csum;
     bool ipsec;
     bool dont_fragment;
+
+    bool in_nshc1_present;
+    bool in_nshc1_flow;
+    ovs_be32 in_nshc1;         /* incoming NSH context c1 */
+
+    bool out_nshc1_present;
+    bool out_nshc1_flow;
+    ovs_be32 out_nshc1;        /* outgoing NSH context c1 */
+
+    bool in_nshc2_present;
+    bool in_nshc2_flow;
+    ovs_be32 in_nshc2;         /* incoming NSH context c2 */
+
+    bool out_nshc2_present;
+    bool out_nshc2_flow;
+    ovs_be32 out_nshc2;        /* outgoing NSH context c2 */
+
+    bool in_nshc3_present;
+    bool in_nshc3_flow;
+    ovs_be32 in_nshc3;         /* incoming NSH context c3 */
+
+    bool out_nshc3_present;
+    bool out_nshc3_flow;
+    ovs_be32 out_nshc3;        /* outgoing NSH context c3 */
+
+    bool in_nshc4_present;
+    bool in_nshc4_flow;
+    ovs_be32 in_nshc4;         /* incoming NSH context c4 */
+
+    bool out_nshc4_present;
+    bool out_nshc4_flow;
+    ovs_be32 out_nshc4;        /* outgoing NSH context c4 */
+
 };
 
 void netdev_run(void);
diff --git a/lib/nx-match.c b/lib/nx-match.c
index eef2c54..7b8f09b 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1004,6 +1004,12 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
     /* Tunnel ID. */
     nxm_put_64m(b, MFF_TUN_ID, oxm,
                 flow->tunnel.tun_id, match->wc.masks.tunnel.tun_id);
+    nxm_put_32m(b, MFF_NSP, oxm, flow->tunnel.nsp, match->wc.masks.tunnel.nsp);
+    nxm_put_8m(b, MFF_NSI, oxm, flow->tunnel.nsi, match->wc.masks.tunnel.nsi);
+    nxm_put_32m(b, MFF_NSH_C1, oxm, flow->tunnel.nshc1, match->wc.masks.tunnel.nshc1);
+    nxm_put_32m(b, MFF_NSH_C2, oxm, flow->tunnel.nshc2, match->wc.masks.tunnel.nshc2);
+    nxm_put_32m(b, MFF_NSH_C3, oxm, flow->tunnel.nshc3, match->wc.masks.tunnel.nshc3);
+    nxm_put_32m(b, MFF_NSH_C4, oxm, flow->tunnel.nshc4, match->wc.masks.tunnel.nshc4);
 
     /* Other tunnel metadata. */
     nxm_put_16m(b, MFF_TUN_FLAGS, oxm,
diff --git a/lib/odp-util.c b/lib/odp-util.c
index c173623..e8bc86d 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -1181,6 +1181,12 @@ static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX +
     [OVS_TUNNEL_KEY_ATTR_TP_SRC]        = { .len = 2 },
     [OVS_TUNNEL_KEY_ATTR_TP_DST]        = { .len = 2 },
     [OVS_TUNNEL_KEY_ATTR_OAM]           = { .len = 0 },
+    [OVS_TUNNEL_KEY_ATTR_NSP]           = { .len = 4 },
+    [OVS_TUNNEL_KEY_ATTR_NSI]           = { .len = 1 },
+    [OVS_TUNNEL_KEY_ATTR_NSH_C1]        = { .len = 4 },
+    [OVS_TUNNEL_KEY_ATTR_NSH_C2]        = { .len = 4 },
+    [OVS_TUNNEL_KEY_ATTR_NSH_C3]        = { .len = 4 },
+    [OVS_TUNNEL_KEY_ATTR_NSH_C4]        = { .len = 4 },
     [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS]   = { .len = ATTR_LEN_VARIABLE },
     [OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS]    = { .len = ATTR_LEN_NESTED,
                                             .next = ovs_vxlan_ext_attr_lens ,
@@ -1340,7 +1346,30 @@ odp_tun_key_from_attr__(const struct nlattr *attr,
                 return ODP_FIT_ERROR;
             }
             break;
-
+        case OVS_TUNNEL_KEY_ATTR_NSP:
+            tun->nsp = nl_attr_get_be32(a);
+            tun->flags |= FLOW_TNL_F_NSP;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSI:
+            tun->nsi = nl_attr_get_u8(a);
+            tun->flags |= FLOW_TNL_F_NSI;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C1:
+            tun->nshc1 = nl_attr_get_be32(a);
+            tun->flags |= FLOW_TNL_F_NSH_C1;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C2:
+            tun->nshc2 = nl_attr_get_be32(a);
+            tun->flags |= FLOW_TNL_F_NSH_C2;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C3:
+            tun->nshc3 = nl_attr_get_be32(a);
+            tun->flags |= FLOW_TNL_F_NSH_C3;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C4:
+            tun->nshc4 = nl_attr_get_be32(a);
+            tun->flags |= FLOW_TNL_F_NSH_C4;
+            break;
         default:
             /* Allow this to show up as unexpected, if there are unknown
              * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
@@ -1413,6 +1442,24 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key,
         nl_msg_end_nested(a, vxlan_opts_ofs);
     }
     tun_metadata_to_geneve_nlattr(tun_key, tun_flow_key, key_buf, a);
+    if (tun_key->nsp || tun_key->flags & FLOW_TNL_F_NSP) {
+        nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_NSP, tun_key->nsp);
+    }
+    if (tun_key->nsi || tun_key->flags & FLOW_TNL_F_NSI) {
+        nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_NSI, tun_key->nsi);
+    }
+    if (tun_key->nshc1 || tun_key->flags & FLOW_TNL_F_NSH_C1) {
+        nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_NSH_C1, tun_key->nshc1);
+    }
+    if (tun_key->nshc2 || tun_key->flags & FLOW_TNL_F_NSH_C2) {
+        nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_NSH_C2, tun_key->nshc2);
+    }
+    if (tun_key->nshc3 || tun_key->flags & FLOW_TNL_F_NSH_C3) {
+        nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_NSH_C3, tun_key->nshc3);
+    }
+    if (tun_key->nshc4 || tun_key->flags & FLOW_TNL_F_NSH_C4) {
+        nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_NSH_C4, tun_key->nshc4);
+    }
 
     nl_msg_end_nested(a, tun_key_ofs);
 }
@@ -1677,6 +1724,24 @@ format_be16x(struct ds *ds, const char *name, ovs_be16 key,
     }
 }
 
+
+static void
+format_be32(struct ds *ds, const char *name, ovs_be32 key,
+            const ovs_be32 *mask, bool verbose)
+{
+    bool mask_empty = mask && !*mask;
+
+    if (verbose || !mask_empty) {
+        bool mask_full = !mask || *mask == OVS_BE32_MAX;
+
+        ds_put_format(ds, "%s=%"PRIx32, name, ntohl(key));
+        if (!mask_full) { /* Partially masked. */
+            ds_put_format(ds, "/%#"PRIx32, ntohl(*mask));
+        }
+        ds_put_char(ds, ',');
+    }
+}
+
 static void
 format_tun_flags(struct ds *ds, const char *name, uint16_t key,
                  const uint16_t *mask, bool verbose)
@@ -1953,6 +2018,54 @@ format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
             format_be16(ds, "tp_dst", nl_attr_get_be16(a),
                         ma ? nl_attr_get(ma) : NULL, verbose);
             break;
+        case OVS_TUNNEL_KEY_ATTR_NSP:
+            format_be32(ds, "nsp", nl_attr_get_be32(a),
+                        ma ? nl_attr_get(ma) : NULL, verbose);
+        flags |= FLOW_TNL_F_NSP;
+            if (ma) {
+                mask_flags |= FLOW_TNL_F_NSP;
+            }
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSI:
+            format_u8u(ds, "nsi", nl_attr_get_u8(a),
+                        ma ? nl_attr_get(ma) : NULL, verbose);
+        flags |= FLOW_TNL_F_NSI;
+            if (ma) {
+                mask_flags |= FLOW_TNL_F_NSI;
+            }
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C1:
+            format_be32(ds, "nshc1", nl_attr_get_be32(a),
+                        ma ? nl_attr_get(ma) : NULL, verbose);
+        flags |= FLOW_TNL_F_NSH_C1;
+            if (ma) {
+                mask_flags |= FLOW_TNL_F_NSH_C1;
+            }
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C2:
+            format_be32(ds, "nshc2", nl_attr_get_be32(a),
+                        ma ? nl_attr_get(ma) : NULL, verbose);
+        flags |= FLOW_TNL_F_NSH_C2;
+            if (ma) {
+                mask_flags |= FLOW_TNL_F_NSH_C2;
+            }
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C3:
+            format_be32(ds, "nshc3", nl_attr_get_be32(a),
+                        ma ? nl_attr_get(ma) : NULL, verbose);
+        flags |= FLOW_TNL_F_NSH_C3;
+            if (ma) {
+                mask_flags |= FLOW_TNL_F_NSH_C3;
+            }
+            break;
+        case OVS_TUNNEL_KEY_ATTR_NSH_C4:
+            format_be32(ds, "nshc4", nl_attr_get_be32(a),
+                        ma ? nl_attr_get(ma) : NULL, verbose);
+        flags |= FLOW_TNL_F_NSH_C4;
+            if (ma) {
+                mask_flags |= FLOW_TNL_F_NSH_C4;
+            }
+            break;
         case OVS_TUNNEL_KEY_ATTR_OAM:
 	    flags |= FLOW_TNL_F_OAM;
             break;
@@ -2016,6 +2129,8 @@ format_frag(struct ds *ds, const char *name, uint8_t key,
     }
 }
 
+
+
 static void
 format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
                     const struct hmap *portno_names, struct ds *ds,
@@ -2546,6 +2661,29 @@ scan_be16(const char *s, ovs_be16 *key, ovs_be16 *mask)
 }
 
 static int
+scan_be32(const char *s, ovs_be32 *key, ovs_be32 *mask)
+{
+    uint32_t key_, mask_;
+    int n;
+
+    if (ovs_scan(s, "%"SCNi32"%n", &key_, &n)) {
+        int len = n;
+
+        *key = htonl(key_);
+        if (mask) {
+            if (ovs_scan(s + len, "/%"SCNi32"%n", &mask_, &n)) {
+                len += n;
+                *mask = htonl(mask_);
+            } else {
+                *mask = OVS_BE32_MAX;
+            }
+        }
+        return len;
+    }
+    return 0;
+}
+
+static int
 scan_be64(const char *s, ovs_be64 *key, ovs_be64 *mask)
 {
     uint64_t key_, mask_;
@@ -3135,12 +3273,19 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         SCAN_FIELD_NESTED("ttl=", uint8_t, u8, OVS_TUNNEL_KEY_ATTR_TTL);
         SCAN_FIELD_NESTED("tp_src=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_SRC);
         SCAN_FIELD_NESTED("tp_dst=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_DST);
+        SCAN_FIELD_NESTED("nsi=", uint8_t, u8, OVS_TUNNEL_KEY_ATTR_NSI);
+        SCAN_FIELD_NESTED("nsp=", ovs_be32, be32, OVS_TUNNEL_KEY_ATTR_NSP);
+        SCAN_FIELD_NESTED("nshc1=", ovs_be32, be32, OVS_TUNNEL_KEY_ATTR_NSH_C1);
+        SCAN_FIELD_NESTED("nshc2=", ovs_be32, be32, OVS_TUNNEL_KEY_ATTR_NSH_C2);
+        SCAN_FIELD_NESTED("nshc3=", ovs_be32, be32, OVS_TUNNEL_KEY_ATTR_NSH_C3);
+        SCAN_FIELD_NESTED("nshc4=", ovs_be32, be32, OVS_TUNNEL_KEY_ATTR_NSH_C4);
         SCAN_FIELD_NESTED_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp, vxlan_gbp_to_attr);
         SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve,
                                geneve_to_attr);
         SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
     } SCAN_END_NESTED();
 
+
     SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT);
 
     SCAN_BEGIN("eth(", struct ovs_key_ethernet) {
diff --git a/lib/odp-util.h b/lib/odp-util.h
index bc27794..9f8e741 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -116,6 +116,12 @@ void odp_portno_names_destroy(struct hmap *portno_names);
  *  - OVS_TUNNEL_KEY_ATTR_OAM            0    --     4      4
  *  - OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS    256  --     4      260
  *  - OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS     -    --     -      - (shared with _GENEVE_OPTS)
+ *  - OVS_TUNNEL_KEY_ATTR_NSP            4    --     4      8
+ *  - OVS_TUNNEL_KEY_ATTR_NSI            1    3      4      8
+ *  - OVS_TUNNEL_KEY_ATTR_NSH_C1         4    --     4      8
+ *  - OVS_TUNNEL_KEY_ATTR_NSH_C2         4    --     4      8
+ *  - OVS_TUNNEL_KEY_ATTR_NSH_C3         4    --     4      8
+ *  - OVS_TUNNEL_KEY_ATTR_NSH_C4         4    --     4      8
  *  OVS_KEY_ATTR_IN_PORT                 4    --     4      8
  *  OVS_KEY_ATTR_SKB_MARK                4    --     4      8
  *  OVS_KEY_ATTR_DP_HASH                 4    --     4      8
@@ -129,7 +135,7 @@ void odp_portno_names_destroy(struct hmap *portno_names);
  *  OVS_KEY_ATTR_ICMPV6                  2     2     4      8
  *  OVS_KEY_ATTR_ND                     28    --     4     32
  *  ----------------------------------------------------------
- *  total                                                 488
+ *  total                                                 536
  *
  * We include some slack space in case the calculation isn't quite right or we
  * add another field and forget to adjust this value.
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 88f0f85..b35daa8 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -286,6 +286,24 @@ enum ofp_raw_action_type {
     /* NX1.0+(34): struct nx_action_conjunction. */
     NXAST_RAW_CONJUNCTION,
 
+    /* NX1.0+(106): uint8_t. */
+    NXAST_RAW_SET_NSI,
+
+    /* NX1.0+(105): ovs_be32. */
+    NXAST_RAW_SET_NSP,
+
+    /* NX1.0+(107): ovs_be32. */
+    NXAST_RAW_SET_NSH_C1,
+
+    /* NX1.0+(108): ovs_be32. */
+    NXAST_RAW_SET_NSH_C2,
+
+    /* NX1.0+(109): ovs_be32. */
+    NXAST_RAW_SET_NSH_C3,
+
+    /* NX1.0+(110): ovs_be32. */
+    NXAST_RAW_SET_NSH_C4,
+
 /* ## ------------------ ## */
 /* ## Debugging actions. ## */
 /* ## ------------------ ## */
@@ -3179,7 +3197,310 @@ format_SET_TUNNEL(const struct ofpact_tunnel *a, struct ds *s)
                    || a->ofpact.raw == NXAST_RAW_SET_TUNNEL64 ? "64" : ""),
                   a->tun_id);
 }
-
+
+/* Action structure for NXAST_SET_NSP.
+ *
+ * Sets the encapsulating NSH service path ID to a 32-bit value. */
+
+/* Set NSI actions. */
+static enum ofperr
+decode_NXAST_RAW_SET_NSI(const uint8_t nsi, struct ofpbuf * out)
+{
+    struct ofpact_nsi *pnsi = ofpact_put_SET_NSI(out);
+    pnsi->ofpact.raw = NXAST_RAW_SET_NSI;
+    pnsi->nsi = nsi;
+
+    return 0;
+}
+
+static void
+encode_SET_NSI(const struct ofpact_nsi *pnsi,
+                  enum ofp_version ofp_version, struct ofpbuf *out)
+{
+    uint8_t nsi = pnsi->nsi;
+
+    if (ofp_version < OFP12_VERSION) {
+        put_NXAST_SET_NSI(out, nsi);
+    } else {
+        ofpact_put_set_field(out, ofp_version, MFF_NSI, nsi);
+    }
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_set_nsi(char *arg, struct ofpbuf *ofpacts,
+                 enum ofp_raw_action_type raw)
+{
+    struct ofpact_nsi *pnsi;
+
+    pnsi = ofpact_put_SET_NSI(ofpacts);
+    pnsi->ofpact.raw = raw;
+
+    return str_to_u8(arg, "nsi", &pnsi->nsi);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_SET_NSI(char *arg, struct ofpbuf *ofpacts,
+                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    return parse_set_nsi(arg, ofpacts, NXAST_RAW_SET_NSI);
+}
+
+static void
+format_SET_NSI(const struct ofpact_nsi *a, struct ds *s)
+{
+    ds_put_format(s, "set_nsi:%d", a->nsi);
+}
+
+/* Set NSP actions. */
+static enum ofperr
+decode_NXAST_RAW_SET_NSP(const ovs_be32 nsp, struct ofpbuf * out)
+{
+    struct ofpact_nsp *pnsp = ofpact_put_SET_NSP(out);
+    pnsp->ofpact.raw = NXAST_RAW_SET_NSP;
+    pnsp->nsp = nsp;
+
+    return 0;
+}
+
+static void
+encode_SET_NSP(const struct ofpact_nsp *pnsp,
+                  enum ofp_version ofp_version, struct ofpbuf *out)
+{
+    uint32_t nsp = pnsp->nsp;
+
+    if (ofp_version < OFP12_VERSION) {
+        put_NXAST_SET_NSP(out, nsp);
+    } else {
+        ofpact_put_set_field(out, ofp_version, MFF_NSP, nsp);
+    }
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_set_nsp(char *arg, struct ofpbuf *ofpacts,
+                 enum ofp_raw_action_type raw)
+{
+    struct ofpact_nsp *pnsp;
+
+    pnsp = ofpact_put_SET_NSP(ofpacts);
+    pnsp->ofpact.raw = raw;
+
+    return str_to_be32(arg, &pnsp->nsp);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_SET_NSP(char *arg, struct ofpbuf *ofpacts,
+                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    return parse_set_nsp(arg, ofpacts, NXAST_RAW_SET_NSP);
+}
+
+static void
+format_SET_NSP(const struct ofpact_nsp *a, struct ds *s)
+{
+    ds_put_format(s, "set_nsp:%#"PRIx32, ntohl(a->nsp));
+}
+
+
+/* Set NSH_C1 actions. */
+static enum ofperr
+decode_NXAST_RAW_SET_NSH_C1(const ovs_be32 nshc1, struct ofpbuf * out)
+{
+    struct ofpact_nshc1 *pnshc1= ofpact_put_SET_NSH_C1(out);
+    pnshc1->ofpact.raw = NXAST_RAW_SET_NSH_C1;
+    pnshc1->nshc1 = nshc1;
+
+    return 0;
+}
+
+static void
+encode_SET_NSH_C1(const struct ofpact_nshc1 *pnshc1,
+                  enum ofp_version ofp_version, struct ofpbuf *out)
+{
+    uint32_t nshc1 = pnshc1->nshc1;
+
+    if (ofp_version < OFP12_VERSION) {
+        put_NXAST_SET_NSH_C1(out, nshc1);
+    } else {
+        ofpact_put_set_field(out, ofp_version, MFF_NSH_C1, nshc1);
+    }
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_set_nshc1(char *arg, struct ofpbuf *ofpacts,
+                 enum ofp_raw_action_type raw)
+{
+    struct ofpact_nshc1 *pnshc1;
+
+    pnshc1 = ofpact_put_SET_NSH_C1(ofpacts);
+    pnshc1->ofpact.raw = raw;
+
+    return str_to_be32(arg, &pnshc1->nshc1);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_SET_NSH_C1(char *arg, struct ofpbuf *ofpacts,
+                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    return parse_set_nshc1(arg, ofpacts, NXAST_RAW_SET_NSH_C1);
+}
+
+static void
+format_SET_NSH_C1(const struct ofpact_nshc1 *a, struct ds *s)
+{
+    ds_put_format(s, "set_nshc1:%#"PRIx32, ntohl(a->nshc1));
+}
+
+
+/* Set NSH_C2 actions. */
+static enum ofperr
+decode_NXAST_RAW_SET_NSH_C2(const ovs_be32 nshc2, struct ofpbuf * out)
+{
+    struct ofpact_nshc2 *pnshc2= ofpact_put_SET_NSH_C2(out);
+    pnshc2->ofpact.raw = NXAST_RAW_SET_NSH_C2;
+    pnshc2->nshc2 = nshc2;
+
+    return 0;
+}
+
+static void
+encode_SET_NSH_C2(const struct ofpact_nshc2 *pnshc2,
+                  enum ofp_version ofp_version, struct ofpbuf *out)
+{
+    uint32_t nshc2 = pnshc2->nshc2;
+
+    if (ofp_version < OFP12_VERSION) {
+        put_NXAST_SET_NSH_C2(out, nshc2);
+    } else {
+        ofpact_put_set_field(out, ofp_version, MFF_NSH_C2, nshc2);
+    }
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_set_nshc2(char *arg, struct ofpbuf *ofpacts,
+                 enum ofp_raw_action_type raw)
+{
+    struct ofpact_nshc2 *pnshc2;
+
+    pnshc2 = ofpact_put_SET_NSH_C2(ofpacts);
+    pnshc2->ofpact.raw = raw;
+
+    return str_to_be32(arg, &pnshc2->nshc2);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_SET_NSH_C2(char *arg, struct ofpbuf *ofpacts,
+                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    return parse_set_nshc2(arg, ofpacts, NXAST_RAW_SET_NSH_C2);
+}
+
+static void
+format_SET_NSH_C2(const struct ofpact_nshc2 *a, struct ds *s)
+{
+    ds_put_format(s, "set_nshc2:%#"PRIx32, ntohl(a->nshc2));
+}
+
+
+/* Set NSH_C3 actions. */
+static enum ofperr
+decode_NXAST_RAW_SET_NSH_C3(const ovs_be32 nshc3, struct ofpbuf * out)
+{
+    struct ofpact_nshc3 *pnshc3= ofpact_put_SET_NSH_C3(out);
+    pnshc3->ofpact.raw = NXAST_RAW_SET_NSH_C3;
+    pnshc3->nshc3 = nshc3;
+
+    return 0;
+}
+
+static void
+encode_SET_NSH_C3(const struct ofpact_nshc3 *pnshc3,
+                  enum ofp_version ofp_version, struct ofpbuf *out)
+{
+    uint32_t nshc3 = pnshc3->nshc3;
+
+    if (ofp_version < OFP12_VERSION) {
+        put_NXAST_SET_NSH_C3(out, nshc3);
+    } else {
+        ofpact_put_set_field(out, ofp_version, MFF_NSH_C3, nshc3);
+    }
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_set_nshc3(char *arg, struct ofpbuf *ofpacts,
+                 enum ofp_raw_action_type raw)
+{
+    struct ofpact_nshc3 *pnshc3;
+
+    pnshc3 = ofpact_put_SET_NSH_C3(ofpacts);
+    pnshc3->ofpact.raw = raw;
+
+    return str_to_be32(arg, &pnshc3->nshc3);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_SET_NSH_C3(char *arg, struct ofpbuf *ofpacts,
+                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    return parse_set_nshc3(arg, ofpacts, NXAST_RAW_SET_NSH_C3);
+}
+
+static void
+format_SET_NSH_C3(const struct ofpact_nshc3 *a, struct ds *s)
+{
+    ds_put_format(s, "set_nshc3:%#"PRIx32, ntohl(a->nshc3));
+}
+
+
+
+/* Set NSH_C4 actions. */
+static enum ofperr
+decode_NXAST_RAW_SET_NSH_C4(const ovs_be32 nshc4, struct ofpbuf * out)
+{
+    struct ofpact_nshc4 *pnshc4= ofpact_put_SET_NSH_C4(out);
+    pnshc4->ofpact.raw = NXAST_RAW_SET_NSH_C4;
+    pnshc4->nshc4 = nshc4;
+
+    return 0;
+}
+
+static void
+encode_SET_NSH_C4(const struct ofpact_nshc4 *pnshc4,
+                  enum ofp_version ofp_version, struct ofpbuf *out)
+{
+    uint32_t nshc4 = pnshc4->nshc4;
+
+    if (ofp_version < OFP12_VERSION) {
+        put_NXAST_SET_NSH_C4(out, nshc4);
+    } else {
+        ofpact_put_set_field(out, ofp_version, MFF_NSH_C4, nshc4);
+    }
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_set_nshc4(char *arg, struct ofpbuf *ofpacts,
+                 enum ofp_raw_action_type raw)
+{
+    struct ofpact_nshc4 *pnshc4;
+
+    pnshc4 = ofpact_put_SET_NSH_C4(ofpacts);
+    pnshc4->ofpact.raw = raw;
+
+    return str_to_be32(arg, &pnshc4->nshc4);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_SET_NSH_C4(char *arg, struct ofpbuf *ofpacts,
+                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    return parse_set_nshc4(arg, ofpacts, NXAST_RAW_SET_NSH_C4);
+}
+
+static void
+format_SET_NSH_C4(const struct ofpact_nshc4 *a, struct ds *s)
+{
+    ds_put_format(s, "set_nshc4:%#"PRIx32, ntohl(a->nshc4));
+}
+
 /* Set queue action. */
 
 static enum ofperr
@@ -4831,6 +5152,12 @@ 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_SET_NSP:
+    case OFPACT_SET_NSI:
+    case OFPACT_SET_NSH_C1:
+    case OFPACT_SET_NSH_C2:
+    case OFPACT_SET_NSH_C3:
+    case OFPACT_SET_NSH_C4:
         return true;
     case OFPACT_BUNDLE:
     case OFPACT_CLEAR_ACTIONS:
@@ -4900,6 +5227,12 @@ 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_SET_NSP:
+    case OFPACT_SET_NSI:
+    case OFPACT_SET_NSH_C1:
+    case OFPACT_SET_NSH_C2:
+    case OFPACT_SET_NSH_C3:
+    case OFPACT_SET_NSH_C4:
         return true;
 
     /* In general these actions are excluded because they are not part of
@@ -5136,6 +5469,12 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
     case OFPACT_EXIT:
     case OFPACT_UNROLL_XLATE:
     case OFPACT_SAMPLE:
+    case OFPACT_SET_NSP:
+    case OFPACT_SET_NSI:
+    case OFPACT_SET_NSH_C1:
+    case OFPACT_SET_NSH_C2:
+    case OFPACT_SET_NSH_C3:
+    case OFPACT_SET_NSH_C4:
     case OFPACT_DEBUG_RECIRC:
     default:
         return OVSINST_OFPIT11_APPLY_ACTIONS;
@@ -5654,6 +5993,12 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
     case OFPACT_SET_QUEUE:
     case OFPACT_POP_QUEUE:
     case OFPACT_RESUBMIT:
+    case OFPACT_SET_NSP:
+    case OFPACT_SET_NSI:
+    case OFPACT_SET_NSH_C1:
+    case OFPACT_SET_NSH_C2:
+    case OFPACT_SET_NSH_C3:
+    case OFPACT_SET_NSH_C4:
         return 0;
 
     case OFPACT_FIN_TIMEOUT:
@@ -6161,6 +6506,12 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
     case OFPACT_GOTO_TABLE:
     case OFPACT_METER:
     case OFPACT_GROUP:
+    case OFPACT_SET_NSI:
+    case OFPACT_SET_NSP:
+    case OFPACT_SET_NSH_C1:
+    case OFPACT_SET_NSH_C2:
+    case OFPACT_SET_NSH_C3:
+    case OFPACT_SET_NSH_C4:
     case OFPACT_DEBUG_RECIRC:
     default:
         return false;
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index 51b2963..876c457 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -92,6 +92,12 @@
     OFPACT(SET_QUEUE,       ofpact_queue,       ofpact, "set_queue")    \
     OFPACT(POP_QUEUE,       ofpact_null,        ofpact, "pop_queue")    \
     OFPACT(FIN_TIMEOUT,     ofpact_fin_timeout, ofpact, "fin_timeout")  \
+    OFPACT(SET_NSI,         ofpact_nsi,         ofpact, "set_nsi")      \
+    OFPACT(SET_NSP,         ofpact_nsp,         ofpact, "set_nsp")      \
+    OFPACT(SET_NSH_C1,      ofpact_nshc1,       ofpact, "set_nshc1")    \
+    OFPACT(SET_NSH_C2,      ofpact_nshc2,       ofpact, "set_nshc2")    \
+    OFPACT(SET_NSH_C3,      ofpact_nshc3,       ofpact, "set_nshc3")    \
+    OFPACT(SET_NSH_C4,      ofpact_nshc4,       ofpact, "set_nshc4")    \
                                                                         \
     /* Flow table interaction. */                                       \
     OFPACT(RESUBMIT,        ofpact_resubmit,    ofpact, "resubmit")     \
@@ -425,6 +431,48 @@ struct ofpact_tunnel {
     uint64_t tun_id;
 };
 
+/* OFPACT_SET_NSI.
+ * Used for NXAST_SET_NSI */
+struct ofpact_nsi {
+    struct ofpact ofpact;
+    uint8_t nsi;
+};
+
+/* OFPACT_SET_NSP.
+ * Used for NXAST_SET_NSP */
+struct ofpact_nsp {
+    struct ofpact ofpact;
+    ovs_be32 nsp;
+};
+
+/* OFPACT_SET_NSH_C1.
+ * Used for NXAST_SET_NSH_C1 */
+struct ofpact_nshc1 {
+    struct ofpact ofpact;
+    ovs_be32 nshc1;
+};
+
+/* OFPACT_SET_NSH_C2.
+ * Used for NXAST_SET_NSH_C2 */
+struct ofpact_nshc2 {
+    struct ofpact ofpact;
+    ovs_be32 nshc2;
+};
+
+/* OFPACT_SET_NSH_C3.
+ * Used for NXAST_SET_NSH_C3 */
+struct ofpact_nshc3 {
+    struct ofpact ofpact;
+    ovs_be32 nshc3;
+};
+
+/* OFPACT_SET_NSH_C4.
+ * Used for NXAST_SET_NSH_C4 */
+struct ofpact_nshc4 {
+    struct ofpact ofpact;
+    ovs_be32 nshc4;
+};
+
 /* OFPACT_SET_QUEUE.
  *
  * Used for NXAST_SET_QUEUE. */
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 5950f06..099d3df 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -139,6 +139,19 @@ str_to_be64(const char *str, ovs_be64 *valuep)
     return error;
 }
 
+char * OVS_WARN_UNUSED_RESULT
+str_to_be32(const char *str, ovs_be32 *valuep)
+{
+    uint32_t value = 0;
+    char *error;
+
+    error = str_to_u32(str, &value);
+    if (!error) {
+        *valuep = htonl(value);
+    }
+    return error;
+}
+
 /* Parses 'str' as an Ethernet address into 'mac'.
  *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
index b64a32e..47d385b 100644
--- a/lib/ofp-parse.h
+++ b/lib/ofp-parse.h
@@ -98,6 +98,7 @@ char *str_to_u32(const char *str, uint32_t *valuep) OVS_WARN_UNUSED_RESULT;
 char *str_to_u64(const char *str, uint64_t *valuep) OVS_WARN_UNUSED_RESULT;
 char *str_to_be64(const char *str, ovs_be64 *valuep) OVS_WARN_UNUSED_RESULT;
 char *str_to_mac(const char *str, struct eth_addr *mac) OVS_WARN_UNUSED_RESULT;
+char *str_to_be32(const char *str, ovs_be32 *valuep) OVS_WARN_UNUSED_RESULT;
 char *str_to_ip(const char *str, ovs_be32 *ip) OVS_WARN_UNUSED_RESULT;
 
 #endif /* ofp-parse.h */
diff --git a/lib/packets.h b/lib/packets.h
index 4fb1427..12f2239 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -45,7 +45,12 @@ struct flow_tnl {
     ovs_be16 tp_dst;
     ovs_be16 gbp_id;
     uint8_t  gbp_flags;
-    uint8_t  pad1[5];        /* Pad to 64 bits. */
+    uint8_t nsi;
+    ovs_be32 nsp;
+    ovs_be32 nshc1;
+    ovs_be32 nshc2;
+    ovs_be32 nshc3;
+    ovs_be32 nshc4;
     struct tun_metadata metadata;
 };
 
@@ -69,6 +74,12 @@ struct flow_tnl {
 
 /* Tunnel information is in userspace datapath format. */
 #define FLOW_TNL_F_UDPIF (1 << 4)
+#define FLOW_TNL_F_NSP (1 << 5)
+#define FLOW_TNL_F_NSI (1 << 6)
+#define FLOW_TNL_F_NSH_C1 (1 << 7)
+#define FLOW_TNL_F_NSH_C2 (1 << 8)
+#define FLOW_TNL_F_NSH_C3 (1 << 9)
+#define FLOW_TNL_F_NSH_C4 (1 << 10)
 
 /* Returns an offset to 'src' covering all the meaningful fields in 'src'. */
 static inline size_t
@@ -899,6 +910,9 @@ struct vxlanhdr {
     ovs_16aligned_be32 vx_vni;
 };
 
+/* VXLAN GPE UDP DST PORT */
+#define VXGPE_DST_PORT 4790
+
 #define VXLAN_FLAGS 0x08000000  /* struct vxlanhdr.vx_flags required value. */
 
 void format_ipv6_addr(char *addr_str, const struct in6_addr *addr);
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 4ed73a3..4bb9801 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -4032,7 +4032,6 @@ recirc_put_unroll_xlate(struct xlate_ctx *ctx)
     }
 }
 
-
 /* Copy remaining actions to the action_set to be executed after recirculation.
  * UNROLL_XLATE action is inserted, if not already done so, before actions that
  * may generate PACKET_INs from the current table and without matching another
@@ -4096,6 +4095,12 @@ recirc_unroll_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
         case OFPACT_METER:
         case OFPACT_SAMPLE:
         case OFPACT_DEBUG_RECIRC:
+        case OFPACT_SET_NSP:
+        case OFPACT_SET_NSI:
+        case OFPACT_SET_NSH_C1:
+        case OFPACT_SET_NSH_C2:
+        case OFPACT_SET_NSH_C3:
+        case OFPACT_SET_NSH_C4:
             break;
 
             /* These need not be copied for restoration. */
@@ -4284,6 +4289,30 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             flow->tunnel.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
             break;
 
+        case OFPACT_SET_NSP:
+            flow->tunnel.nsp = ofpact_get_SET_NSP(a)->nsp;
+            break;
+
+        case OFPACT_SET_NSI:
+            flow->tunnel.nsi = ofpact_get_SET_NSI(a)->nsi;
+            break;
+
+        case OFPACT_SET_NSH_C1:
+            flow->tunnel.nshc1 = ofpact_get_SET_NSH_C1(a)->nshc1;
+            break;
+
+        case OFPACT_SET_NSH_C2:
+            flow->tunnel.nshc2 = ofpact_get_SET_NSH_C2(a)->nshc2;
+            break;
+
+        case OFPACT_SET_NSH_C3:
+            flow->tunnel.nshc3 = ofpact_get_SET_NSH_C3(a)->nshc3;
+            break;
+
+        case OFPACT_SET_NSH_C4:
+            flow->tunnel.nshc4 = ofpact_get_SET_NSH_C4(a)->nshc4;
+            break;
+
         case OFPACT_SET_QUEUE:
             memset(&wc->masks.skb_priority, 0xff,
                    sizeof wc->masks.skb_priority);
diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
index b85c2b5..52e28fb 100644
--- a/ofproto/tunnel.c
+++ b/ofproto/tunnel.c
@@ -47,13 +47,25 @@ VLOG_DEFINE_THIS_MODULE(tunnel);
 
 struct tnl_match {
     ovs_be64 in_key;
+    ovs_be32 in_nsp;
+    ovs_be32 in_nshc1;
+    ovs_be32 in_nshc2;
+    ovs_be32 in_nshc3;
+    ovs_be32 in_nshc4;
     ovs_be32 ip_src;
     ovs_be32 ip_dst;
     odp_port_t odp_port;
     uint32_t pkt_mark;
+    uint8_t in_nsi;
     bool in_key_flow;
+    bool in_nsp_flow;
+    bool in_nshc1_flow;
+    bool in_nshc2_flow;
+    bool in_nshc3_flow;
+    bool in_nshc4_flow;
     bool ip_src_flow;
     bool ip_dst_flow;
+    bool in_nsi_flow;
 };
 
 struct tnl_port {
@@ -85,17 +97,41 @@ static struct fat_rwlock rwlock;
  *       (ip_dst_flow == false) or arrange for the destination IP to be matched
  *       as tunnel.ip_dst in the OpenFlow flow (ip_dst_flow == true).
  *
+ *     - in_nsp: A vport may match a specific NSH service path (in_nsp_flow ==
+ *       false) or arrange for the service path to be matched as tunnel.in_nsp
+ *       in the OpenFlow flow (in_nsp_flow == true).
+ *
+ *     - in_nsi: A vport may match a specific NSH service index (in_nsi_flow ==
+ *       false) or arrange for the service index to be matched as tunnel.in_nsi
+ *       in the OpenFlow flow (in_nsi_flow == true).
+ *
+ *     - in_nshc1: A vport may match a specific NSH_C1 (in_nshc1_flow ==
+ *       false) or arrange for the NSH_C1 to be matched as tunnel.in_nshc1
+ *       in the OpenFlow flow (in_nshc1_flow == true).
+ *
+ *     - in_nshc2: A vport may match a specific NSH_C2 (in_nshc1_flow ==
+ *       false) or arrange for the NSH_C2 to be matched as tunnel.in_nshc2
+ *       in the OpenFlow flow (in_nshc2_flow == true).
+ *
+ *     - in_nshc3: A vport may match a specific NSH_C3 (in_nshc1_flow ==
+ *       false) or arrange for the NSH_C3 to be matched as tunnel.in_nshc3
+ *       in the OpenFlow flow (in_nshc3_flow == true).
+ *
+ *     - in_nshc4: A vport may match a specific NSH_C4 (in_nshc1_flow ==
+ *       false) or arrange for the NSH_C4 to be matched as tunnel.in_nshc4
+ *       in the OpenFlow flow (in_nshc4_flow == true).
+ *
  *     - ip_src: A vport may match a specific IP source address (ip_src_flow ==
  *       false, ip_src != 0), wildcard all source addresses (ip_src_flow ==
  *       false, ip_src == 0), or arrange for the IP source address to be
  *       handled in the OpenFlow flow table (ip_src_flow == true).
  *
- * Thus, there are 2 * 2 * 3 == 12 possible ways a vport can match against a
- * tunnel packet.  We number the possibilities for each field in increasing
- * order as listed in each bullet above.  We order the 12 overall combinations
- * in lexicographic order considering in_key first, then ip_dst, then
- * ip_src. */
-#define N_MATCH_TYPES (2 * 2 * 3)
+ * Thus, there are 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 3 == 768 possible ways a vport can match
+ * against a tunnel packet.  We number the possibilities for each field in
+ * increasing order as listed in each bullet above.  We order the 768 overall
+ * combinations in lexicographic order considering in_key first, then ip_dst,
+ * then in_nsp, then in_nsi, then ip_src. */
+#define N_MATCH_TYPES (2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 3)
 
 /* The three possibilities (see above) for vport ip_src matches. */
 enum ip_src_type {
@@ -110,6 +146,10 @@ enum ip_src_type {
 static struct hmap *tnl_match_maps[N_MATCH_TYPES] OVS_GUARDED_BY(rwlock);
 static struct hmap **tnl_match_map(const struct tnl_match *);
 
+static unsigned int tnl_match_m_to_idx(const struct tnl_match *);
+static void tnl_match_idx_to_m(const struct flow *, unsigned int,
+    struct tnl_match *);
+
 static struct hmap ofport_map__ = HMAP_INITIALIZER(&ofport_map__);
 static struct hmap *ofport_map OVS_GUARDED_BY(rwlock) = &ofport_map__;
 
@@ -121,7 +161,10 @@ static struct tnl_port *tnl_find_exact(struct tnl_match *, struct hmap *)
     OVS_REQ_RDLOCK(rwlock);
 static struct tnl_port *tnl_find_ofport(const struct ofport_dpif *)
     OVS_REQ_RDLOCK(rwlock);
-
+static struct tnl_port *tnl_find_odp_port(odp_port_t odp_port)
+    OVS_REQ_RDLOCK(rwlock);
+static struct tnl_port *tnl_find_exact_odp_port(odp_port_t, struct hmap *)
+    OVS_REQ_RDLOCK(rwlock);
 static uint32_t tnl_hash(struct tnl_match *);
 static void tnl_match_fmt(const struct tnl_match *, struct ds *);
 static char *tnl_port_fmt(const struct tnl_port *) OVS_REQ_RDLOCK(rwlock);
@@ -161,12 +204,24 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
     tnl_port->change_seq = netdev_get_change_seq(tnl_port->netdev);
 
     tnl_port->match.in_key = cfg->in_key;
+    tnl_port->match.in_nsp = cfg->in_nsp;
+    tnl_port->match.in_nsi = cfg->in_nsi;
+    tnl_port->match.in_nshc1 = cfg->in_nshc1;
+    tnl_port->match.in_nshc2 = cfg->in_nshc2;
+    tnl_port->match.in_nshc3 = cfg->in_nshc3;
+    tnl_port->match.in_nshc4 = cfg->in_nshc4;
     tnl_port->match.ip_src = cfg->ip_src;
     tnl_port->match.ip_dst = cfg->ip_dst;
     tnl_port->match.ip_src_flow = cfg->ip_src_flow;
     tnl_port->match.ip_dst_flow = cfg->ip_dst_flow;
     tnl_port->match.pkt_mark = cfg->ipsec ? IPSEC_MARK : 0;
     tnl_port->match.in_key_flow = cfg->in_key_flow;
+    tnl_port->match.in_nsp_flow = cfg->in_nsp_flow;
+    tnl_port->match.in_nsi_flow = cfg->in_nsi_flow;
+    tnl_port->match.in_nshc1_flow = cfg->in_nshc1_flow;
+    tnl_port->match.in_nshc2_flow = cfg->in_nshc2_flow;
+    tnl_port->match.in_nshc3_flow = cfg->in_nshc3_flow;
+    tnl_port->match.in_nshc4_flow = cfg->in_nshc4_flow;
     tnl_port->match.odp_port = odp_port;
 
     map = tnl_match_map(&tnl_port->match);
@@ -437,6 +492,28 @@ tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow,
         flow->tunnel.ip_tos = cfg->tos;
     }
 
+    if (!cfg->out_key_flow) {
+        flow->tunnel.tun_id = cfg->out_key;
+    }
+    if (!cfg->out_nsp_flow) {
+        flow->tunnel.nsp = cfg->out_nsp;
+    }
+    if (!cfg->out_nsi_flow) {
+        flow->tunnel.nsi = cfg->out_nsi;
+    }
+    if (!cfg->out_nshc1_flow) {
+        flow->tunnel.nshc1 = cfg->out_nshc1;
+    }
+    if (!cfg->out_nshc2_flow) {
+        flow->tunnel.nshc2 = cfg->out_nshc2;
+    }
+    if (!cfg->out_nshc3_flow) {
+        flow->tunnel.nshc3 = cfg->out_nshc3;
+    }
+    if (!cfg->out_nshc4_flow) {
+        flow->tunnel.nshc4 = cfg->out_nshc4;
+    }
+
     /* ECN fields are always inherited. */
     if (is_ip_any(flow)) {
         wc->masks.nw_tos |= IP_ECN_MASK;
@@ -450,6 +527,12 @@ tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow,
 
     flow->tunnel.flags |= (cfg->dont_fragment ? FLOW_TNL_F_DONT_FRAGMENT : 0)
         | (cfg->csum ? FLOW_TNL_F_CSUM : 0)
+        | (cfg->out_nsp_present ? FLOW_TNL_F_NSP : 0)
+        | (cfg->out_nsi_present ? FLOW_TNL_F_NSI : 0)
+        | (cfg->out_nshc1_present ? FLOW_TNL_F_NSH_C1 : 0)
+        | (cfg->out_nshc2_present ? FLOW_TNL_F_NSH_C2 : 0)
+        | (cfg->out_nshc3_present ? FLOW_TNL_F_NSH_C3 : 0)
+        | (cfg->out_nshc4_present ? FLOW_TNL_F_NSH_C4 : 0)
         | (cfg->out_key_present ? FLOW_TNL_F_KEY : 0);
 
     if (pre_flow_str) {
@@ -512,57 +595,122 @@ tnl_find_exact(struct tnl_match *match, struct hmap *map)
 static struct tnl_port *
 tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock)
 {
-    enum ip_src_type ip_src;
-    int in_key_flow;
-    int ip_dst_flow;
-    int i;
-
-    i = 0;
-    for (in_key_flow = 0; in_key_flow < 2; in_key_flow++) {
-        for (ip_dst_flow = 0; ip_dst_flow < 2; ip_dst_flow++) {
-            for (ip_src = 0; ip_src < 3; ip_src++) {
-                struct hmap *map = tnl_match_maps[i];
-
-                if (map) {
-                    struct tnl_port *tnl_port;
-                    struct tnl_match match;
-
-                    memset(&match, 0, sizeof match);
-
-                    /* The apparent mix-up of 'ip_dst' and 'ip_src' below is
-                     * correct, because "struct tnl_match" is expressed in
-                     * terms of packets being sent out, but we are using it
-                     * here as a description of how to treat received
-                     * packets. */
-                    match.in_key = in_key_flow ? 0 : flow->tunnel.tun_id;
-                    match.ip_src = (ip_src == IP_SRC_CFG
-                                    ? flow->tunnel.ip_dst
-                                    : 0);
-                    match.ip_dst = ip_dst_flow ? 0 : flow->tunnel.ip_src;
-                    match.odp_port = flow->in_port.odp_port;
-                    match.pkt_mark = flow->pkt_mark;
-                    match.in_key_flow = in_key_flow;
-                    match.ip_dst_flow = ip_dst_flow;
-                    match.ip_src_flow = ip_src == IP_SRC_FLOW;
-
-                    tnl_port = tnl_find_exact(&match, map);
-                    if (tnl_port) {
-                        return tnl_port;
-                    }
-                }
-
-                i++;
+   int i;
+
+    for (i = 0; i < N_MATCH_TYPES; i++) {
+         struct hmap *map = tnl_match_maps[i];
+
+         if (map) {
+            struct tnl_port *tnl_port;
+            struct tnl_match match;
+
+            memset(&match, 0, sizeof match);
+
+            tnl_match_idx_to_m(flow, i, &match);
+            tnl_port = tnl_find_exact(&match, map);
+            if (tnl_port) {
+                return tnl_port;
             }
         }
-    }
+   }
 
     return NULL;
 }
 
-/* Returns a pointer to the 'tnl_match_maps' element corresponding to 'm''s
- * matching criteria. */
-static struct hmap **
-tnl_match_map(const struct tnl_match *m)
+/* Coverts a index to corresponding matching criteria 'm'. */
+static void
+tnl_match_idx_to_m(const struct flow *flow, unsigned int idx,
+                   struct tnl_match *m)
+{
+    enum ip_src_type ip_src;
+    bool in_key_flow;
+    bool ip_dst_flow;
+    bool in_nsp_flow;
+    bool in_nsi_flow;
+    bool in_nshc1_flow;
+    bool in_nshc2_flow;
+    bool in_nshc3_flow;
+    bool in_nshc4_flow;
+
+    if (!m)
+        return;
+
+    in_key_flow = (idx < (N_MATCH_TYPES / 2)) ? false : true;
+
+    if (idx >= (N_MATCH_TYPES / 2))
+        idx -= (N_MATCH_TYPES / 2);
+
+    ip_dst_flow = (idx < (N_MATCH_TYPES / (2 * 2))) ? false : true;
+
+    if (idx >= (N_MATCH_TYPES / (2 * 2)))
+        idx -= (N_MATCH_TYPES / (2 * 2));
+
+    in_nsp_flow = (idx < (N_MATCH_TYPES / (2 * 2 * 2))) ? false : true;
+
+    if (idx >= (N_MATCH_TYPES / (2 * 2 * 2)))
+        idx -= (N_MATCH_TYPES / (2 * 2 * 2));
+
+    in_nsi_flow = (idx < (N_MATCH_TYPES / (2 * 2 * 2 * 2))) ? false : true;
+
+    if (idx >= (N_MATCH_TYPES / (2 * 2 * 2 * 2)))
+        idx -= (N_MATCH_TYPES / (2 * 2 * 2 * 2));
+
+    in_nshc1_flow = (idx < (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2))) ? false : true;
+
+    if (idx >= (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2)))
+        idx -= (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2));
+
+    in_nshc2_flow = (idx < (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2))) ? false : true;
+
+    if (idx >= (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2)))
+        idx -= (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2));
+
+    in_nshc3_flow = (idx < (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2 * 2))) ? false : true;
+
+    if (idx >= (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2 * 2)))
+        idx -= (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2 * 2));
+
+    in_nshc4_flow = (idx < (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2 * 2 * 2))) ? false : true;
+
+    if (idx >= (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2 * 2 * 2)))
+        idx -= (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2 * 2 * 2));
+
+    ip_src = idx;
+
+    /* The apparent mix-up of 'ip_dst' and 'ip_src' below is
+     * correct, because "struct tnl_match" is expressed in
+     * terms of packets being sent out, but we are using it
+     * here as a description of how to treat received
+     * packets. */
+    m->in_key = in_key_flow ? 0 : flow->tunnel.tun_id;
+    m->ip_src = (ip_src == IP_SRC_CFG
+                    ? flow->tunnel.ip_dst
+                    : 0);
+    m->in_nsp = in_nsp_flow ? 0 : flow->tunnel.nsp;
+    m->in_nsi = in_nsi_flow ? 1 : flow->tunnel.nsi;
+    m->in_nshc1 = in_nshc1_flow ? 0 : flow->tunnel.nshc1;
+    m->in_nshc2 = in_nshc2_flow ? 0 : flow->tunnel.nshc2;
+    m->in_nshc3 = in_nshc3_flow ? 0 : flow->tunnel.nshc3;
+    m->in_nshc4 = in_nshc4_flow ? 0 : flow->tunnel.nshc4;
+    m->ip_dst = ip_dst_flow ? 0 : flow->tunnel.ip_src;
+    m->odp_port = flow->in_port.odp_port;
+    m->pkt_mark = flow->pkt_mark;
+    m->in_key_flow = in_key_flow;
+    m->ip_dst_flow = ip_dst_flow;
+    m->in_nsp_flow = in_nsp_flow;
+    m->in_nsi_flow = in_nsi_flow;
+    m->in_nshc1_flow = in_nshc1_flow;
+    m->in_nshc2_flow = in_nshc2_flow;
+    m->in_nshc3_flow = in_nshc3_flow;
+    m->in_nshc4_flow = in_nshc4_flow;
+
+    m->ip_src_flow = ip_src == IP_SRC_FLOW;
+
+}
+
+/* Returns a index corresponding to 'm''s matching criteria. */
+static unsigned int
+tnl_match_m_to_idx(const struct tnl_match *m)
 {
     enum ip_src_type ip_src;
 
@@ -570,7 +718,23 @@ tnl_match_map(const struct tnl_match *m)
               : m->ip_src ? IP_SRC_CFG
               : IP_SRC_ANY);
 
-    return &tnl_match_maps[6 * m->in_key_flow + 3 * m->ip_dst_flow + ip_src];
+    return (m->in_key_flow * (N_MATCH_TYPES / 2) +
+            m->ip_dst_flow * (N_MATCH_TYPES / (2 * 2)) +
+            m->in_nsp_flow * (N_MATCH_TYPES / (2 * 2 * 2)) +
+            m->in_nsi_flow * (N_MATCH_TYPES / (2 * 2 * 2 * 2)) +
+            m->in_nshc1_flow * (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2)) +
+            m->in_nshc2_flow * (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2)) +
+            m->in_nshc3_flow * (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2 * 2)) +
+            m->in_nshc4_flow * (N_MATCH_TYPES / (2 * 2 * 2 * 2 * 2 * 2 * 2 * 2)) +
+            ip_src);
+}
+
+/* Returns a pointer to the 'tnl_match_maps' element corresponding to 'm''s
+ * matching criteria. */
+static struct hmap **
+tnl_match_map(const struct tnl_match *m)
+{
+    return &tnl_match_maps[tnl_match_m_to_idx(m)];
 }
 
 static void
@@ -592,6 +756,37 @@ tnl_match_fmt(const struct tnl_match *match, struct ds *ds)
         ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key));
     }
 
+    if (match->in_nsp_flow) {
+        ds_put_cstr(ds, ", nsp=flow");
+    } else {
+        ds_put_format(ds, ", nsp=%#"PRIx32, ntohl(match->in_nsp));
+    }
+    if (match->in_nsi_flow) {
+        ds_put_cstr(ds, ", nsi=flow");
+    } else {
+        ds_put_format(ds, ", nsi=%"PRIu8, match->in_nsi);
+    }
+    if (match->in_nshc1_flow) {
+        ds_put_cstr(ds, ", nshc1=flow");
+    } else {
+        ds_put_format(ds, ", nshc1=%#"PRIx32, ntohl(match->in_nshc1));
+    }
+    if (match->in_nshc2_flow) {
+        ds_put_cstr(ds, ", nshc2=flow");
+    } else {
+        ds_put_format(ds, ", nshc2=%#"PRIx32, ntohl(match->in_nshc2));
+    }
+    if (match->in_nshc3_flow) {
+        ds_put_cstr(ds, ", nshc3=flow");
+    } else {
+        ds_put_format(ds, ", nshc3=%#"PRIx32, ntohl(match->in_nshc3));
+    }
+    if (match->in_nshc4_flow) {
+        ds_put_cstr(ds, ", nshc4=flow");
+    } else {
+        ds_put_format(ds, ", nshc4=%#"PRIx32, ntohl(match->in_nshc4));
+    }
+
     ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port);
     ds_put_format(ds, ", pkt mark=%"PRIu32, match->pkt_mark);
 }
@@ -635,6 +830,79 @@ tnl_port_fmt(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock)
         }
     }
 
+    if (cfg->out_nsp != cfg->in_nsp ||
+        cfg->out_nsp_present != cfg->in_nsp_present ||
+        cfg->out_nsp_flow != cfg->in_nsp_flow) {
+        ds_put_cstr(&ds, ", out_nsp=");
+        if (!cfg->out_nsp_present) {
+            ds_put_cstr(&ds, "none");
+        } else if (cfg->out_nsp_flow) {
+            ds_put_cstr(&ds, "flow");
+        } else {
+            ds_put_format(&ds, "%#"PRIx32, ntohl(cfg->out_nsp));
+        }
+    }
+    if (cfg->out_nsi != cfg->in_nsi ||
+        cfg->out_nsi_present != cfg->in_nsi_present ||
+        cfg->out_nsi_flow != cfg->in_nsi_flow) {
+        ds_put_cstr(&ds, ", out_nsi=");
+        if (!cfg->out_nsi_present) {
+            ds_put_cstr(&ds, "none");
+        } else if (cfg->out_nsi_flow) {
+            ds_put_cstr(&ds, "flow");
+        } else {
+            ds_put_format(&ds, "%"PRIu8, cfg->out_nsi);
+        }
+    }
+    if (cfg->out_nshc1 != cfg->in_nshc1 ||
+        cfg->out_nshc1_present != cfg->in_nshc1_present ||
+        cfg->out_nshc1_flow != cfg->in_nshc1_flow) {
+        ds_put_cstr(&ds, ", out_nshc1=");
+        if (!cfg->out_nshc1_present) {
+            ds_put_cstr(&ds, "none");
+        } else if (cfg->out_nshc1_flow) {
+            ds_put_cstr(&ds, "flow");
+        } else {
+            ds_put_format(&ds, "%#"PRIx32, ntohl(cfg->out_nshc1));
+        }
+    }
+    if (cfg->out_nshc2 != cfg->in_nshc2 ||
+        cfg->out_nshc2_present != cfg->in_nshc2_present ||
+        cfg->out_nshc2_flow != cfg->in_nshc2_flow) {
+        ds_put_cstr(&ds, ", out_nshc2=");
+        if (!cfg->out_nshc2_present) {
+            ds_put_cstr(&ds, "none");
+        } else if (cfg->out_nshc2_flow) {
+            ds_put_cstr(&ds, "flow");
+        } else {
+            ds_put_format(&ds, "%#"PRIx32, ntohl(cfg->out_nshc2));
+        }
+    }
+    if (cfg->out_nshc3 != cfg->in_nshc3 ||
+        cfg->out_nshc3_present != cfg->in_nshc3_present ||
+        cfg->out_nshc3_flow != cfg->in_nshc3_flow) {
+        ds_put_cstr(&ds, ", out_nshc3=");
+        if (!cfg->out_nshc3_present) {
+            ds_put_cstr(&ds, "none");
+        } else if (cfg->out_nshc3_flow) {
+            ds_put_cstr(&ds, "flow");
+        } else {
+            ds_put_format(&ds, "%#"PRIx32, ntohl(cfg->out_nshc3));
+        }
+    }
+    if (cfg->out_nshc4 != cfg->in_nshc4 ||
+        cfg->out_nshc4_present != cfg->in_nshc4_present ||
+        cfg->out_nshc4_flow != cfg->in_nshc4_flow) {
+        ds_put_cstr(&ds, ", out_nshc4=");
+        if (!cfg->out_nshc4_present) {
+            ds_put_cstr(&ds, "none");
+        } else if (cfg->out_nshc4_flow) {
+            ds_put_cstr(&ds, "flow");
+        } else {
+            ds_put_format(&ds, "%#"PRIx32, ntohl(cfg->out_nshc4));
+        }
+    }
+
     if (cfg->ttl_inherit) {
         ds_put_cstr(&ds, ", ttl=inherit");
     } else {
diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h
index 3bb76c5..3e704fb 100644
--- a/ofproto/tunnel.h
+++ b/ofproto/tunnel.h
@@ -55,5 +55,4 @@ int tnl_port_build_header(const struct ofport_dpif *ofport,
                           const struct eth_addr dmac,
                           const struct eth_addr smac,
                           ovs_be32 ip_src, struct ovs_action_push_tnl *data);
-
 #endif /* tunnel.h */
diff --git a/tests/ofproto.at b/tests/ofproto.at
index c012a34..fc19b10 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1531,7 +1531,7 @@ head_table () {
         actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
         supported on Set-Field: tun_id tun_src tun_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 dnl
 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl
-metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll
+metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll nsp nsi nshc1 nshc2 nshc3 nshc4
     matching:
       dp_hash: arbitrary mask
       recirc_id: exact match or wildcard
@@ -1662,6 +1662,12 @@ metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xr
       nd_target: arbitrary mask
       nd_sll: arbitrary mask
       nd_tll: arbitrary mask
+      nsp: arbitrary mask
+      nsi: arbitrary mask
+      nshc1: arbitrary mask
+      nshc2: arbitrary mask
+      nshc3: arbitrary mask
+      nshc4: arbitrary mask
 
 ' $1
 }
@@ -4068,7 +4074,7 @@ vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and ea
 vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm
 vconn|DBG|unix: received: OFPT_BARRIER_REQUEST:
 vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY:
-vconn|DBG|unix: received: NXST_FLOW request: 
+vconn|DBG|unix: received: NXST_FLOW request:
 vconn|DBG|unix: sent (Success): NXST_FLOW reply:
  idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8
  in_port=2,dl_src=00:66:77:88:99:aa actions=drop
diff --git a/tests/tunnel.at b/tests/tunnel.at
index f277f27..f43a07d 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -412,6 +412,121 @@ AT_CHECK([tail -1 stdout], [0],
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([tunnel - VXLAN-GPE NSH kernel space])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
+                    options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4790])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
+		br0 65534/100: (dummy)
+		p1 1/4790: (vxlan: dst_port=4790, remote_ip=1.1.1.1)
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel VXLAN-GPE NSH - encap - nsh/nsi/nshc kernel space])
+OVS_VSWITCHD_START([dnl
+    add-port br0 p1 -- set Interface p1 type=vxlan options:key=flow \
+        options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=1 \
+    -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
+        options:remote_ip=flow options:dst_port=4790 ofport_request=2 \
+    -- add-port br0 p3 -- set Interface p3 type=vxlan options:key=flow \
+        options:remote_ip=2.2.2.2 options:dst_port=4790 options:nsp=111 options:nsi=11 options:nshc1=11 options:nshc2=12 options:nshc3=13 options:nshc4=14 ofport_request=3 \
+    -- add-port br0 p4 -- set Interface p4 type=vxlan options:key=flow \
+        options:remote_ip=3.3.3.3 options:dst_port=4790 options:nsp=222 options:nsi=22 options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=4 \
+    -- add-port br0 p5 -- set Interface p5 type=vxlan options:key=flow \
+        options:remote_ip=4.4.4.4 options:dst_port=4790 options:nsp=flow options:nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=5 \
+    -- add-port br0 p6 -- set Interface p6 type=vxlan options:key=flow \
+        options:remote_ip=flow options:dst_port=4790 options:nsp=flow options:nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=6])
+
+OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
+ADD_OF_PORTS([br0], [90])
+AT_DATA([flows.txt], [dnl
+in_port=90 actions=resubmit:1,resubmit:2,resubmit:3,resubmit:4,resubmit:5
+in_port=1 actions=set_field:42->tun_id,output:1
+in_port=2 actions=set_field:3.3.3.3->tun_dst,output:2
+in_port=3 actions=output:3
+in_port=4 actions=set_nshc1:22,set_nshc2:23,set_nshc3:24,set_nshc4:25,output:4
+in_port=5 actions=set_nsp:333,set_nsi:33,set_nshc1:33,set_nshc2:34,set_nshc3:35,set_nshc4:36,output:5
+in_port=6 actions=set_field:5.5.5.5->tun_dst,set_nsp:444,set_nsi:44,set_nshc1:44,set_nshc2:45,set_nshc3:46,set_nshc4:47,output:6
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x2a,dst=1.1.1.1,ttl=64,nsi=1,flags(df|key|nsi))),4790,set(tunnel(tun_id=0x2a,dst=3.3.3.3,ttl=64,nsi=1,flags(df|key|nsi))),4790,set(tunnel(tun_id=0x2a,dst=2.2.2.2,ttl=64,nsp=6f,nsi=11,nshc1=b,nshc2=c,nshc3=d,nshc4=e,flags(df|key|nsp|nsi|nshc1|nshc2|nshc3|nshc4))),4790,set(tunnel(tun_id=0x2a,dst=3.3.3.3,ttl=64,nsp=de,nsi=22,nshc1=16,nshc2=17,nshc3=18,nshc4=19,flags(df|key|nsp|nsi|nshc1|nshc2|nshc3|nshc4))),4790,set(tunnel(tun_id=0x2a,dst=4.4.4.4,ttl=64,nsp=14d,nsi=33,nshc1=21,nshc2=22,nshc3=23,nshc4=24,flags(df|key|nsp|nsi|nshc1|nshc2|nshc3|nshc4))),4790
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - VXLAN-GPE NSH decap - decap-encap - remote_ip nsp nsi nshc* kernel space])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan options:key=flow \
+        options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=1 \
+    -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
+        options:remote_ip=2.2.2.2 options:dst_port=4790 options:nsp=111 options:nsi=11 options:nshc1=11 options:nshc2=12 options:nshc3=13 options:nshc4=14 ofport_request=2 \
+    -- add-port br0 p3 -- set Interface p3 type=vxlan options:key=flow \
+        options:remote_ip=3.3.3.3 options:dst_port=4790 options:nsp=flow options:nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=3 \
+    -- add-port br0 p4 -- set Interface p4 type=vxlan options:key=flow \
+        options:remote_ip=4.4.4.4 options:dst_port=4790 options:in_nsp=221 options:out_nsp=flow options:in_nsi=3 options:out_nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=4 \
+    -- add-port br0 p5 -- set Interface p5 type=vxlan options:key=flow \
+        options:remote_ip=flow options:dst_port=4790 options:nsp=flow options:nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=5])
+AT_DATA([flows.txt], [dnl
+priority=16,in_port=1,actions=IN_PORT
+priority=16,in_port=2,actions=IN_PORT
+priority=16,in_port=3,actions=set_nsp:111,set_nsi=11,set_nshc1:22,set_nshc2:23,set_nshc3:24,set_nshc4:25,IN_PORT
+priority=16,in_port=4,actions=set_nsp:222,set_nsi=22,set_nshc1:32,set_nshc2:33,set_nshc3:34,set_nshc4:35,IN_PORT
+priority=16,in_port=5,nsp=311,nsi=31,actions=set_nsp:322,set_nsi=33,set_nshc1:42,set_nshc2:43,set_nshc3:44,set_nshc4:45,set_field:6.6.6.6->tun_dst,IN_PORT
+])
+OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
+		br0 65534/100: (dummy)
+		p1 1/4790: (vxlan: dst_port=4790, key=flow, remote_ip=1.1.1.1)
+		p2 2/4790: (vxlan: dst_port=4790, key=flow, nshc1=0xb, nshc2=0xc, nshc3=0xd, nshc4=0xe, nsi=11, nsp=0x6f, remote_ip=2.2.2.2)
+		p3 3/4790: (vxlan: dst_port=4790, key=flow, nshc1=flow, nshc2=flow, nshc3=flow, nshc4=flow, nsi=flow, nsp=flow, remote_ip=3.3.3.3)
+		p4 4/4790: (vxlan: dst_port=4790, in_nsi=3, in_nsp=0xdd, key=flow, nshc1=flow, nshc2=flow, nshc3=flow, nshc4=flow, out_nsi=flow, out_nsp=flow, remote_ip=4.4.4.4)
+		p5 5/4790: (vxlan: dst_port=4790, key=flow, nshc1=flow, nshc2=flow, nshc3=flow, nshc4=flow, nsi=flow, nsp=flow, remote_ip=flow)
+])
+
+dnl remote_ip p1
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=1.2.3.4,nsi=1,ttl=64,flags()),in_port(4790),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,dst=1.1.1.1,ttl=64,nsi=1,flags(df|key|nsi))),4790
+])
+
+dnl remote_ip nsp nsi nshc*  p2
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=2.2.2.2,dst=1.2.3.4,nsi=11,nsp=111,nshc1=11,nshc2=12,nshc3=13,nshc4=14,ttl=64,flags()),in_port(4790),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,dst=2.2.2.2,ttl=64,nsp=6f,nsi=11,nshc1=b,nshc2=c,nshc3=d,nshc4=e,flags(df|key|nsp|nsi|nshc1|nshc2|nshc3|nshc4))),4790
+])
+
+dnl remote_ip nsp=flow nsi=flow nshc*=flow p3
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=3.3.3.3,dst=1.2.3.4,ttl=64,flags()),in_port(4790),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,dst=3.3.3.3,ttl=64,nsp=6f,nsi=11,nshc1=16,nshc2=17,nshc3=18,nshc4=19,flags(df|key|nsp|nsi|nshc1|nshc2|nshc3|nshc4))),4790
+])
+
+dnl remote_ip nshc*=flow p4
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=4.4.4.4,dst=1.2.3.4,nsp=221,nsi=3,ttl=64,flags()),in_port(4790),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,dst=4.4.4.4,ttl=64,nsp=de,nsi=22,nshc1=20,nshc2=21,nshc3=22,nshc4=23,flags(df|key|nsp|nsi|nshc1|nshc2|nshc3|nshc4))),4790
+])
+
+dnl remote_ip  nshc*=flow p4  not matched
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=4.4.4.4,dst=1.2.3.4,nsp=223,nsi=3,ttl=64,flags()),in_port(4790),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: drop
+])
+
+dnl remote_ip remote_ip=flow nsp=flow nsi=flow nshc*=flow p5
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=5.5.5.5,dst=1.2.3.4,nsp=311,nsi=31,ttl=64,flags()),in_port(4790),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,dst=6.6.6.6,ttl=64,nsp=142,nsi=33,nshc1=2a,nshc2=2b,nshc3=2c,nshc4=2d,flags(df|key|nsp|nsi|nshc1|nshc2|nshc3|nshc4))),4790
+])
+
+OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"])
+AT_CLEANUP
+
 AT_SETUP([tunnel - Geneve metadata])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
                     options:remote_ip=1.1.1.1 ofport_request=1 \
