[ovs-dev,1/7] vxlan-gpe-nsh: decap and encap in kernel dataplane and control plane.
diff mbox

Message ID 1443606444-12269-2-git-send-email-mengke.liu@intel.com
State Changes Requested
Headers show

Commit Message

mengke Sept. 30, 2015, 9:47 a.m. UTC
NSH is designed for implementation of service function chaining. The patch add
VxLAN-GPE NSH decapsulation and encapsulation implementation at data plane 
level in kernel space and rules related with NSH match and actions at control
plane level. 

The VXLAN-GPE NSH type are enabled in this patch. The design is based on basic 
VxLAN implementation. The UDP port 4790 is used for VXLAN-GPE NSH type. When 
VxLAN-GPE NSH packets are received from VxLAN-GPE NSH port, the decapsulation 
will be implemented and the fields related with NSH will be parsed. When 
packets are sent to VxLAN-GPE NSH port, the encapsulation will be implemented 
according to the VxLAN-GPE NSH configuration and related rules.

Notes:
(1) port 4790 is for VxLAN-GPE NSH extension.
(2) new options for VxLAN-GPE NSH: service path header(nsp, nsi) and context
header with NSH MD-type 1(nshc1, nshc2, nshc3, nshc4). These fields have 
fixed/flow setting: fixed value indicates input value in related field must be
fixed value detected by this port and output value in related field will be set
as fixed value when encapsulation is implemented; flow value indicates the
input value is checked by the openflow rule and output value is set by the
openflow rule.
(3) new related actions: Add 6 new actions for NSH set fields: set_nsp, 
set_nsi,set_nshc1,set_nshc1,set_nshc3,set_nshc4

Signed-off-by: Pritesh Kothari <pritkoth@cisco.com>
Signed-off-by: Ricky Li <ricky.li@intel.com>
Signed-off-by: Mengke Liu <mengke.liu@intel.com>
---
 datapath/flow.h                                   |  30 +-
 datapath/flow_netlink.c                           |  70 ++++
 datapath/linux/Modules.mk                         |   1 +
 datapath/linux/compat/include/linux/openvswitch.h |   6 +
 datapath/linux/compat/include/net/ip_tunnels.h    |   8 +
 datapath/linux/compat/include/net/nsh.h           | 103 ++++++
 datapath/linux/compat/include/net/vxlan.h         |  20 +-
 datapath/linux/compat/vxlan.c                     |  97 +++++-
 datapath/vport-geneve.c                           |   2 +-
 datapath/vport-gre.c                              |   2 +-
 datapath/vport-lisp.c                             |   2 +-
 datapath/vport-stt.c                              |   2 +-
 datapath/vport-vxlan.c                            |  15 +-
 datapath/vport.c                                  |   5 +
 lib/flow.c                                        |  48 +++
 lib/match.c                                       |  90 ++++++
 lib/match.h                                       |  14 +
 lib/meta-flow.c                                   | 135 ++++++++
 lib/meta-flow.h                                   | 102 ++++++
 lib/netdev-vport.c                                | 289 ++++++++++++++++-
 lib/netdev.h                                      |  49 +++
 lib/nx-match.c                                    |   6 +
 lib/odp-util.c                                    | 147 ++++++++-
 lib/odp-util.h                                    |   8 +-
 lib/ofp-actions.c                                 | 353 +++++++++++++++++++-
 lib/ofp-actions.h                                 |  48 +++
 lib/ofp-parse.c                                   |  13 +
 lib/ofp-parse.h                                   |   1 +
 lib/packets.h                                     |  16 +-
 ofproto/ofproto-dpif-xlate.c                      |  31 +-
 ofproto/tunnel.c                                  | 374 +++++++++++++++++++---
 ofproto/tunnel.h                                  |   1 -
 tests/ofproto.at                                  |  10 +-
 tests/tunnel.at                                   | 115 +++++++
 34 files changed, 2126 insertions(+), 87 deletions(-)
 create mode 100644 datapath/linux/compat/include/net/nsh.h

Patch
diff mbox

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 \