diff --git a/ovs-nsh/Dockerfile b/ovs-nsh/Dockerfile
index 9d23652..54e54a9 100644
--- a/ovs-nsh/Dockerfile
+++ b/ovs-nsh/Dockerfile
@@ -2,26 +2,31 @@
FROM ubuntu:14.04.3
+ENV HOME /root
RUN rm -rf /lib/modules
RUN apt-get update
-RUN apt-get install -y software-properties-common python-software-properties \
- make python-setuptools python-all dpkg-dev debhelper \
- fuseiso git genisoimage bind9-host wget curl lintian tmux lxc iptables \
- ca-certificates sudo apt-utils lsb-release libtool autoconf automake build-essential fakeroot libssl-dev graphviz dh-autoreconf python-qt4 python-twisted-conch python-zopeinterface linux-headers-3.13.0-76-generic
+RUN apt-get install -y software-properties-common python-software-properties\
+ make python-setuptools apt-utils autoconf automake bind9-host\
+ build-essential bzip2 ca-certificates curl debhelper dh-autoreconf\
+ dpkg-dev fakeroot fuseiso genisoimage git graphviz iptables\
+ libssl-dev libtool lintian python-flake8 dh-python\
+ lsb-release lxc openssl procps python-all python-qt4 python-setuptools\
+ python-six python-twisted-conch python-zopeinterface python3-six\
+ sudo tmux wget linux-headers-3.13.0-85-generic
-RUN ln -s /lib/modules/3.13.0-76-generic /lib/modules/`uname -r`
+RUN ln -s /lib/modules/3.13.0-85-generic /lib/modules/$(uname -r)
-RUN apt-get install -y pkg-config unzip liblua5.2-dev libpcap-dev libedit-dev libncurses5-dev libncursesw5-dev
+RUN apt-get install -y pkg-config unzip liblua5.2-dev libpcap0.8-dev libcap-ng-dev libedit-dev libncurses5-dev libncursesw5-dev
RUN echo "ALL ALL=NOPASSWD: ALL" > /etc/sudoers.d/open-sudo
RUN chmod 0440 /etc/sudoers.d/open-sudo
-ADD ./patches /patches
+ADD ./patches /root/patches
-ADD ./build-ovs-nsh.sh /build-ovs-nsh.sh
-RUN chmod +x /build-ovs-nsh.sh
-RUN /build-ovs-nsh.sh
+ADD ./build-ovs-nsh.sh /root/build-ovs-nsh.sh
+RUN chmod +x /root/build-ovs-nsh.sh
+RUN /root/build-ovs-nsh.sh
-ADD ./build-ovs-nsh-dpdk.sh /build-ovs-nsh-dpdk.sh
-RUN chmod +x /build-ovs-nsh-dpdk.sh
-RUN /build-ovs-nsh-dpdk.sh
+ADD ./build-ovs-nsh-dpdk.sh /root/build-ovs-nsh-dpdk.sh
+RUN chmod +x /root/build-ovs-nsh-dpdk.sh
+RUN /root/build-ovs-nsh-dpdk.sh
diff --git a/ovs-nsh/build-ovs-nsh-dpdk.sh b/ovs-nsh/build-ovs-nsh-dpdk.sh
index 1f9e7cc..481ec4b 100755
--- a/ovs-nsh/build-ovs-nsh-dpdk.sh
+++ b/ovs-nsh/build-ovs-nsh-dpdk.sh
@@ -1,13 +1,17 @@
#!/bin/bash
-DPDK_VER=2.1.0
-
-OVS_COMMIT=121daded51b9798fe3722824b27a05c16806cbd1
-RTE_TARGET=x86_64-native-linuxapp-gcc
-PATCHES="060679 060680 060681 060682 060683 060684 060685"
-URL_OVS=https://github.com/openvswitch/ovs.git
+DPDK_VER=2.2.0
URL_DPDK=http://dpdk.org/browse/dpdk/snapshot/dpdk-${DPDK_VER}.tar.gz
+export RTE_SDK=/root/dpdk-${DPDK_VER}
+export RTE_TARGET=x86_64-native-linuxapp-gcc
+export DPDK_BUILD=${RTE_SDK}/${RTE_TARGET}
+
+OVS_COMMIT=7d433ae57ebb90cd68e8fa948a096f619ac4e2d8
+URL_OVS=https://github.com/openvswitch/ovs.git
+PATCHES="eeaf57e bf1e7ff 21bd423 17a6124 299fc5b"
+
+cd $HOME
wget ${URL_DPDK}
tar -xzvf dpdk-${DPDK_VER}.tar.gz
cd dpdk-${DPDK_VER}
@@ -15,36 +19,37 @@ sed -i -e 's/CONFIG_RTE_LIBRTE_VHOST=n/CONFIG_RTE_LIBRTE_VHOST=y/' \
-e 's/CONFIG_RTE_BUILD_COMBINE_LIBS=n/CONFIG_RTE_BUILD_COMBINE_LIBS=y/' \
-e 's/CONFIG_RTE_PKTMBUF_HEADROOM=128/CONFIG_RTE_PKTMBUF_HEADROOM=256/' \
config/common_linuxapp
-cd /
+cd $HOME
tar -czvf dpdk-${DPDK_VER}.tar.gz dpdk-${DPDK_VER}
cd dpdk-${DPDK_VER}
make install T=${RTE_TARGET}
find . | grep "\.o$" | xargs rm -rf
-cd /
+cd $HOME
tar czvf dpdk-${DPDK_VER}.bin.tar.gz dpdk-${DPDK_VER}
+cd $HOME
+
git clone ${URL_OVS} openvswitch-dpdk
+
cd openvswitch-dpdk
-git checkout ${OVS_COMMIT} -b development
+git checkout ${OVS_COMMIT} -b yyang13
for patch in ${PATCHES}
do
- patch -p1 < /patches/${patch}.patch
+ patch -p1 < ${HOME}/patches/${patch}.patch
done
-export RTE_SDK=/dpdk-${DPDK_VER}
-export DPDK_BUILD=${RTE_SDK}/${RTE_TARGET}
+
./boot.sh
./configure --with-dpdk=$DPDK_BUILD
-sed -i "s?set ovs-vswitchd unix?set ovs-vswitchd --dpdk -c 0x1 -n 4 -- unix?" utilities/ovs-ctl.in;sed -i "s?configure --with-linux?configure --with-dpdk=/dpdk-2.1.0/x86_64-native-linuxapp-gcc --with-linux?" debian/dkms.conf.in;sed -i "s?configure --with-linux?configure --with-dpdk=/dpdk-2.1.0/x86_64-native-linuxapp-gcc --with-linux?" debian/rules.modules;sed -i "s?configure --?configure -- --with-dpdk=/dpdk-2.1.0/x86_64-native-linuxapp-gcc?" debian/rules;make dist;tar -xzf openvswitch-2.4.90.tar.gz;
-cd openvswitch-2.4.90;DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary
+sed -i "s?set ovs-vswitchd unix?set ovs-vswitchd --dpdk -c 0x1 -n 4 -- unix?" utilities/ovs-ctl.in;sed -i "s?configure --with-linux?configure --with-dpdk=$DPDK_BUILD --with-linux?" debian/dkms.conf.in;sed -i "s?configure --with-linux?configure --with-dpdk=$DPDK_BUILD --with-linux?" debian/rules.modules;sed -i "s?configure --?configure -- --with-dpdk=$DPDK_BUILD?" debian/rules;make dist;tar -xzf openvswitch-2.5.90.tar.gz;
+cd openvswitch-2.5.90;DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary
-cd /
+cd $HOME
wget https://01.org/sites/default/files/downloads/intel-data-plane-performance-demonstrators/dppd-prox-v021.zip
unzip dppd-prox-v021.zip
-export RTE_SDK=/dpdk-${DPDK_VER}
-export RTE_TARGET=x86_64-native-linuxapp-gcc
-cd /dppd-PROX-v021
-export DPPD_DIR=`pwd`; make
+
+cd dppd-PROX-v021
+export DPPD_DIR=$(pwd); make
find . | grep "\.o$" | xargs rm -rf
-cd /
-tar czvf dppd-prox-v021.bin.tar.gz dppd-PROX-v021
+cd $HOME
+tar -czvf dppd-prox-v021.bin.tar.gz dppd-PROX-v021
diff --git a/ovs-nsh/build-ovs-nsh.sh b/ovs-nsh/build-ovs-nsh.sh
index 9a5fc9c..b0aede6 100755
--- a/ovs-nsh/build-ovs-nsh.sh
+++ b/ovs-nsh/build-ovs-nsh.sh
@@ -1,16 +1,30 @@
#!/bin/bash
-OVS_COMMIT=121daded51b9798fe3722824b27a05c16806cbd1
-PATCHES="060679 060680 060681 060682 060683 060684 060685"
+OVS_COMMIT=7d433ae57ebb90cd68e8fa948a096f619ac4e2d8
URL_OVS=https://github.com/openvswitch/ovs.git
+PATCHES="eeaf57e bf1e7ff 21bd423 17a6124 299fc5b"
+
+cd $HOME
+
git clone ${URL_OVS} openvswitch
+
cd openvswitch
-git checkout ${OVS_COMMIT} -b development
+git checkout ${OVS_COMMIT} -b yyang13
for patch in ${PATCHES}
do
- patch -p1 < /patches/${patch}.patch
+ patch -p1 < ${HOME}/patches/${patch}.patch
done
-./boot.sh;./configure;make dist;tar -xzf openvswitch-2.4.90.tar.gz
-cd openvswitch-2.4.90;dpkg-checkbuilddeps;DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary
+./boot.sh
+./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --with-linux=/lib/modules/$(uname -r)/build
+make dist
+OVSTBALL=$(echo openvswitch-*.tar.gz)
+OVSART='openvswitch-2.5.90'
+
+
+tar -xzf ${HOME}/openvswitch/$OVSTBALL
+cd $OVSART
+dpkg-checkbuilddeps
+DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary
+
diff --git a/ovs-nsh/patches/060679.patch b/ovs-nsh/patches/060679.patch
deleted file mode 100644
index 635cf1d..0000000
--- a/ovs-nsh/patches/060679.patch
+++ /dev/null
@@ -1,3187 +0,0 @@
-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 at cisco.com>
-Signed-off-by: Ricky Li <ricky.li at intel.com>
-Signed-off-by: Mengke Liu <mengke.liu at 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
-
-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
-+#include
-+
-+
-+/**
-+ * 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
- #include
- #include
-+#include
- #include
-
-+
- #include
-
- #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
- #include
- #include
-+#include
-
- #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 \
---
-1.9.3
-
diff --git a/ovs-nsh/patches/060680.patch b/ovs-nsh/patches/060680.patch
deleted file mode 100644
index 538d2df..0000000
--- a/ovs-nsh/patches/060680.patch
+++ /dev/null
@@ -1,788 +0,0 @@
-at data plane level in user space and modify the related codes at control plane
-level in user space.
-
-The design is based on basic VxLAN impletation. When packets are received at
-data plane level in user space, function 'dp_netdev_input' will be called for
-processing the packets at data plane level in user space.
-
-When VXLAN-GPE NSH packets are received, decapsulation will be implemented. For
-the first time, the packets are sent to control plane by function 'upcall_cb',
-and tunnel port will be lookuped by matching the UDP port which is 4790 for
-VxLAN-GPE NSH port, if VxLAN-GPE NSH tunnel port are matched
-successfully, the tunnel pop action will be appended and implemented at data
-plane level, and the NSH related field will be parsed, then packets will be
-reprocessed by function 'dp_netdev_input'.
-
-When original packets are sent to VxLAN-GPE NSH port, the encapsulation will
-be implemented. For the first time, in the control plane the tunnel
-tunnel_push_data are built according to VxLAN-GPE NSH port configuration and
-related rules, then the tunnel push actions are appended and implemented at
-data plane level. Finally packets will be reprocessed by function
-'dp_netdev_input'.
-
-Signed-off-by: Ricky Li <ricky.li at intel.com>
-Signed-off-by: Mengke Liu <mengke.liu at intel.com>
----
- lib/netdev-vport.c | 166 +++++++++++++++++++++++++++++++++++-----
- lib/odp-util.c | 175 ++++++++++++++++++++++++++++++-------------
- lib/packets.h | 115 +++++++++++++++++++++++++++-
- ofproto/ofproto-dpif-xlate.c | 1 -
- tests/tunnel.at | 118 +++++++++++++++++++++++++++++
- 5 files changed, 500 insertions(+), 75 deletions(-)
-
-diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
-index 3f85386..a0a4da2 100644
---- a/lib/netdev-vport.c
-+++ b/lib/netdev-vport.c
-@@ -67,6 +67,12 @@ static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5);
- sizeof(struct udp_header) + \
- sizeof(struct genevehdr))
-
-+#define VXNSH_HLEN (sizeof(struct eth_header) + \
-+ sizeof(struct ip_header) + \
-+ sizeof(struct udp_header) + \
-+ sizeof(struct vxgpehdr) + \
-+ sizeof(struct nshhdr))
-+
- #define DEFAULT_TTL 64
-
- struct netdev_vport {
-@@ -1462,29 +1468,69 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
- {
- struct pkt_metadata *md = &packet->md;
- struct flow_tnl *tnl = &md->tunnel;
-- struct vxlanhdr *vxh;
-+ struct udp_header *udp;
-
- pkt_metadata_init_tnl(md);
- if (VXLAN_HLEN > dp_packet_size(packet)) {
- return EINVAL;
- }
-
-- vxh = udp_extract_tnl_md(packet, tnl);
-- if (!vxh) {
-- return EINVAL;
-+ udp = ip_extract_tnl_md(packet, tnl);
-+ if (!udp) {
-+ return EINVAL;;
- }
-
-- if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
-- (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
-- VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
-- ntohl(get_16aligned_be32(&vxh->vx_flags)),
-- ntohl(get_16aligned_be32(&vxh->vx_vni)));
-- return EINVAL;
-- }
-- tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
-- tnl->flags |= FLOW_TNL_F_KEY;
-+ if (ntohs(udp->udp_dst) == VXGPE_DST_PORT) {
-+
-+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
-+
-+ if (get_16aligned_be32(&vxg->vx_vni) & htonl(0xff)) {
-+ VLOG_WARN_RL(&err_rl, "invalid vxlan-gpe vni=%#x\n",
-+ ntohl(get_16aligned_be32(&vxg->vx_vni)));
-+ return EINVAL;;
-+ }
-+
-+ tnl->tp_src = udp->udp_src;
-+ tnl->tp_dst = udp->udp_dst;
-+ tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxg->vx_vni)) >> 8);
-+
-+ if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
-+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
-+
-+ tnl->nsp = nsh->b.b2 << 8;
-+ tnl->nsi = nsh->b.svc_idx;
-+ tnl->nshc1 = nsh->c.nshc1;
-+ tnl->nshc2 = nsh->c.nshc2;
-+ tnl->nshc3 = nsh->c.nshc3;
-+ tnl->nshc4 = nsh->c.nshc4;
-+ tnl->flags |= FLOW_TNL_F_NSP;
-+ tnl->flags |= FLOW_TNL_F_NSI;
-+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
-+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
-+
-+ dp_packet_reset_packet(packet, VXNSH_HLEN);
-+ } else {
-+ VLOG_WARN("Unsupported vxlan GPE + NSH format!");
-+ return EINVAL;;
-+ }
-+
-+ } else {
-+
-+ struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
-
-- dp_packet_reset_packet(packet, VXLAN_HLEN);
-+ if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
-+ (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
-+ VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
-+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
-+ ntohl(get_16aligned_be32(&vxh->vx_vni)));
-+ return EINVAL;;
-+ }
-+
-+ tnl->tp_src = udp->udp_src;
-+ tnl->tp_dst = udp->udp_dst;
-+ tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
-+ dp_packet_reset_packet(packet, VXLAN_HLEN);
-+ }
-
- return 0;
- }
-@@ -1496,23 +1542,103 @@ netdev_vxlan_build_header(const struct netdev *netdev,
- {
- struct netdev_vport *dev = netdev_vport_cast(netdev);
- struct netdev_tunnel_config *tnl_cfg;
-- struct vxlanhdr *vxh;
-+ struct ip_header *ip;
-+ struct udp_header *udp;
-+ bool isnsh = false;
-
- /* XXX: RCUfy tnl_cfg. */
- ovs_mutex_lock(&dev->mutex);
- tnl_cfg = &dev->tnl_cfg;
-
-- vxh = udp_build_header(tnl_cfg, tnl_flow, data);
-+ ip = ip_hdr(data->header);
-+ ip->ip_proto = IPPROTO_UDP;
-+
-+ udp = (struct udp_header *) (ip + 1);
-+ udp->udp_dst = tnl_cfg->dst_port;
-+
-+ if (tnl_flow->tunnel.flags & FLOW_TNL_F_CSUM) {
-+ /* Write a value in now to mark that we should compute the checksum
-+ * later. 0xffff is handy because it is transparent to the
-+ * calculation. */
-+ udp->udp_csum = htons(0xffff);
-+ }
-+
-+ if (ntohs(udp->udp_dst) == VXGPE_DST_PORT){
-+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
-
-- put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
-- put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
-+ memset(vxg, 0, sizeof *vxg);
-+ vxg->i = 0x01;
-+ vxg->p = 0x01;
-+ vxg->ver = 0x01;
-+ vxg->proto = VXG_P_NSH;
-+ put_16aligned_be32(&vxg->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
-+
-+ if (vxg->p && vxg->proto == VXG_P_NSH){
-+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
-+
-+ memset(nsh, 0, sizeof *nsh);
-+ nsh->b.ver = 0x01;
-+ nsh->b.len = 6;
-+ nsh->b.mdtype = NSH_M_TYPE1;
-+ nsh->b.proto = NSH_P_ETHERNET;
-+
-+ nsh->b.b2 = tnl_flow->tunnel.nsp >> 8;
-+ nsh->b.svc_idx = tnl_flow->tunnel.nsi;
-+
-+ nsh->c.nshc1 = tnl_flow->tunnel.nshc1;
-+ nsh->c.nshc2 = tnl_flow->tunnel.nshc2;
-+ nsh->c.nshc3 = tnl_flow->tunnel.nshc3;
-+ nsh->c.nshc4 = tnl_flow->tunnel.nshc4;
-+
-+ isnsh = true;
-+ }
-+
-+ } else {
-+ struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
-+ put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
-+ put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
-+ }
-
- ovs_mutex_unlock(&dev->mutex);
-- data->header_len = VXLAN_HLEN;
-+
-+ if(isnsh)
-+ data->header_len = VXNSH_HLEN;
-+ else
-+ data->header_len = VXLAN_HLEN;
- data->tnl_type = OVS_VPORT_TYPE_VXLAN;
-+
- return 0;
- }
-
-+static void
-+netdev_vxlan_push_header(struct dp_packet *packet,
-+ const struct ovs_action_push_tnl *data)
-+{
-+ int ip_tot_size;
-+ int size = data->header_len;
-+ const void *header = data->header;
-+ struct udp_header *udp;
-+
-+ udp = push_ip_header(packet, header, size, &ip_tot_size);
-+
-+ /* set udp src port */
-+ udp->udp_src = get_src_port(packet);
-+ udp->udp_len = htons(ip_tot_size - sizeof (struct ip_header));
-+ /* udp_csum is zero */
-+
-+ if (udp->udp_csum) {
-+ uint32_t csum = packet_csum_pseudoheader(ip_hdr(dp_packet_data(packet)));
-+
-+ csum = csum_continue(csum, udp,
-+ ip_tot_size - sizeof (struct ip_header));
-+ udp->udp_csum = csum_finish(csum);
-+
-+ if (!udp->udp_csum) {
-+ udp->udp_csum = htons(0xffff);
-+ }
-+ }
-+}
-+
- static int
- netdev_geneve_pop_header(struct dp_packet *packet)
- {
-@@ -1736,7 +1862,7 @@ netdev_vport_tunnel_register(void)
- netdev_gre_pop_header),
- TUNNEL_CLASS("ipsec_gre", "gre_sys", NULL, NULL, NULL),
- TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header,
-- push_udp_header,
-+ netdev_vxlan_push_header,
- netdev_vxlan_pop_header),
- TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL),
- TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL),
-diff --git a/lib/odp-util.c b/lib/odp-util.c
-index e8bc86d..1696f77 100644
---- a/lib/odp-util.c
-+++ b/lib/odp-util.c
-@@ -468,12 +468,42 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
-
- if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
- const struct vxlanhdr *vxh;
--
-- vxh = format_udp_tnl_push_header(ds, ip);
--
-- ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
-- ntohl(get_16aligned_be32(&vxh->vx_flags)),
-- ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
-+ const struct udp_header *udp;
-+ const struct vxgpehdr *vxg;
-+
-+ /* UDP */
-+ udp = (const struct udp_header *) (ip + 1);
-+ ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16",csum=0x%"PRIx16"),",
-+ ntohs(udp->udp_src), ntohs(udp->udp_dst),
-+ ntohs(udp->udp_csum));
-+
-+ /* VxLan & VxLan GPE(UDP port: 4790) */
-+ if (ntohs(udp->udp_dst) == 4790) {
-+ vxg = (const struct vxgpehdr *) (udp + 1);
-+
-+ ds_put_format(ds, "vxlangpe(vni=0x%"PRIx32",",
-+ ntohl(get_16aligned_be32(&vxg->vx_vni)));
-+ ds_put_format(ds, "proto=%"PRIu8"),", vxg->proto);
-+ if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
-+ const struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
-+
-+ /* NSH */
-+ ds_put_format(ds, "nsh(mdtype=%"PRIu8",proto=%"PRIu8",",
-+ nsh->b.mdtype, nsh->b.proto);
-+ ds_put_format(ds, "nsp=%"PRIx32",nsi=%"PRIu8",",
-+ nsh->b.b2 & 0x00FFFFFF, nsh->b.svc_idx);
-+ ds_put_format(ds, "nshc1=%"PRIx32",nshc2=%"PRIx32",",
-+ ntohl(nsh->c.nshc1), ntohl(nsh->c.nshc2));
-+ ds_put_format(ds, "nshc3=%"PRIx32",nshc4=%"PRIx32",",
-+ ntohl(nsh->c.nshc3), ntohl(nsh->c.nshc4));
-+ ds_put_format(ds, ")");
-+ }
-+ } else {
-+ vxh = (const struct vxlanhdr *) (udp + 1);
-+ ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
-+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
-+ ntohl(get_16aligned_be32(&vxh->vx_vni))>>8);
-+ }
- } else if (data->tnl_type == OVS_VPORT_TYPE_GENEVE) {
- const struct genevehdr *gnh;
-
-@@ -490,8 +520,8 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
- ds, false);
- ds_put_char(ds, ')');
- }
--
- ds_put_char(ds, ')');
-+
- } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
- const struct gre_base_hdr *greh;
- ovs_16aligned_be32 *options;
-@@ -504,7 +534,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
- ntohs(greh->flags), ntohs(greh->protocol));
- options = (ovs_16aligned_be32 *)(greh + 1);
- if (greh->flags & htons(GRE_CSUM)) {
-- ds_put_format(ds, ",csum=0x%"PRIx16, ntohs(*((ovs_be16 *)options)));
-+ ds_put_format(ds, ",csum=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
- options++;
- }
- if (greh->flags & htons(GRE_KEY)) {
-@@ -791,8 +821,10 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
- struct ip_header *ip;
- struct udp_header *udp;
- struct gre_base_hdr *greh;
-+ struct nshhdr *nsh;
- uint16_t gre_proto, gre_flags, dl_type, udp_src, udp_dst, csum;
-- ovs_be32 sip, dip;
-+ ovs_be32 sip, dip, nsp, nshc1,nshc2,nshc3,nshc4;
-+ uint8_t nsi;
- uint32_t tnl_type = 0, header_len = 0;
- void *l3, *l4;
- int n = 0;
-@@ -837,71 +869,108 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
- udp = (struct udp_header *) l4;
- greh = (struct gre_base_hdr *) l4;
- if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
-- &udp_src, &udp_dst, &csum)) {
-- uint32_t vx_flags, vni;
-+ &udp_src, &udp_dst, &csum)) {
-+ struct vxlanhdr *vxh;
-+ struct vxgpehdr *vxg;
-+ uint32_t vx_flags, vx_vni;
-+ uint32_t geneve_vni;
-
- udp->udp_src = htons(udp_src);
- udp->udp_dst = htons(udp_dst);
- udp->udp_len = 0;
- udp->udp_csum = htons(csum);
-
-+ vxh = (struct vxlanhdr *) (udp + 1);
-+ vxg = (struct vxgpehdr *) (udp + 1);
-+
- if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
-- &vx_flags, &vni)) {
-- struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
-+ &vx_flags, &vx_vni)) {
-+ tnl_type = OVS_VPORT_TYPE_VXLAN;
-
- put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
-- put_16aligned_be32(&vxh->vx_vni, htonl(vni << 8));
-- tnl_type = OVS_VPORT_TYPE_VXLAN;
-+ put_16aligned_be32(&vxh->vx_vni, htonl(vx_vni<<8));
-+
- header_len = sizeof *eth + sizeof *ip +
- sizeof *udp + sizeof *vxh;
-- } else if (ovs_scan_len(s, &n, "geneve(")) {
-- struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
-
-- memset(gnh, 0, sizeof *gnh);
-- header_len = sizeof *eth + sizeof *ip +
-- sizeof *udp + sizeof *gnh;
-+ } else if (ovs_scan_len(s, &n, "vxlangpe(vni=0x%"SCNx32",proto="SCNi8"),",
-+ &vx_vni, &vxg->proto)) {
-+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
-
-- if (ovs_scan_len(s, &n, "oam,")) {
-- gnh->oam = 1;
-- }
-- if (ovs_scan_len(s, &n, "crit,")) {
-- gnh->critical = 1;
-- }
-- if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &vni)) {
-+ tnl_type = OVS_VPORT_TYPE_VXLAN;
-+ vxg->i = 0x01;
-+ vxg->p = 0x01;
-+ vxg->ver = 0x01;
-+ put_16aligned_be32(&vxg->vx_vni, htonl(vx_vni));
-+
-+ if (ovs_scan_len(s, &n, "nsh(mdtype=%"SCNi8",proto=%"SCNi8",nsp=0x%"SCNx32
-+ ",nsi=%"SCNi8",nshc1=0x%"SCNx32",nshc2=0x%"SCNx32
-+ ",nshc3=0x%"SCNx32",nshc4=0x%"SCNx32"))",
-+ &nsh->b.mdtype, &nsh->b.proto,
-+ &nsp, &nsi,
-+ &nshc1, &nshc2,
-+ &nshc3, &nshc4)) {
-+ nsh->b.ver = 0x01;
-+ nsh->b.len = 6;
-+ nsh->b.b2 = nsp;
-+ nsh->b.svc_idx = nsi;
-+ nsh->c.nshc1=nshc1;
-+ nsh->c.nshc2=nshc2;
-+ nsh->c.nshc3=nshc3;
-+ nsh->c.nshc4=nshc4;
-+ header_len = sizeof *eth + sizeof *ip +
-+ sizeof *udp + sizeof *vxh + sizeof *nsh;
-+ } else {
- return -EINVAL;
- }
-- if (ovs_scan_len(s, &n, ",options(")) {
-- struct geneve_scan options;
-- int len;
--
-- memset(&options, 0, sizeof options);
-- len = scan_geneve(s + n, &options, NULL);
-- if (!len) {
-- return -EINVAL;
-- }
-+ } else if (ovs_scan_len(s, &n, "geneve(")) {
-+ struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
-
-- memcpy(gnh->options, options.d, options.len);
-- gnh->opt_len = options.len / 4;
-- header_len += options.len;
-+ memset(gnh, 0, sizeof *gnh);
-+ header_len = sizeof *eth + sizeof *ip +
-+ sizeof *udp + sizeof *gnh;
-
-- n += len;
-- }
-- if (!ovs_scan_len(s, &n, "))")) {
-+ if (ovs_scan_len(s, &n, "oam,")) {
-+ gnh->oam = 1;
-+ }
-+ if (ovs_scan_len(s, &n, "crit,")) {
-+ gnh->critical = 1;
-+ }
-+ if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &geneve_vni)) {
-+ return -EINVAL;
-+ }
-+ if (ovs_scan_len(s, &n, ",options(")) {
-+ struct geneve_scan options;
-+ int len;
-+
-+ memset(&options, 0, sizeof options);
-+ len = scan_geneve(s + n, &options, NULL);
-+ if (!len) {
- return -EINVAL;
- }
-
-- gnh->proto_type = htons(ETH_TYPE_TEB);
-- put_16aligned_be32(&gnh->vni, htonl(vni << 8));
-- tnl_type = OVS_VPORT_TYPE_GENEVE;
-- } else {
-+ memcpy(gnh->options, options.d, options.len);
-+ gnh->opt_len = options.len / 4;
-+ header_len += options.len;
-+
-+ n += len;
-+ }
-+ if (!ovs_scan_len(s, &n, "))")) {
- return -EINVAL;
- }
-- } else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
-- &gre_flags, &gre_proto)){
-
-- tnl_type = OVS_VPORT_TYPE_GRE;
-- greh->flags = htons(gre_flags);
-- greh->protocol = htons(gre_proto);
-+ gnh->proto_type = htons(ETH_TYPE_TEB);
-+ put_16aligned_be32(&gnh->vni, htonl(geneve_vni << 8));
-+ tnl_type = OVS_VPORT_TYPE_GENEVE;
-+ } else {
-+ return -EINVAL;
-+ }
-+} else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
-+ &gre_flags, &gre_proto)){
-+
-+ tnl_type = OVS_VPORT_TYPE_GRE;
-+ greh->flags = htons(gre_flags);
-+ greh->protocol = htons(gre_proto);
- ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
-
- if (greh->flags & htons(GRE_CSUM)) {
-@@ -941,7 +1010,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
- ((uint8_t *) options - (uint8_t *) greh);
- } else {
- return -EINVAL;
-- }
-+ }
-
- /* check tunnel meta data. */
- if (data->tnl_type != tnl_type) {
-@@ -1120,6 +1189,7 @@ parse_odp_action(const char *s, const struct simap *port_names,
- struct ovs_action_push_tnl data;
- int n;
-
-+ memset(&data, 0, sizeof data);
- n = ovs_parse_tnl_push(s, &data);
- if (n > 0) {
- odp_put_tnl_push_action(actions, &data);
-@@ -3285,7 +3355,6 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
- 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/packets.h b/lib/packets.h
-index 12f2239..7f9ab98 100644
---- a/lib/packets.h
-+++ b/lib/packets.h
-@@ -33,7 +33,6 @@
- struct dp_packet;
- struct ds;
-
--/* Tunnel information used in flow key and metadata. */
- struct flow_tnl {
- ovs_be32 ip_dst;
- ovs_be32 ip_src;
-@@ -913,6 +912,120 @@ struct vxlanhdr {
- /* VXLAN GPE UDP DST PORT */
- #define VXGPE_DST_PORT 4790
-
-+/**
-+ * struct vxlan_gpehdr - Generic Protocol Extension for VXLAN header.
-+ * @p: Next Protocol field indicator bit
-+ * @o: Operations and Management Packet indicator bit.
-+ * @proto: IEEE Ethertypes to indicate the frame within.
-+ * @vni: VXLAN Network Identifier.
-+ */
-+struct vxgpehdr {
-+#ifdef WORDS_BIGENDIAN
-+ uint8_t res1:4;
-+ uint8_t i:1;
-+ uint8_t p:1;
-+ uint8_t res2:1;
-+ uint8_t o:1;
-+
-+ uint8_t ver:2;
-+ uint8_t res3:6;
-+#else
-+ uint8_t o:1;
-+ uint8_t res2:1;
-+ uint8_t p:1;
-+ uint8_t i:1;
-+ uint8_t res1:4;
-+
-+ uint8_t res3:6;
-+ uint8_t ver:2;
-+#endif
-+ uint8_t res4;
-+ uint8_t proto;
-+ ovs_16aligned_be32 vx_vni;
-+};
-+
-+/* VxLAN-GPE Header Next Protocol */
-+#define VXG_P_IPV4 0x01
-+#define VXG_P_IPV6 0x02
-+#define VXG_P_ETHERNET 0x03
-+#define VXG_P_NSH 0x04
-+
-+/**
-+ * 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 {
-+#ifdef WORDS_BIGENDIAN
-+ uint8_t ver:2;
-+ uint8_t o:1;
-+ uint8_t c:1;
-+ uint8_t res1:4;
-+
-+ uint8_t res2:2;
-+ uint8_t len:6;
-+#else
-+ uint8_t res1:4;
-+ uint8_t c:1;
-+ uint8_t o:1;
-+ uint8_t ver:2;
-+
-+ uint8_t len:6;
-+ uint8_t res2:2;
-+#endif
-+ uint8_t mdtype;
-+ uint8_t proto;
-+ union {
-+ struct {
-+ uint8_t svc_path[3];
-+ uint8_t svc_idx;
-+ };
-+ ovs_be32 b2;
-+ };
-+};
-+
-+/**
-+ * struct nsh_ctx - Keeps track of NSH context data
-+ * @npc: NSH network platform context
-+ * @nsc: NSH network shared context
-+ * @spc: NSH service platform context
-+ * @ssc: NSH service shared context
-+ */
-+struct nsh_ctx {
-+ ovs_be32 nshc1;
-+ ovs_be32 nshc2;
-+ ovs_be32 nshc3;
-+ ovs_be32 nshc4;
-+};
-+
-+/**
-+ * 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;
-+};
-+
-+/* 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
-+
-+/* Used for masking nsp and nsi values in field nsp below */
-+#define NSH_M_NSP 0xFFFFFF00 //uncertain
-+#define NSH_M_NSI 0x000000FF
-+
- #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 4bb9801..9c64c24 100644
---- a/ofproto/ofproto-dpif-xlate.c
-+++ b/ofproto/ofproto-dpif-xlate.c
-@@ -4780,7 +4780,6 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
- if (!xbridge) {
- return;
- }
--
- struct flow *flow = &xin->flow;
-
- union mf_subvalue stack_stub[1024 / sizeof(union mf_subvalue)];
-diff --git a/tests/tunnel.at b/tests/tunnel.at
-index f43a07d..5ec5e6c 100644
---- a/tests/tunnel.at
-+++ b/tests/tunnel.at
-@@ -527,6 +527,124 @@ AT_CHECK([tail -1 stdout], [0],
- OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"])
- AT_CLEANUP
-
-+AT_SETUP([tunnel - VXLAN-GPE NSH user 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(["/The Open vSwitch kernel module is probably not loaded/d"])
-+AT_CLEANUP
-+
-+AT_SETUP([tunnel VXLAN-GPE NSH - encap - nsh/nsi/nshc user 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=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])
-+
-+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
-+])
-+AT_CHECK([ovs-vsctl add-port br0 p7 -- set Interface p7 type=dummy ofport_request=7])
-+AT_CHECK([ovs-vsctl add-port br0 p8 -- set Interface p8 type=dummy ofport_request=8])
-+
-+AT_CHECK([
-+ovs-appctl ovs/route/add 1.1.1.1/24 br0
-+ovs-appctl tnl/arp/set br0 1.1.1.1 68:05:ca:30:6b:d1
-+ovs-appctl ovs/route/add 2.2.2.2/24 br0
-+ovs-appctl tnl/arp/set br0 2.2.2.2 68:05:ca:30:6b:d2
-+ovs-appctl ovs/route/add 3.3.3.3/24 br0
-+ovs-appctl tnl/arp/set br0 3.3.3.3 68:05:ca:30:6b:d3
-+ovs-appctl ovs/route/add 4.4.4.4/24 br0
-+ovs-appctl tnl/arp/set br0 4.4.4.4 68:05:ca:30:6b:d4
-+ovs-appctl ovs/route/add 5.5.5.5/24 br0
-+ovs-appctl tnl/arp/set br0 5.5.5.5 68:05:ca:30:6b:d5
-+],[0],[stdout])
-+
-+AT_DATA([flows.txt], [dnl
-+in_port=7 actions=resubmit:1,resubmit:2,resubmit:3
-+in_port=1 actions=output:1
-+in_port=2 actions=set_field:3.3.3.3->tun_dst,output:2
-+in_port=3 actions=output:3
-+])
-+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),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: tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d1,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=1.1.1.1,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=0,nsi=1,nshc1=0,nshc2=0,nshc3=0,nshc4=0,)),out_port(100)),tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d3,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=3.3.3.3,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=0,nsi=1,nshc1=0,nshc2=0,nshc3=0,nshc4=0,)),out_port(100)),tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d2,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=2.2.2.2,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=6f0000,nsi=11,nshc1=b,nshc2=c,nshc3=d,nshc4=e,)),out_port(100))
-+])
-+
-+AT_DATA([flows.txt], [dnl
-+in_port=8 actions=resubmit:4,resubmit:5,resubmit:6
-+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(8),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: tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d3,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=3.3.3.3,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=de0000,nsi=22,nshc1=16,nshc2=17,nshc3=18,nshc4=19,)),out_port(100)),tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d4,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=4.4.4.4,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=4d0100,nsi=33,nshc1=21,nshc2=22,nshc3=23,nshc4=24,)),out_port(100)),tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d5,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=5.5.5.5,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=bc0100,nsi=44,nshc1=2c,nshc2=2d,nshc3=2e,nshc4=2f,)),out_port(100))
-+])
-+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
-+AT_CLEANUP
-+
-+AT_SETUP([tunnel VXLAN-GPE NSH - decap - nsh/nsi/nshc user space])
-+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
-+AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0])
-+AT_CHECK([ovs-vsctl add-port int-br p1 -- set Interface p1 type=vxlan options:key=flow \
-+ options:remote_ip=1.1.1.1 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 int-br p2 -- set Interface p2 type=vxlan options:key=flow \
-+ options:remote_ip=2.2.2.2 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 int-br p3 -- set Interface p3 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=4], [0])
-+
-+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
-+])
-+AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK
-+])
-+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
-+
-+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
-+ br0 65534/100: (dummy)
-+ p0 1/1: (dummy)
-+ int-br:
-+ int-br 65534/2: (dummy)
-+ p1 2/4790: (vxlan: dst_port=4790, key=flow, nshc1=0xb, nshc2=0xc, nshc3=0xd, nshc4=0xe, nsi=11, nsp=0x6f, remote_ip=1.1.1.1)
-+ p2 3/4790: (vxlan: dst_port=4790, key=flow, nshc1=flow, nshc2=flow, nshc3=flow, nshc4=flow, nsi=flow, nsp=flow, remote_ip=2.2.2.2)
-+ p3 4/4790: (vxlan: dst_port=4790, key=flow, nshc1=flow, nshc2=flow, nshc3=flow, nshc4=flow, nsi=flow, nsp=flow, remote_ip=flow)
-+])
-+
-+AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
-+Listening ports:
-+vxlan_sys_4790 (4790)
-+])
-+
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4790)'], [0], [stdout])
-+AT_CHECK([tail -1 stdout], [0],
-+ [Datapath actions: tnl_pop(4790)
-+])
-+
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=2.2.2.2,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4790)'], [0], [stdout])
-+AT_CHECK([tail -1 stdout], [0],
-+ [Datapath actions: tnl_pop(4790)
-+])
-+
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=3.4.5.6,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4790)'], [0], [stdout])
-+AT_CHECK([tail -1 stdout], [0],
-+ [Datapath actions: tnl_pop(4790)
-+])
-+
-+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/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 \
---
-1.9.3
-
diff --git a/ovs-nsh/patches/060681.patch b/ovs-nsh/patches/060681.patch
deleted file mode 100644
index f5afed1..0000000
--- a/ovs-nsh/patches/060681.patch
+++ /dev/null
@@ -1,1025 +0,0 @@
-decapsulation-reencapsulation case.
-
-When VxLAN-GPE NSH packets are received and then resent to VxLAN-GPE NSH port.
-The decapsulation and encapsulation will be implemented. However, tunnel pop
-and tunnel push actions are very time-consuming when decapsulation and
-encapsulation.
-
-With this feature (options:tun_nodecap=true), tunnel port will parse the input
-tunnel packets, but the tunnel header will be kept. And the tunnel header can
-be modified by the set field actions. This feature can improve performance.
-
-Signed-off-by: Ricky Li <ricky.li at intel.com>
-Signed-off-by: Mengke Liu <mengke.liu at intel.com>
----
- datapath/linux/compat/include/linux/openvswitch.h | 23 +++++
- lib/dpif-netdev.c | 36 +++++++
- lib/dpif.c | 1 +
- lib/netdev-bsd.c | 1 +
- lib/netdev-dpdk.c | 1 +
- lib/netdev-dummy.c | 1 +
- lib/netdev-linux.c | 1 +
- lib/netdev-provider.h | 4 +
- lib/netdev-vport.c | 111 +++++++++++++++++++---
- lib/netdev.c | 24 +++++
- lib/netdev.h | 7 +-
- lib/odp-execute.c | 5 +
- lib/odp-util.c | 96 ++++++++++++++++++-
- lib/odp-util.h | 7 +-
- lib/ofp-print.c | 1 +
- lib/packets.c | 20 ++++
- lib/packets.h | 6 ++
- ofproto/ofproto-dpif-sflow.c | 4 +
- ofproto/ofproto-dpif-xlate.c | 50 ++++++++--
- ofproto/tunnel.c | 86 +++++++++++++++++
- ofproto/tunnel.h | 4 +
- tests/tunnel.at | 28 ++++++
- 22 files changed, 495 insertions(+), 22 deletions(-)
-
-diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
-index aa5dfde..b8ac152 100644
---- a/datapath/linux/compat/include/linux/openvswitch.h
-+++ b/datapath/linux/compat/include/linux/openvswitch.h
-@@ -645,6 +645,27 @@ struct ovs_action_push_tnl {
- uint32_t tnl_type; /* For logging. */
- uint8_t header[TNL_PUSH_HEADER_SIZE];
- };
-+
-+#define OVS_POP_SPEC_ACTION_NO_DECAP 2
-+
-+/*
-+ * struct ovs_action_pop_tnl - %OVS_ACTION_ATTR_TUNNEL_POP_SPEC
-+ * @tnl_port: To identify tunnel port to pass header info.
-+ * @out_port: Physical port to send encapsulated packet.
-+ * @header_len: Length of the header to be pushed.
-+ * @tnl_type: This is only required to format this header. Otherwise
-+ * ODP layer can not parse %header.
-+ * @header: Partial header for the tunnel. Specified pop action can use
-+ * this header to build final header according to actual packet parameters.
-+ */
-+struct ovs_action_pop_tnl {
-+ uint32_t tnl_port;
-+ uint32_t out_port;
-+ uint32_t header_len;
-+ uint16_t tnl_type; /* For logging. */
-+ uint16_t pop_type;
-+ uint8_t header[TNL_PUSH_HEADER_SIZE];
-+};
- #endif
-
- /**
-@@ -712,6 +733,8 @@ enum ovs_action_attr {
- #ifndef __KERNEL__
- OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/
- OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */
-+ OVS_ACTION_ATTR_TUNNEL_POP_SPEC, /* struct ovs_action_pop_tnl */
-+
- #endif
- __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
- * from userspace. */
-diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
-index e6ba33f..07f05c4 100644
---- a/lib/dpif-netdev.c
-+++ b/lib/dpif-netdev.c
-@@ -3549,6 +3549,42 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
- }
- break;
-
-+ case OVS_ACTION_ATTR_TUNNEL_POP_SPEC:
-+ if (*depth < MAX_RECIRC_DEPTH) {
-+ const struct ovs_action_pop_tnl *data;
-+ odp_port_t portno;
-+
-+ data = nl_attr_get(a);
-+ portno = u32_to_odp(data->tnl_port);
-+
-+ p = dp_netdev_lookup_port(dp, portno);
-+ if (p) {
-+ struct dp_packet *tnl_pkt[NETDEV_MAX_BURST];
-+ int err;
-+
-+ if (!may_steal) {
-+ dp_netdev_clone_pkt_batch(tnl_pkt, packets, cnt);
-+ packets = tnl_pkt;
-+ }
-+
-+ err = netdev_pop_header_spec(p->netdev, packets, cnt, data);
-+ if (!err) {
-+
-+ for (i = 0; i < cnt; i++) {
-+ packets[i]->md.in_port.odp_port = portno;
-+ }
-+
-+ (*depth)++;
-+ dp_netdev_input(pmd, packets, cnt);
-+ (*depth)--;
-+ } else {
-+ dp_netdev_drop_packets(tnl_pkt, cnt, !may_steal);
-+ }
-+ return;
-+ }
-+ }
-+ break;
-+
- case OVS_ACTION_ATTR_USERSPACE:
- if (!fat_rwlock_tryrdlock(&dp->upcall_rwlock)) {
- const struct nlattr *userdata;
-diff --git a/lib/dpif.c b/lib/dpif.c
-index 9a67a24..bb2d519 100644
---- a/lib/dpif.c
-+++ b/lib/dpif.c
-@@ -1100,6 +1100,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt,
- case OVS_ACTION_ATTR_OUTPUT:
- case OVS_ACTION_ATTR_TUNNEL_PUSH:
- case OVS_ACTION_ATTR_TUNNEL_POP:
-+ case OVS_ACTION_ATTR_TUNNEL_POP_SPEC:
- case OVS_ACTION_ATTR_USERSPACE:
- case OVS_ACTION_ATTR_RECIRC: {
- struct dpif_execute execute;
-diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
-index 60e5615..001e888 100644
---- a/lib/netdev-bsd.c
-+++ b/lib/netdev-bsd.c
-@@ -1560,6 +1560,7 @@ netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off,
- NULL, /* build header */ \
- NULL, /* push header */ \
- NULL, /* pop header */ \
-+ NULL, /* pop specific header */ \
- NULL, /* get_numa_id */ \
- NULL, /* set_multiq */ \
- \
-diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
-index b72a33b..4947013 100644
---- a/lib/netdev-dpdk.c
-+++ b/lib/netdev-dpdk.c
-@@ -2050,6 +2050,7 @@ unlock_dpdk:
- NULL, /* build header */ \
- NULL, /* push header */ \
- NULL, /* pop header */ \
-+ NULL, /* pop specific header */ \
- netdev_dpdk_get_numa_id, /* get_numa_id */ \
- MULTIQ, /* set_multiq */ \
- \
-diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
-index 76815c2..2f44901 100644
---- a/lib/netdev-dummy.c
-+++ b/lib/netdev-dummy.c
-@@ -1098,6 +1098,7 @@ static const struct netdev_class dummy_class = {
- NULL, /* build header */
- NULL, /* push header */
- NULL, /* pop header */
-+ NULL, /* pop specific header */
- NULL, /* get_numa_id */
- NULL, /* set_multiq */
-
-diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
-index 584e804..79f47f3 100644
---- a/lib/netdev-linux.c
-+++ b/lib/netdev-linux.c
-@@ -2790,6 +2790,7 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
- NULL, /* build header */ \
- NULL, /* push header */ \
- NULL, /* pop header */ \
-+ NULL, /* pop specific header */ \
- NULL, /* get_numa_id */ \
- NULL, /* set_multiq */ \
- \
-diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
-index a33bb3b..e163376 100644
---- a/lib/netdev-provider.h
-+++ b/lib/netdev-provider.h
-@@ -271,6 +271,10 @@ struct netdev_class {
- * for further processing. */
- int (*pop_header)(struct dp_packet *packet);
-
-+ /* Pop tunnel header from packet with specific actions */
-+ int (*pop_header_spec)(struct dp_packet *packet,
-+ const struct ovs_action_pop_tnl *data);
-+
- /* Returns the id of the numa node the 'netdev' is on. If there is no
- * such info, returns NETDEV_NUMA_UNSPEC. */
- int (*get_numa_id)(const struct netdev *netdev);
-diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
-index a0a4da2..d926c00 100644
---- a/lib/netdev-vport.c
-+++ b/lib/netdev-vport.c
-@@ -561,7 +561,13 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
- } else {
- tnl_cfg.ip_dst = in_addr.s_addr;
- }
-- } else if (!strcmp(node->key, "local_ip")) {
-+ } else if (!strcmp(node->key, "tun_nodecap")) {
-+ if (!strcmp(node->value, "true")) {
-+ tnl_cfg.tun_nodecap = true;
-+ } else {
-+ tnl_cfg.tun_nodecap = false;
-+ }
-+ }else if (!strcmp(node->key, "local_ip")) {
- struct in_addr in_addr;
- if (!strcmp(node->value, "flow")) {
- tnl_cfg.ip_src_flow = true;
-@@ -878,6 +884,10 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
- smap_add(args, "df_default", "false");
- }
-
-+ if (tnl_cfg.tun_nodecap) {
-+ smap_add(args, "tun_nodecap", "true");
-+ }
-+
- 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
-@@ -1536,6 +1546,80 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
- }
-
- static int
-+vxlan_extract_md_no_decap(struct dp_packet *packet)
-+{
-+ struct pkt_metadata *md = &packet->md;
-+ struct flow_tnl *tnl = &md->tunnel;
-+ struct ip_header *nh;
-+ struct udp_header *udp;
-+
-+ memset(md, 0, sizeof *md);
-+ if (VXLAN_HLEN > dp_packet_size(packet)) {
-+ return EINVAL;;
-+ }
-+
-+ udp = ip_extract_tnl_md(packet, tnl);
-+ if (!udp) {
-+ return EINVAL;;
-+ }
-+
-+ /* TTL decrement */
-+ nh = dp_packet_l3(packet);
-+ if(nh->ip_ttl){
-+ nh->ip_ttl = nh->ip_ttl - 1;
-+ }
-+
-+ if (ntohs(udp->udp_dst) == VXGPE_DST_PORT) {
-+
-+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
-+
-+ if (get_16aligned_be32(&vxg->vx_vni) & htonl(0xff)) {
-+ VLOG_WARN_RL(&err_rl, "invalid vxlan-gpe vni=%#x\n",
-+ ntohl(get_16aligned_be32(&vxg->vx_vni)));
-+ return EINVAL;;
-+ }
-+
-+ tnl->tp_src = udp->udp_src;
-+ tnl->tp_dst = udp->udp_dst;
-+
-+ if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
-+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
-+
-+ tnl->nsp = nsh->b.b2 << 8;
-+ tnl->nsi = nsh->b.svc_idx;
-+ tnl->nshc1 = nsh->c.nshc1;
-+ tnl->nshc2 = nsh->c.nshc2;
-+ tnl->nshc3 = nsh->c.nshc3;
-+ tnl->nshc4 = nsh->c.nshc4;
-+ tnl->flags |= FLOW_TNL_F_NSP;
-+ tnl->flags |= FLOW_TNL_F_NSI;
-+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
-+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
-+ tnl->nsh_flags = NSH_TNL_F_NODECAP;
-+ } else {
-+ VLOG_WARN("Unsupported vxlan GPE + NSH format!");
-+ return EINVAL;;
-+ }
-+
-+ }
-+
-+ return 0;
-+}
-+
-+
-+static int
-+netdev_vxlan_pop_header_spec(struct dp_packet *packet,
-+ const struct ovs_action_pop_tnl *data)
-+{
-+ if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
-+ return vxlan_extract_md_no_decap(packet);
-+ }
-+
-+ return EINVAL;
-+}
-+
-+
-+static int
- netdev_vxlan_build_header(const struct netdev *netdev,
- struct ovs_action_push_tnl *data,
- const struct flow *tnl_flow)
-@@ -1772,7 +1856,8 @@ netdev_vport_range(struct unixctl_conn *conn, int argc,
- #define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG, \
- GET_TUNNEL_CONFIG, GET_STATUS, \
- BUILD_HEADER, \
-- PUSH_HEADER, POP_HEADER) \
-+ PUSH_HEADER, POP_HEADER, \
-+ POP_HEADER_SPEC) \
- NULL, \
- netdev_vport_run, \
- netdev_vport_wait, \
-@@ -1787,6 +1872,7 @@ netdev_vport_range(struct unixctl_conn *conn, int argc,
- BUILD_HEADER, \
- PUSH_HEADER, \
- POP_HEADER, \
-+ POP_HEADER_SPEC, \
- NULL, /* get_numa_id */ \
- NULL, /* set_multiq */ \
- \
-@@ -1840,13 +1926,13 @@ netdev_vport_range(struct unixctl_conn *conn, int argc,
-
-
-
--#define TUNNEL_CLASS(NAME, DPIF_PORT, BUILD_HEADER, PUSH_HEADER, POP_HEADER) \
-+#define TUNNEL_CLASS(NAME, DPIF_PORT, BUILD_HEADER, PUSH_HEADER, POP_HEADER,POP_HEADER_SPEC) \
- { DPIF_PORT, \
- { NAME, VPORT_FUNCTIONS(get_tunnel_config, \
- set_tunnel_config, \
- get_netdev_tunnel_config, \
- tunnel_get_status, \
-- BUILD_HEADER, PUSH_HEADER, POP_HEADER) }}
-+ BUILD_HEADER, PUSH_HEADER, POP_HEADER, POP_HEADER_SPEC) }}
-
- void
- netdev_vport_tunnel_register(void)
-@@ -1855,17 +1941,18 @@ netdev_vport_tunnel_register(void)
- * a port number to the end if one is necessary. */
- static const struct vport_class vport_classes[] = {
- TUNNEL_CLASS("geneve", "genev_sys", netdev_geneve_build_header,
-- push_udp_header,
-- netdev_geneve_pop_header),
-+ push_udp_header,
-+ netdev_geneve_pop_header, NULL),
- TUNNEL_CLASS("gre", "gre_sys", netdev_gre_build_header,
- netdev_gre_push_header,
-- netdev_gre_pop_header),
-- TUNNEL_CLASS("ipsec_gre", "gre_sys", NULL, NULL, NULL),
-+ netdev_gre_pop_header,NULL),
-+ TUNNEL_CLASS("ipsec_gre", "gre_sys", NULL, NULL, NULL,NULL),
- TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header,
- netdev_vxlan_push_header,
-- netdev_vxlan_pop_header),
-- TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL),
-- TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL),
-+ netdev_vxlan_pop_header,
-+ netdev_vxlan_pop_header_spec),
-+ TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL,NULL),
-+ TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL,NULL),
- };
- static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
-
-@@ -1891,6 +1978,6 @@ netdev_vport_patch_register(void)
- { "patch", VPORT_FUNCTIONS(get_patch_config,
- set_patch_config,
- NULL,
-- NULL, NULL, NULL, NULL) }};
-+ NULL, NULL, NULL, NULL, NULL) }};
- netdev_register_provider(&patch_class.netdev_class);
- }
-diff --git a/lib/netdev.c b/lib/netdev.c
-index e3b70b1..884bc0e 100644
---- a/lib/netdev.c
-+++ b/lib/netdev.c
-@@ -796,6 +796,30 @@ netdev_push_header(const struct netdev *netdev,
- return 0;
- }
-
-+
-+int
-+netdev_pop_header_spec(struct netdev *netdev,
-+ struct dp_packet **buffers, int cnt,
-+ const struct ovs_action_pop_tnl *data)
-+{
-+ int i;
-+
-+ if (!netdev->netdev_class->pop_header_spec) {
-+ return -EINVAL;
-+ }
-+
-+ for (i = 0; i < cnt; i++) {
-+ int err;
-+
-+ err = netdev->netdev_class->pop_header_spec(buffers[i], data);
-+ if (err) {
-+ dp_packet_clear(buffers[i]);
-+ }
-+ }
-+
-+ return 0;
-+}
-+
- /* Registers with the poll loop to wake up from the next call to poll_block()
- * when the packet transmission queue has sufficient room to transmit a packet
- * with netdev_send().
-diff --git a/lib/netdev.h b/lib/netdev.h
-index 4dadf1c..b30c932 100644
---- a/lib/netdev.h
-+++ b/lib/netdev.h
-@@ -72,6 +72,7 @@ struct in6_addr;
- struct smap;
- struct sset;
- struct ovs_action_push_tnl;
-+struct ovs_action_pop_tnl;
-
- /* Network device statistics.
- *
-@@ -181,6 +182,8 @@ struct netdev_tunnel_config {
- bool out_nshc4_flow;
- ovs_be32 out_nshc4; /* outgoing NSH context c4 */
-
-+ bool tun_nodecap;
-+
- };
-
- void netdev_run(void);
-@@ -241,7 +244,9 @@ int netdev_push_header(const struct netdev *netdev,
- const struct ovs_action_push_tnl *data);
- int netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers,
- int cnt);
--
-+int netdev_pop_header_spec(struct netdev *netdev,
-+ struct dp_packet **buffers, int cnt,
-+ const struct ovs_action_pop_tnl *data);
- /* Hardware address. */
- int netdev_set_etheraddr(struct netdev *, const struct eth_addr mac);
- int netdev_get_etheraddr(const struct netdev *, struct eth_addr *mac);
-diff --git a/lib/odp-execute.c b/lib/odp-execute.c
-index 54a43cd..2ecdabf 100644
---- a/lib/odp-execute.c
-+++ b/lib/odp-execute.c
-@@ -238,6 +238,9 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
-
- case OVS_KEY_ATTR_TUNNEL:
- odp_set_tunnel_action(a, &md->tunnel);
-+ if (md->tunnel.nsh_flags & NSH_TNL_F_NODECAP) {
-+ packet_set_nsh(packet, &md->tunnel);
-+ }
- break;
-
- case OVS_KEY_ATTR_SKB_MARK:
-@@ -474,6 +477,7 @@ requires_datapath_assistance(const struct nlattr *a)
- case OVS_ACTION_ATTR_OUTPUT:
- case OVS_ACTION_ATTR_TUNNEL_PUSH:
- case OVS_ACTION_ATTR_TUNNEL_POP:
-+ case OVS_ACTION_ATTR_TUNNEL_POP_SPEC:
- case OVS_ACTION_ATTR_USERSPACE:
- case OVS_ACTION_ATTR_RECIRC:
- return true;
-@@ -609,6 +613,7 @@ odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal,
- case OVS_ACTION_ATTR_OUTPUT:
- case OVS_ACTION_ATTR_TUNNEL_PUSH:
- case OVS_ACTION_ATTR_TUNNEL_POP:
-+ case OVS_ACTION_ATTR_TUNNEL_POP_SPEC:
- case OVS_ACTION_ATTR_USERSPACE:
- case OVS_ACTION_ATTR_RECIRC:
- case OVS_ACTION_ATTR_UNSPEC:
-diff --git a/lib/odp-util.c b/lib/odp-util.c
-index 1696f77..190117f 100644
---- a/lib/odp-util.c
-+++ b/lib/odp-util.c
-@@ -103,6 +103,7 @@ odp_action_len(uint16_t type)
- case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t);
- case OVS_ACTION_ATTR_TUNNEL_PUSH: return ATTR_LEN_VARIABLE;
- case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t);
-+ case OVS_ACTION_ATTR_TUNNEL_POP_SPEC: return ATTR_LEN_VARIABLE;
- case OVS_ACTION_ATTR_USERSPACE: return ATTR_LEN_VARIABLE;
- case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
- case OVS_ACTION_ATTR_POP_VLAN: return 0;
-@@ -551,6 +552,20 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
- }
-
- static void
-+format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
-+{
-+ struct ovs_action_pop_tnl *data;
-+
-+ data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
-+
-+ ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
-+ if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
-+ ds_put_format(ds, "pop_type=%"PRIu16")",
-+ OVS_POP_SPEC_ACTION_NO_DECAP);
-+ }
-+}
-+
-+static void
- format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr)
- {
- struct ovs_action_push_tnl *data;
-@@ -586,6 +601,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
- case OVS_ACTION_ATTR_TUNNEL_POP:
- ds_put_format(ds, "tnl_pop(%"PRIu32")", nl_attr_get_u32(a));
- break;
-+ case OVS_ACTION_ATTR_TUNNEL_POP_SPEC:
-+ format_odp_tnl_pop_spec_action(ds, a);
-+ break;
- case OVS_ACTION_ATTR_TUNNEL_PUSH:
- format_odp_tnl_push_action(ds, a);
- break;
-@@ -1029,6 +1047,35 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
- }
-
- static int
-+ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
-+{
-+ struct eth_header *eth;
-+ struct nshhdr *nsh;
-+ uint32_t tnl_type = 0, header_len = 0;
-+ uint16_t dl_type;
-+ ovs_be32 nsp, nshc1, nshc2, nshc3, nshc4;
-+ uint8_t nsi;
-+ int n = 0;
-+ if (!ovs_scan_len(s, &n, "tnl_pop_spec(tnl_port(%"SCNi32"),",
-+ &data->tnl_port)) {
-+ return -EINVAL;
-+ }
-+
-+ if (!ovs_scan_len(s, &n, "pop_type=%"SCNi16")",
-+ &data->pop_type)) {
-+ return -EINVAL;
-+ }
-+
-+ if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
-+ return n;
-+ } else {
-+ return -EINVAL;
-+ }
-+
-+ return n;
-+}
-+
-+static int
- parse_odp_action(const char *s, const struct simap *port_names,
- struct ofpbuf *actions)
- {
-@@ -1186,6 +1233,22 @@ parse_odp_action(const char *s, const struct simap *port_names,
- }
-
- {
-+ struct ovs_action_pop_tnl data;
-+ int n;
-+
-+ if (ovs_scan(s, "tnl_pop_spec(tnl_port(%"SCNi32"),", &(data.tnl_port))) {
-+ memset(&data, 0, sizeof data);
-+ n = ovs_parse_tnl_pop_spec(s, &data);
-+ if (n > 0) {
-+ odp_put_tnl_pop_spec_action(actions, &data);
-+ return n;
-+ } else if (n < 0) {
-+ return n;
-+ }
-+ }
-+ }
-+
-+ {
- struct ovs_action_push_tnl data;
- int n;
-
-@@ -1461,7 +1524,9 @@ enum odp_key_fitness
- odp_tun_key_from_attr(const struct nlattr *attr, bool udpif,
- struct flow_tnl *tun)
- {
-+ uint8_t nsh_flags=tun->nsh_flags;
- memset(tun, 0, sizeof *tun);
-+ tun->nsh_flags=nsh_flags;
- return odp_tun_key_from_attr__(attr, NULL, 0, NULL, tun, udpif);
- }
-
-@@ -4564,7 +4629,16 @@ odp_put_tnl_push_action(struct ofpbuf *odp_actions,
- nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_TUNNEL_PUSH, data, size);
- }
-
--
-+void
-+odp_put_tnl_pop_spec_action(struct ofpbuf *odp_actions,
-+ struct ovs_action_pop_tnl *data)
-+{
-+ int size = offsetof(struct ovs_action_pop_tnl, header);
-+
-+ size += data->header_len;
-+ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_TUNNEL_POP_SPEC, data, size);
-+}
-+
- /* The commit_odp_actions() function and its helpers. */
-
- static void
-@@ -4617,6 +4691,26 @@ commit_odp_tunnel_action(const struct flow *flow, struct flow *base,
- }
- }
-
-+/* If any of the flow key data that ODP actions can modify are different in
-+ * 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
-+ * 'odp_actions' that change the flow tunneling information in key from
-+ * 'base->tunnel' into 'flow->tunnel', and then changes 'base->tunnel' in the
-+ * same way. In other words, operates the same as commit_odp_actions(), but
-+ * only on tunneling information. */
-+void
-+commit_odp_tunnel_set_action(const struct flow_tnl *tunnel, struct flow_tnl *base,
-+ struct ofpbuf *odp_actions)
-+{
-+ /* A valid IPV4_TUNNEL must have non-zero ip_dst. */
-+ if (tunnel->ip_dst) {
-+
-+ if (!memcmp(tunnel, base, sizeof *tunnel)) {
-+ return;
-+ }
-+ odp_put_tunnel_action(tunnel, odp_actions);
-+ }
-+}
-+
- static bool
- commit(enum ovs_key_attr attr, bool use_masked_set,
- const void *key, void *base, void *mask, size_t size,
-diff --git a/lib/odp-util.h b/lib/odp-util.h
-index 9f8e741..b30c246 100644
---- a/lib/odp-util.h
-+++ b/lib/odp-util.h
-@@ -245,6 +245,10 @@ const char *odp_key_fitness_to_string(enum odp_key_fitness);
-
- void commit_odp_tunnel_action(const struct flow *, struct flow *base,
- struct ofpbuf *odp_actions);
-+void commit_odp_tunnel_set_action(const struct flow_tnl *tunnel,
-+ struct flow_tnl *base,
-+ struct ofpbuf *odp_actions);
-+
- void commit_masked_set_action(struct ofpbuf *odp_actions,
- enum ovs_key_attr key_type, const void *key,
- const void *mask, size_t key_size);
-@@ -308,7 +312,8 @@ size_t odp_put_userspace_action(uint32_t pid,
- struct ofpbuf *odp_actions);
- void odp_put_tunnel_action(const struct flow_tnl *tunnel,
- struct ofpbuf *odp_actions);
--
- void odp_put_tnl_push_action(struct ofpbuf *odp_actions,
- struct ovs_action_push_tnl *data);
-+void odp_put_tnl_pop_spec_action(struct ofpbuf *odp_actions,
-+ struct ovs_action_pop_tnl *data);
- #endif /* odp-util.h */
-diff --git a/lib/ofp-print.c b/lib/ofp-print.c
-index d0c94ce..0684405 100644
---- a/lib/ofp-print.c
-+++ b/lib/ofp-print.c
-@@ -63,6 +63,7 @@ ofp_packet_to_string(const void *data, size_t len)
- struct flow flow;
- size_t l4_size;
-
-+ buf.md.tunnel.nsh_flags=0;
- dp_packet_use_const(&buf, data, len);
- flow_extract(&buf, &flow);
- flow_format(&ds, &flow);
-diff --git a/lib/packets.c b/lib/packets.c
-index a4d7854..d69d006 100644
---- a/lib/packets.c
-+++ b/lib/packets.c
-@@ -926,6 +926,26 @@ packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4],
- }
- }
-
-+void
-+packet_set_nsh(struct dp_packet *packet, struct flow_tnl *tun_key)
-+{
-+ struct eth_header *eth;
-+ struct nshhdr *nsh;
-+
-+ eth = (struct eth_header *) dp_packet_data(packet);
-+ struct ip_header *ip = (struct ip_header *) (eth + 1);
-+ struct udp_header *udp = (struct udp_header *) (ip + 1);
-+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
-+
-+ nsh = (struct nshhdr *) (vxg + 1);
-+ nsh->b.b2 = tun_key->nsp >> 8;
-+ nsh->b.svc_idx = tun_key->nsi;
-+ nsh->c.nshc1 = tun_key->nshc1;
-+ nsh->c.nshc2 = tun_key->nshc2;
-+ nsh->c.nshc3 = tun_key->nshc3;
-+ nsh->c.nshc4 = tun_key->nshc4;
-+}
-+
- const char *
- packet_tcp_flag_to_string(uint32_t flag)
- {
-diff --git a/lib/packets.h b/lib/packets.h
-index 7f9ab98..87c955a 100644
---- a/lib/packets.h
-+++ b/lib/packets.h
-@@ -33,6 +33,7 @@
- struct dp_packet;
- struct ds;
-
-+/* Tunnel information used in flow key and metadata. */
- struct flow_tnl {
- ovs_be32 ip_dst;
- ovs_be32 ip_src;
-@@ -44,12 +45,14 @@ struct flow_tnl {
- ovs_be16 tp_dst;
- ovs_be16 gbp_id;
- uint8_t gbp_flags;
-+ uint8_t nsh_flags;
- uint8_t nsi;
- ovs_be32 nsp;
- ovs_be32 nshc1;
- ovs_be32 nshc2;
- ovs_be32 nshc3;
- ovs_be32 nshc4;
-+ uint8_t pad1[7]; /* Pad to 64 bits. */
- struct tun_metadata metadata;
- };
-
-@@ -80,6 +83,8 @@ struct flow_tnl {
- #define FLOW_TNL_F_NSH_C3 (1 << 9)
- #define FLOW_TNL_F_NSH_C4 (1 << 10)
-
-+#define NSH_TNL_F_NODECAP (1 << 1)
-+
- /* Returns an offset to 'src' covering all the meaningful fields in 'src'. */
- static inline size_t
- flow_tnl_size(const struct flow_tnl *src)
-@@ -1055,6 +1060,7 @@ void packet_set_udp_port(struct dp_packet *, ovs_be16 src, ovs_be16 dst);
- void packet_set_sctp_port(struct dp_packet *, ovs_be16 src, ovs_be16 dst);
- void packet_set_nd(struct dp_packet *, const ovs_be32 target[4],
- const struct eth_addr sll, const struct eth_addr tll);
-+void packet_set_nsh(struct dp_packet *packet, struct flow_tnl *tun_key);
-
- void packet_format_tcp_flags(struct ds *, uint16_t);
- const char *packet_tcp_flag_to_string(uint32_t flag);
-diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
-index d479997..da8a9d1 100644
---- a/ofproto/ofproto-dpif-sflow.c
-+++ b/ofproto/ofproto-dpif-sflow.c
-@@ -1119,6 +1119,10 @@ dpif_sflow_read_actions(const struct flow *flow,
- sflow_actions->tunnel_err = true;
- break;
-
-+ case OVS_ACTION_ATTR_TUNNEL_POP_SPEC:
-+ sflow_actions->tunnel_err = true;
-+ break;
-+
- case OVS_ACTION_ATTR_TUNNEL_PUSH:
- /* XXX: This actions appears to come with it's own
- * OUTPUT action, so should it be regarded as having
-diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
-index 9c64c24..71e255e 100644
---- a/ofproto/ofproto-dpif-xlate.c
-+++ b/ofproto/ofproto-dpif-xlate.c
-@@ -171,7 +171,7 @@ struct xlate_ctx {
- * which might lead to an infinite loop. This could happen easily
- * if a tunnel is marked as 'ip_remote=flow', and the flow does not
- * actually set the tun_dst field. */
-- ovs_be32 orig_tunnel_ip_dst;
-+ struct flow_tnl orig_tunnel;
-
- /* Stack for the push and pop actions. Each stack element is of type
- * "union mf_subvalue". */
-@@ -2787,6 +2787,37 @@ build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport,
- return 0;
- }
-
-+static int
-+build_tunnel_pop(const struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct flow *flow)
-+{
-+ const struct netdev_tunnel_config * cfg;
-+
-+ cfg = tnl_port_cfg(tunnel_odp_port, flow);
-+
-+ if (cfg) {
-+ if (cfg->tun_nodecap) {
-+ struct ovs_action_pop_tnl tnl_pop_data;
-+ memset(&tnl_pop_data, 0, sizeof tnl_pop_data);
-+
-+ tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
-+ tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_NO_DECAP;
-+ odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
-+
-+ } else {
-+ nl_msg_put_odp_port(ctx->odp_actions,
-+ OVS_ACTION_ATTR_TUNNEL_POP,
-+ tunnel_odp_port);
-+ }
-+
-+ } else {
-+ nl_msg_put_odp_port(ctx->odp_actions,
-+ OVS_ACTION_ATTR_TUNNEL_POP,
-+ tunnel_odp_port);
-+ }
-+
-+ return 0;
-+}
-+
- static void
- xlate_commit_actions(struct xlate_ctx *ctx)
- {
-@@ -2970,7 +3001,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
- xlate_report(ctx, "Tunneling decided against output");
- goto out; /* restore flow_nw_tos */
- }
-- if (flow->tunnel.ip_dst == ctx->orig_tunnel_ip_dst) {
-+ if (flow->tunnel.ip_dst == ctx->orig_tunnel.ip_dst) {
- xlate_report(ctx, "Not tunneling to our own address");
- goto out; /* restore flow_nw_tos */
- }
-@@ -3011,6 +3042,10 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
- if (out_port != ODPP_NONE) {
- xlate_commit_actions(ctx);
-
-+ if (flow->tunnel.nsh_flags & NSH_TNL_F_NODECAP) {
-+ commit_odp_tunnel_set_action(&flow->tunnel, &ctx->orig_tunnel,
-+ ctx->odp_actions);
-+ }
- if (xr) {
- struct ovs_action_hash *act_hash;
-
-@@ -3040,10 +3075,11 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
- odp_tnl_port = tnl_port_map_lookup(flow, wc);
- }
-
-- if (odp_tnl_port != ODPP_NONE) {
-- nl_msg_put_odp_port(ctx->odp_actions,
-- OVS_ACTION_ATTR_TUNNEL_POP,
-- odp_tnl_port);
-+ if (odp_tnl_port != ODPP_NONE &&
-+ !(flow->tunnel.nsh_flags & NSH_TNL_F_NODECAP)) {
-+ flow_tnl = flow->tunnel;
-+ build_tunnel_pop(ctx, odp_tnl_port, flow);
-+ flow->tunnel = flow_tnl;
- } else {
- /* Tunnel push-pop action is not compatible with
- * IPFIX action. */
-@@ -4791,7 +4827,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
- .xin = xin,
- .xout = xout,
- .base_flow = *flow,
-- .orig_tunnel_ip_dst = flow->tunnel.ip_dst,
-+ .orig_tunnel = flow->tunnel,
- .xbridge = xbridge,
- .stack = OFPBUF_STUB_INITIALIZER(stack_stub),
- .rule = xin->rule,
-diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
-index 52e28fb..4606fb6 100644
---- a/ofproto/tunnel.c
-+++ b/ofproto/tunnel.c
-@@ -553,6 +553,55 @@ out:
- return out_port;
- }
-
-+const struct netdev_tunnel_config *
-+tnl_port_cfg(odp_port_t odp_port, struct flow *flow) OVS_EXCLUDED(rwlock)
-+{
-+ const struct netdev_tunnel_config *cfg = NULL;
-+ struct tnl_port *tnl_port;
-+
-+ fat_rwlock_rdlock(&rwlock);
-+ tnl_port = tnl_find_odp_port(odp_port);
-+ if (!tnl_port) {
-+ goto out;
-+ }
-+
-+ cfg = netdev_get_tunnel_config(tnl_port->netdev);
-+ ovs_assert(cfg);
-+
-+ 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;
-+ }
-+
-+ 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);
-+
-+out:
-+ fat_rwlock_unlock(&rwlock);
-+ return cfg;
-+}
-+
- static uint32_t
- tnl_hash(struct tnl_match *match)
- {
-@@ -590,6 +639,43 @@ tnl_find_exact(struct tnl_match *match, struct hmap *map)
- return NULL;
- }
-
-+static struct tnl_port *
-+tnl_find_exact_odp_port(odp_port_t odp_port, struct hmap *map)
-+ OVS_REQ_RDLOCK(rwlock)
-+{
-+ if (map) {
-+ struct tnl_port *tnl_port;
-+
-+ HMAP_FOR_EACH (tnl_port, match_node, map) {
-+ if (tnl_port->match.odp_port == odp_port) {
-+ return tnl_port;
-+ }
-+ }
-+ }
-+ return NULL;
-+}
-+
-+static struct tnl_port *
-+tnl_find_odp_port(odp_port_t odp_port) OVS_REQ_RDLOCK(rwlock)
-+{
-+ int i;
-+
-+ for (i = 0; i < N_MATCH_TYPES; i++) {
-+ struct hmap *map = tnl_match_maps[i];
-+
-+ if (map) {
-+ struct tnl_port *tnl_port;
-+
-+ tnl_port = tnl_find_exact_odp_port(odp_port, map);
-+ if (tnl_port) {
-+ return tnl_port;
-+ }
-+ }
-+ }
-+
-+ return NULL;
-+}
-+
- /* Returns the tnl_port that is the best match for the tunnel data in 'flow',
- * or NULL if no tnl_port matches 'flow'. */
- static struct tnl_port *
-diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h
-index 3e704fb..2b608ce 100644
---- a/ofproto/tunnel.h
-+++ b/ofproto/tunnel.h
-@@ -26,6 +26,7 @@
- * header information from the kernel. */
-
- struct ovs_action_push_tnl;
-+struct ovs_action_pop_tnl;
- struct ofport_dpif;
- struct netdev;
-
-@@ -43,6 +44,9 @@ bool tnl_process_ecn(struct flow *);
- odp_port_t tnl_port_send(const struct ofport_dpif *, struct flow *,
- struct flow_wildcards *wc);
-
-+const struct netdev_tunnel_config *
-+tnl_port_cfg(odp_port_t odp_port, struct flow *flow);
-+
- /* Returns true if 'flow' should be submitted to tnl_port_receive(). */
- static inline bool
- tnl_port_should_receive(const struct flow *flow)
-diff --git a/tests/tunnel.at b/tests/tunnel.at
-index 5ec5e6c..851afdc 100644
---- a/tests/tunnel.at
-+++ b/tests/tunnel.at
-@@ -645,6 +645,34 @@ AT_CHECK([tail -1 stdout], [0],
- OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
- AT_CLEANUP
-
-+AT_SETUP([tunnel - VXLAN-GPE NSH tun_nodecap - user space])
-+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
-+AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0])
-+AT_CHECK([ovs-vsctl add-port int-br p1 -- set Interface p1 type=vxlan options:key=flow \
-+ options:remote_ip=1.1.1.1 options:tun_nodecap=true options:nsi=flow options:nsp=flow options:nshc1=flow options:in_key=flow options:dst_port=4790 ofport_request=2])
-+AT_CHECK([ovs-vsctl add-port int-br p2 -- set Interface p2 type=dummy ofport_request=3])
-+
-+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
-+])
-+AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK
-+])
-+
-+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
-+
-+AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
-+Listening ports:
-+vxlan_sys_4790 (4790)
-+])
-+
-+dnl remote_ip p0
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=8,dst=4790)'], [0], [stdout])
-+AT_CHECK([tail -1 stdout], [0],
-+ [Datapath actions: tnl_pop_spec(tnl_port(4790),pop_type=2)
-+])
-+
-+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/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 \
---
-1.9.3
-
diff --git a/ovs-nsh/patches/060682.patch b/ovs-nsh/patches/060682.patch
deleted file mode 100644
index c6ccd25..0000000
--- a/ovs-nsh/patches/060682.patch
+++ /dev/null
@@ -1,730 +0,0 @@
-Ethernet-NSH packet.
-
-With this feature(options:nsh-convert=true),when VxLAN-GPE NSH packets (Outer
-MAC header + Outer IP header + UDP header + VxLAN-GPE + NSH + original packet)
-are received by VxLAN-GPE NSH port, the vport will remove Outer MAC header,
-Outer IP header, UDP header, VxLAN-GPE header, and then modify and push the
-outer MAC header. Then the packet with VxLAN-GPE+NSH format is converted to
-Outer MAC header + NSH header + original packet.
-
-Signed-off-by: Ricky Li <ricky.li at intel.com>
-Signed-off-by: Mengke Liu <mengke.liu at intel.com>
----
- datapath/linux/compat/include/linux/openvswitch.h | 6 +-
- lib/netdev-vport.c | 109 +++++++++++++++++++++-
- lib/netdev.h | 6 ++
- lib/odp-util.c | 80 ++++++++++++++--
- lib/ovs-router.c | 64 +++++++++++++
- lib/ovs-router.h | 1 +
- lib/packets.h | 13 ++-
- ofproto/ofproto-dpif-xlate.c | 64 ++++++++++++-
- ofproto/tunnel.c | 37 ++++++++
- ofproto/tunnel.h | 5 +
- tests/tunnel.at | 32 +++++++
- 11 files changed, 398 insertions(+), 19 deletions(-)
-
-diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
-index b8ac152..3d588bb 100644
---- a/datapath/linux/compat/include/linux/openvswitch.h
-+++ b/datapath/linux/compat/include/linux/openvswitch.h
-@@ -230,6 +230,7 @@ enum ovs_vport_type {
- OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */
- OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */
- OVS_VPORT_TYPE_STT = 106, /* STT tunnel */
-+ OVS_VPORT_TYPE_NSH, /* L2+NSH ENCAP tunnel. */
- __OVS_VPORT_TYPE_MAX
- };
-
-@@ -646,7 +647,10 @@ struct ovs_action_push_tnl {
- uint8_t header[TNL_PUSH_HEADER_SIZE];
- };
-
--#define OVS_POP_SPEC_ACTION_NO_DECAP 2
-+enum ovs_pop_spec_action_type {
-+ OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH,
-+ OVS_POP_SPEC_ACTION_NO_DECAP = 2,
-+};
-
- /*
- * struct ovs_action_pop_tnl - %OVS_ACTION_ATTR_TUNNEL_POP_SPEC
-diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
-index d926c00..6e0d5ba 100644
---- a/lib/netdev-vport.c
-+++ b/lib/netdev-vport.c
-@@ -561,6 +561,23 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
- } else {
- tnl_cfg.ip_dst = in_addr.s_addr;
- }
-+ } else if (!strcmp(node->key, "remote_mac")) {
-+ if (!strcmp(node->value, "flow")) {
-+ tnl_cfg.eth_dst_flow = true;
-+ VLOG_ERR("remote_mac doesn't support setting by flow");
-+ return EINVAL;
-+ } else if (eth_addr_from_string(node->value,&tnl_cfg.eth_dst)){
-+ tnl_cfg.eth_dst_present = true;
-+ } else {
-+ VLOG_WARN("%s: bad %s 'remote_mac'", name, type);
-+ return EINVAL;
-+ }
-+ } else if (!strcmp(node->key, "nsh_convert")) {
-+ if (!strcmp(node->value, "true")) {
-+ tnl_cfg.nsh_convert = true;
-+ } else {
-+ tnl_cfg.nsh_convert = false;
-+ }
- } else if (!strcmp(node->key, "tun_nodecap")) {
- if (!strcmp(node->value, "true")) {
- tnl_cfg.tun_nodecap = true;
-@@ -883,6 +900,13 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
- if (!tnl_cfg.dont_fragment) {
- smap_add(args, "df_default", "false");
- }
-+ if (tnl_cfg.eth_dst_present) {
-+ smap_add_format(args, "remote_mac", ETH_ADDR_FMT, ETH_ADDR_ARGS(tnl_cfg.eth_dst));
-+ }
-+
-+ if (tnl_cfg.nsh_convert) {
-+ smap_add(args, "nsh_convert", "true");
-+ }
-
- if (tnl_cfg.tun_nodecap) {
- smap_add(args, "tun_nodecap", "true");
-@@ -1546,6 +1570,84 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
- }
-
- static int
-+vxlan_extract_md_convert_to_eth_nsh(struct dp_packet *packet, const struct ovs_action_pop_tnl *data)
-+{
-+ struct pkt_metadata *md = &packet->md;
-+ struct flow_tnl *tnl = &md->tunnel;
-+ struct udp_header *udp;
-+
-+ memset(md, 0, sizeof *md);
-+ if (VXLAN_HLEN > dp_packet_size(packet)) {
-+ return EINVAL;
-+ }
-+
-+ udp = ip_extract_tnl_md(packet, tnl);
-+ if (!udp) {
-+ return EINVAL;
-+ }
-+
-+ if (ntohs(udp->udp_dst) == VXGPE_DST_PORT) {
-+
-+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
-+
-+ if (get_16aligned_be32(&vxg->vx_vni) & htonl(0xff)) {
-+ VLOG_WARN_RL(&err_rl, "invalid vxlan-gpe vni=%#x\n",
-+ ntohl(get_16aligned_be32(&vxg->vx_vni)));
-+ return EINVAL;
-+ }
-+
-+ tnl->tp_src = udp->udp_src;
-+ tnl->tp_dst = udp->udp_dst;
-+ tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxg->vx_vni)) >> 8);
-+
-+ if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
-+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
-+ struct eth_header *eth = NULL;
-+
-+ tnl->nsp = nsh->b.b2 << 8;
-+ tnl->nsi = nsh->b.svc_idx;
-+ tnl->nshc1 = nsh->c.nshc1;
-+ tnl->nshc2 = nsh->c.nshc2;
-+ tnl->nshc3 = nsh->c.nshc3;
-+ tnl->nshc4 = nsh->c.nshc4;
-+ tnl->flags |= FLOW_TNL_F_NSP;
-+ tnl->flags |= FLOW_TNL_F_NSI;
-+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
-+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
-+ tnl->nsh_flags = NSH_TNL_F_ETHERNET;
-+
-+ dp_packet_reset_packet(packet, VXNSH_HLEN - sizeof (struct nshhdr));
-+ eth = (struct eth_header *) dp_packet_push_uninit(packet, data->header_len);
-+ memcpy(eth, data->header, data->header_len);
-+ eth->eth_type = htons(ETH_TYPE_NSH);
-+ } else {
-+ VLOG_WARN("Unsupported vxlan GPE + NSH format!");
-+ return EINVAL;
-+ }
-+
-+ } else {
-+
-+ struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
-+
-+ if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
-+ (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
-+ VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
-+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
-+ ntohl(get_16aligned_be32(&vxh->vx_vni)));
-+ return EINVAL;
-+ }
-+
-+ tnl->tp_src = udp->udp_src;
-+ tnl->tp_dst = udp->udp_dst;
-+ tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
-+ dp_packet_reset_packet(packet, VXLAN_HLEN);
-+ }
-+
-+ return 0;
-+
-+}
-+
-+static int
- vxlan_extract_md_no_decap(struct dp_packet *packet)
- {
- struct pkt_metadata *md = &packet->md;
-@@ -1595,6 +1697,7 @@ vxlan_extract_md_no_decap(struct dp_packet *packet)
- tnl->flags |= FLOW_TNL_F_NSI;
- tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
- FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
-+ tnl->tun_len = VXNSH_HLEN;
- tnl->nsh_flags = NSH_TNL_F_NODECAP;
- } else {
- VLOG_WARN("Unsupported vxlan GPE + NSH format!");
-@@ -1606,19 +1709,19 @@ vxlan_extract_md_no_decap(struct dp_packet *packet)
- return 0;
- }
-
--
- static int
- netdev_vxlan_pop_header_spec(struct dp_packet *packet,
- const struct ovs_action_pop_tnl *data)
- {
-- if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
-+ if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
-+ return vxlan_extract_md_convert_to_eth_nsh(packet, data);
-+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
- return vxlan_extract_md_no_decap(packet);
- }
-
- return EINVAL;
- }
-
--
- static int
- netdev_vxlan_build_header(const struct netdev *netdev,
- struct ovs_action_push_tnl *data,
-diff --git a/lib/netdev.h b/lib/netdev.h
-index b30c932..26013ef 100644
---- a/lib/netdev.h
-+++ b/lib/netdev.h
-@@ -150,6 +150,10 @@ struct netdev_tunnel_config {
- bool ipsec;
- bool dont_fragment;
-
-+ bool eth_dst_present;
-+ bool eth_dst_flow;
-+ struct eth_addr eth_dst;
-+
- bool in_nshc1_present;
- bool in_nshc1_flow;
- ovs_be32 in_nshc1; /* incoming NSH context c1 */
-@@ -182,6 +186,7 @@ struct netdev_tunnel_config {
- bool out_nshc4_flow;
- ovs_be32 out_nshc4; /* outgoing NSH context c4 */
-
-+ bool nsh_convert;
- bool tun_nodecap;
-
- };
-@@ -247,6 +252,7 @@ int netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers,
- int netdev_pop_header_spec(struct netdev *netdev,
- struct dp_packet **buffers, int cnt,
- const struct ovs_action_pop_tnl *data);
-+
- /* Hardware address. */
- int netdev_set_etheraddr(struct netdev *, const struct eth_addr mac);
- int netdev_get_etheraddr(const struct netdev *, struct eth_addr *mac);
-diff --git a/lib/odp-util.c b/lib/odp-util.c
-index 190117f..6da2d5b 100644
---- a/lib/odp-util.c
-+++ b/lib/odp-util.c
-@@ -552,16 +552,22 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
- }
-
- static void
--format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
-+format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data)
- {
-- struct ovs_action_pop_tnl *data;
-+ const struct eth_header *eth;
-
-- data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
-+ eth = (const struct eth_header *)data->header;
-+ if (data->tnl_type == OVS_VPORT_TYPE_NSH) {
-+ const struct nshhdr *nsh = (const struct nshhdr *) (eth + 1);
-
-- ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
-- if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
-- ds_put_format(ds, "pop_type=%"PRIu16")",
-- OVS_POP_SPEC_ACTION_NO_DECAP);
-+ /* Ethernet */
-+ ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
-+ data->header_len, data->tnl_type);
-+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
-+ ds_put_format(ds, ",src=");
-+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
-+ ds_put_format(ds, ",dl_type=0x%04"PRIx16")", ntohs(eth->eth_type));
-+ ds_put_format(ds, "),");
- }
- }
-
-@@ -578,6 +584,26 @@ format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr)
- }
-
- static void
-+format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
-+{
-+ struct ovs_action_pop_tnl *data;
-+
-+ data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
-+
-+ ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
-+ if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
-+ ds_put_format(ds, "pop_type=%"PRIu16",",
-+ OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH);
-+ format_odp_tnl_pop_header(ds, data);
-+ ds_put_format(ds, "out_port(%"PRIu32"))", data->out_port);
-+
-+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
-+ ds_put_format(ds, "pop_type=%"PRIu16")",
-+ OVS_POP_SPEC_ACTION_NO_DECAP);
-+ }
-+}
-+
-+static void
- format_odp_action(struct ds *ds, const struct nlattr *a)
- {
- int expected_len;
-@@ -1050,11 +1076,8 @@ static int
- ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
- {
- struct eth_header *eth;
-- struct nshhdr *nsh;
- uint32_t tnl_type = 0, header_len = 0;
- uint16_t dl_type;
-- ovs_be32 nsp, nshc1, nshc2, nshc3, nshc4;
-- uint8_t nsi;
- int n = 0;
- if (!ovs_scan_len(s, &n, "tnl_pop_spec(tnl_port(%"SCNi32"),",
- &data->tnl_port)) {
-@@ -1068,6 +1091,42 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
-
- if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
- return n;
-+
-+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
-+
-+ eth = (struct eth_header *) data->header;
-+
-+ if (!ovs_scan_len(s, &n, ",header(size=%"SCNi32",type=%"SCNi32","
-+ "eth(dst="ETH_ADDR_SCAN_FMT",",
-+ &data->header_len,
-+ &data->tnl_type,
-+ ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
-+ return -EINVAL;
-+ }
-+ if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
-+ ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
-+ return -EINVAL;
-+ }
-+ if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
-+ return -EINVAL;
-+ }
-+ eth->eth_type = htons(dl_type);
-+
-+ tnl_type = OVS_VPORT_TYPE_NSH;
-+ header_len = sizeof *eth;
-+
-+ /* check tunnel meta data. */
-+ if (data->tnl_type != tnl_type) {
-+ return -EINVAL;
-+ }
-+ if (data->header_len != header_len) {
-+ return -EINVAL;
-+ }
-+
-+ /* Out port */
-+ if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
-+ return -EINVAL;
-+ }
- } else {
- return -EINVAL;
- }
-@@ -1075,6 +1134,7 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
- return n;
- }
-
-+
- static int
- parse_odp_action(const char *s, const struct simap *port_names,
- struct ofpbuf *actions)
-diff --git a/lib/ovs-router.c b/lib/ovs-router.c
-index d6c7652..9f61bac 100644
---- a/lib/ovs-router.c
-+++ b/lib/ovs-router.c
-@@ -82,6 +82,24 @@ ovs_router_lookup(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw)
- return route_table_fallback_lookup(ip_dst, output_bridge, gw);
- }
-
-+bool
-+ovs_router_lookup_mac(const struct eth_addr *mac, char output_bridge[])
-+{
-+ const struct cls_rule *cr;
-+ struct flow s_flow;
-+
-+ memset(&s_flow, 0, sizeof (struct flow));
-+ memcpy(s_flow.dl_dst.ea, mac->ea, ETH_ADDR_LEN);
-+ cr = classifier_lookup(&cls,CLS_MAX_VERSION, &s_flow, NULL);
-+ if (cr) {
-+ struct ovs_router_entry *p = ovs_router_entry_cast(cr);
-+
-+ strncpy(output_bridge, p->output_bridge, IFNAMSIZ);
-+ return true;
-+ }
-+ return false;
-+}
-+
- static void
- rt_entry_free(struct ovs_router_entry *p)
- {
-@@ -133,6 +151,36 @@ ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, uint8_t plen,
- seq_change(tnl_conf_seq);
- }
-
-+static void
-+ovs_router_insert_mac__(uint8_t priority, struct eth_addr *mac,
-+ const char output_bridge[])
-+{
-+ const struct cls_rule *cr;
-+ struct ovs_router_entry *p;
-+ struct match s_match;
-+
-+ memset(&s_match, 0, sizeof (struct match));
-+ memcpy(s_match.flow.dl_dst.ea, mac->ea, ETH_ADDR_LEN);
-+
-+ p = xzalloc(sizeof *p);
-+ strncpy(p->output_bridge, output_bridge, IFNAMSIZ);
-+ p->gw = 0;
-+ p->nw_addr = 0;
-+ p->plen = 32;
-+ p->priority = priority;
-+ cls_rule_init(&p->cr, &s_match, priority); /* Longest prefix matches first. */
-+
-+ ovs_mutex_lock(&mutex);
-+ cr = classifier_replace(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0);
-+ ovs_mutex_unlock(&mutex);
-+
-+ if (cr) {
-+ /* An old rule with the same match was displaced. */
-+ ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
-+ }
-+ seq_change(tnl_conf_seq);
-+}
-+
- void
- ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[],
- ovs_be32 gw)
-@@ -231,6 +279,20 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
- }
-
- static void
-+ovs_router_add_mac(struct unixctl_conn *conn, int argc OVS_UNUSED,
-+ const char *argv[], void *aux OVS_UNUSED)
-+{
-+ struct eth_addr mac;
-+
-+ if (eth_addr_from_string(argv[1], &mac)) {
-+ ovs_router_insert_mac__(48, &mac, argv[2]);
-+ unixctl_command_reply(conn, "OK");
-+ } else {
-+ unixctl_command_reply(conn, "Invalid parameters");
-+ }
-+}
-+
-+static void
- ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[], void *aux OVS_UNUSED)
- {
-@@ -326,6 +388,8 @@ ovs_router_init(void)
- classifier_init(&cls, NULL);
- unixctl_command_register("ovs/route/add", "ipv4_addr/prefix_len out_br_name gw", 2, 3,
- ovs_router_add, NULL);
-+ unixctl_command_register("ovs/route/addmac", "mac_addr out_br_name", 2, 2,
-+ ovs_router_add_mac, NULL);
- unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
- unixctl_command_register("ovs/route/del", "ipv4_addr/prefix_len", 1, 1, ovs_router_del,
- NULL);
-diff --git a/lib/ovs-router.h b/lib/ovs-router.h
-index cc0ebc2..3f5a504 100644
---- a/lib/ovs-router.h
-+++ b/lib/ovs-router.h
-@@ -23,6 +23,7 @@
- extern "C" {
- #endif
-
-+bool ovs_router_lookup_mac(const struct eth_addr *mac, char output_bridge[]);
- bool ovs_router_lookup(ovs_be32 ip_dst, char out_dev[], ovs_be32 *gw);
- void ovs_router_init(void);
- void ovs_router_insert(ovs_be32 ip_dst, uint8_t plen,
-diff --git a/lib/packets.h b/lib/packets.h
-index 87c955a..c586390 100644
---- a/lib/packets.h
-+++ b/lib/packets.h
-@@ -33,6 +33,8 @@
- struct dp_packet;
- struct ds;
-
-+#define ETH_ADDR_LEN 6
-+
- /* Tunnel information used in flow key and metadata. */
- struct flow_tnl {
- ovs_be32 ip_dst;
-@@ -52,7 +54,9 @@ struct flow_tnl {
- ovs_be32 nshc2;
- ovs_be32 nshc3;
- ovs_be32 nshc4;
-- uint8_t pad1[7]; /* Pad to 64 bits. */
-+ struct eth_addr eth_dst;
-+ uint8_t tun_len;
-+ uint8_t pad1[4]; /* Pad to 64 bits. */
- struct tun_metadata metadata;
- };
-
-@@ -83,7 +87,9 @@ struct flow_tnl {
- #define FLOW_TNL_F_NSH_C3 (1 << 9)
- #define FLOW_TNL_F_NSH_C4 (1 << 10)
-
--#define NSH_TNL_F_NODECAP (1 << 1)
-+#define NSH_TNL_F_ETHERNET (1 << 0)
-+#define NSH_TNL_F_VXLAN (1 << 1)
-+#define NSH_TNL_F_NODECAP (1 << 2)
-
- /* Returns an offset to 'src' covering all the meaningful fields in 'src'. */
- static inline size_t
-@@ -160,8 +166,6 @@ pkt_metadata_init(struct pkt_metadata *md, odp_port_t port)
-
- bool dpid_from_string(const char *s, uint64_t *dpidp);
-
--#define ETH_ADDR_LEN 6
--
- static const struct eth_addr eth_addr_broadcast OVS_UNUSED
- = { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } };
-
-@@ -352,6 +356,7 @@ ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos,
- #define ETH_TYPE_RARP 0x8035
- #define ETH_TYPE_MPLS 0x8847
- #define ETH_TYPE_MPLS_MCAST 0x8848
-+#define ETH_TYPE_NSH 0x894f
-
- static inline bool eth_type_mpls(ovs_be16 eth_type)
- {
-diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
-index 71e255e..bff0a83 100644
---- a/ofproto/ofproto-dpif-xlate.c
-+++ b/ofproto/ofproto-dpif-xlate.c
-@@ -2690,6 +2690,36 @@ tnl_route_lookup_flow(const struct flow *oflow,
- }
-
- static int
-+tnl_outdev_lookup_mac(const struct eth_addr *mac,
-+ struct xport **out_port)
-+{
-+ char out_dev[IFNAMSIZ];
-+ struct xbridge *xbridge;
-+ struct xlate_cfg *xcfg;
-+
-+ if (!ovs_router_lookup_mac(mac, out_dev)) {
-+ return -ENOENT;
-+ }
-+
-+ xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
-+ ovs_assert(xcfg);
-+
-+ HMAP_FOR_EACH (xbridge, hmap_node, &xcfg->xbridges) {
-+ if (!strncmp(xbridge->name, out_dev, IFNAMSIZ)) {
-+ struct xport *port;
-+
-+ HMAP_FOR_EACH (port, ofp_node, &xbridge->xports) {
-+ if (!strncmp(netdev_get_name(port->netdev), out_dev, IFNAMSIZ)) {
-+ *out_port = port;
-+ return 0;
-+ }
-+ }
-+ }
-+ }
-+ return -ENOENT;
-+}
-+
-+static int
- compose_table_xlate(struct xlate_ctx *ctx, const struct xport *out_dev,
- struct dp_packet *packet)
- {
-@@ -2795,7 +2825,39 @@ build_tunnel_pop(const struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct
- cfg = tnl_port_cfg(tunnel_odp_port, flow);
-
- if (cfg) {
-- if (cfg->tun_nodecap) {
-+ if (cfg->nsh_convert && (ntohs(cfg->dst_port) == VXGPE_DST_PORT)) {
-+ struct ovs_action_pop_tnl tnl_pop_data;
-+ struct xport *out_dev = NULL;
-+ struct eth_addr smac;
-+
-+ int err;
-+
-+ err = tnl_outdev_lookup_mac(&cfg->eth_dst, &out_dev);
-+ if (err) {
-+ VLOG_WARN("tnl_outdev_lookup_mac failed...");
-+ return err;
-+ }
-+
-+ /* Use mac addr of bridge port of the peer. */
-+ err = netdev_get_etheraddr(out_dev->netdev, &smac);
-+ if (err) {
-+ VLOG_WARN("netdev_get_etheraddr failed...");
-+ return err;
-+ }
-+
-+ err = tnl_port_build_nsh_header_odport_popspec(tunnel_odp_port, flow,
-+ &cfg->eth_dst, &smac, &tnl_pop_data);
-+ if (err) {
-+ VLOG_WARN("tnl_port_build_nsh_header failed...");
-+ return err;
-+ }
-+ tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
-+ tnl_pop_data.out_port = odp_to_u32(out_dev->odp_port);
-+ tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH;
-+ tnl_pop_data.tnl_type = OVS_VPORT_TYPE_NSH;
-+ odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
-+
-+ } else if (cfg->tun_nodecap) {
- struct ovs_action_pop_tnl tnl_pop_data;
- memset(&tnl_pop_data, 0, sizeof tnl_pop_data);
-
-diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
-index 4606fb6..b0e46e6 100644
---- a/ofproto/tunnel.c
-+++ b/ofproto/tunnel.c
-@@ -45,6 +45,8 @@ VLOG_DEFINE_THIS_MODULE(tunnel);
- /* skb mark used for IPsec tunnel packets */
- #define IPSEC_MARK 1
-
-+#define ETH_NSH_HLEN (sizeof(struct eth_header) + \
-+ sizeof(struct nshhdr))
- struct tnl_match {
- ovs_be64 in_key;
- ovs_be32 in_nsp;
-@@ -568,6 +570,9 @@ tnl_port_cfg(odp_port_t odp_port, struct flow *flow) OVS_EXCLUDED(rwlock)
- cfg = netdev_get_tunnel_config(tnl_port->netdev);
- ovs_assert(cfg);
-
-+ if (!cfg->eth_dst_flow) {
-+ memcpy(flow->tunnel.eth_dst.ea, cfg->eth_dst.ea, ETH_ADDR_LEN);
-+ }
- if (!cfg->out_nsp_flow) {
- flow->tunnel.nsp = cfg->out_nsp;
- }
-@@ -602,6 +607,7 @@ out:
- return cfg;
- }
-
-+
- static uint32_t
- tnl_hash(struct tnl_match *match)
- {
-@@ -1063,3 +1069,34 @@ tnl_port_build_header(const struct ofport_dpif *ofport,
-
- return res;
- }
-+
-+int
-+tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port,
-+ const struct flow *tnl_flow OVS_UNUSED,
-+ const struct eth_addr *dmac,
-+ const struct eth_addr *smac,
-+ struct ovs_action_pop_tnl *data)
-+{
-+ struct tnl_port *tnl_port;
-+ struct eth_header *eth;
-+ int res = 0;
-+
-+ fat_rwlock_rdlock(&rwlock);
-+ tnl_port = tnl_find_odp_port(odp_port);
-+ ovs_assert(tnl_port);
-+
-+ /* Build Ethernet and IP headers. */
-+ memset(data->header, 0, sizeof data->header);
-+
-+ eth = (struct eth_header *)data->header;
-+ memcpy(eth->eth_dst.ea, dmac->ea, ETH_ADDR_LEN);
-+ memcpy(eth->eth_src.ea, smac->ea, ETH_ADDR_LEN);
-+ eth->eth_type = htons(ETH_TYPE_NSH);
-+
-+ data->header_len = ETH_NSH_HLEN - sizeof (struct nshhdr);
-+ data->tnl_type = OVS_VPORT_TYPE_NSH;
-+
-+ fat_rwlock_unlock(&rwlock);
-+
-+ return res;
-+}
-diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h
-index 2b608ce..0c51a4e 100644
---- a/ofproto/tunnel.h
-+++ b/ofproto/tunnel.h
-@@ -59,4 +59,9 @@ 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);
-+int tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port,
-+ const struct flow *tnl_flow OVS_UNUSED,
-+ const struct eth_addr *dmac,
-+ const struct eth_addr *smac,
-+ struct ovs_action_pop_tnl *data);
- #endif /* tunnel.h */
-diff --git a/tests/tunnel.at b/tests/tunnel.at
-index 851afdc..dc35809 100644
---- a/tests/tunnel.at
-+++ b/tests/tunnel.at
-@@ -673,6 +673,38 @@ AT_CHECK([tail -1 stdout], [0],
- OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
- AT_CLEANUP
-
-+AT_SETUP([tunnel - VXLAN-GPE NSH - nsh_convert from VXLAN-GPE NSH to Ethernet NSH - user space])
-+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
-+AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0])
-+AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=vxlan options:remote_ip=1.1.1.1 options:dst_port=4790 \
-+ options:nsh_convert=true options:nsi=flow options:nsp=flow options:nshc1=flow options:in_key=flow options:remote_mac=00:00:00:11:11:22 ofport_request=2])
-+AT_CHECK([ovs-vsctl add-port int-br p2 -- set Interface p2 type=dummy ofport_request=3])
-+
-+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
-+])
-+AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK
-+])
-+AT_CHECK([ovs-appctl ovs/route/addmac 00:00:00:11:11:22 br0],[0],[dnl
-+OK
-+])
-+
-+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
-+
-+AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
-+Listening ports:
-+vxlan_sys_4790 (4790)
-+])
-+
-+dnl remote_ip p0
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=8,dst=4790)'], [0], [stdout])
-+
-+AT_CHECK([tail -1 stdout], [0],
-+ [Datapath actions: tnl_pop_spec(tnl_port(4790),pop_type=0,header(size=14,type=107,eth(dst=00:00:00:11:11:22,src=aa:55:aa:55:00:00,dl_type=0x894f)),out_port(100))
-+])
-+
-+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/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 \
---
-1.9.3
-
diff --git a/ovs-nsh/patches/060683.patch b/ovs-nsh/patches/060683.patch
deleted file mode 100644
index 008f430..0000000
--- a/ovs-nsh/patches/060683.patch
+++ /dev/null
@@ -1,1334 +0,0 @@
-decapsulation and encapsulation implementation at data plane level in user
-space and modify the related codes at control plane level in user space.
-
-When vport of Ethernet-NSH-Netdev receives an original packet, it will
-encapsulate the packet with NSH and Ethernet header. The required information
-for encapsulation is stored in vport configuration and rules when
-related field are set 'flow'.
-
-When Ethernet NSH packet are received, the tunnel port will be lookuped by the
-Ethernet type(0X894F), if the Ethernet NSH port are found, the decapsulation
-will be implemented. The tunnel pop actions will be implemented and the related
-fields will be parsed.
-
-Signed-off-by: Ricky Li <ricky.li at intel.com>
-Signed-off-by: Mengke Liu <mengke.liu at intel.com>
----
- datapath/linux/compat/include/linux/openvswitch.h | 2 +
- lib/dpif-netlink.c | 3 +
- lib/dpif.c | 3 +-
- lib/flow.c | 3 +-
- lib/match.c | 1 +
- lib/netdev-vport.c | 99 ++++-
- lib/odp-util.c | 503 +++++++++++++---------
- lib/packets.c | 24 +-
- lib/packets.h | 15 +-
- lib/tnl-ports.c | 32 ++
- lib/tnl-ports.h | 2 +
- ofproto/ofproto-dpif-ipfix.c | 4 +
- ofproto/ofproto-dpif-xlate.c | 55 ++-
- ofproto/tunnel.c | 54 ++-
- ofproto/tunnel.h | 8 +-
- tests/tunnel.at | 60 +++
- 16 files changed, 620 insertions(+), 248 deletions(-)
-
-diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
-index 3d588bb..045a1f4 100644
---- a/datapath/linux/compat/include/linux/openvswitch.h
-+++ b/datapath/linux/compat/include/linux/openvswitch.h
-@@ -358,6 +358,8 @@ enum ovs_tunnel_key_attr {
- OVS_TUNNEL_KEY_ATTR_ID, /* be64 Tunnel ID */
- OVS_TUNNEL_KEY_ATTR_IPV4_SRC, /* be32 src IP address. */
- OVS_TUNNEL_KEY_ATTR_IPV4_DST, /* be32 dst IP address. */
-+ OVS_TUNNEL_KEY_ATTR_ETH_SRC, /* Ethernet src . */
-+ OVS_TUNNEL_KEY_ATTR_ETH_DST, /* Ethernet src . */
- OVS_TUNNEL_KEY_ATTR_TOS, /* u8 Tunnel IP ToS. */
- OVS_TUNNEL_KEY_ATTR_TTL, /* u8 Tunnel IP TTL. */
- OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT, /* No argument, set DF. */
-diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
-index ffeb124..cd9a9b5 100644
---- a/lib/dpif-netlink.c
-+++ b/lib/dpif-netlink.c
-@@ -762,6 +762,9 @@ get_vport_type(const struct dpif_netlink_vport *vport)
- case OVS_VPORT_TYPE_VXLAN:
- return "vxlan";
-
-+ case OVS_VPORT_TYPE_NSH:
-+ return "eth_nsh";
-+
- case OVS_VPORT_TYPE_LISP:
- return "lisp";
-
-diff --git a/lib/dpif.c b/lib/dpif.c
-index bb2d519..5bbdb96 100644
---- a/lib/dpif.c
-+++ b/lib/dpif.c
-@@ -1108,7 +1108,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt,
- uint64_t stub[256 / 8];
- struct pkt_metadata *md = &packet->md;
-
-- if (md->tunnel.ip_dst) {
-+ if (md->tunnel.ip_dst ||
-+ md->tunnel.nsh_flags & NSH_TNL_F_ETHERNET_PARSED) {
- /* The Linux kernel datapath throws away the tunnel information
- * that we supply as metadata. We have to use a "set" action to
- * supply it. */
-diff --git a/lib/flow.c b/lib/flow.c
-index 2cbfb6d..7572f01 100644
---- a/lib/flow.c
-+++ b/lib/flow.c
-@@ -449,7 +449,8 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
- uint8_t nw_frag, nw_tos, nw_ttl, nw_proto;
-
- /* Metadata. */
-- if (md->tunnel.ip_dst) {
-+ if (md->tunnel.ip_dst ||
-+ md->tunnel.nsh_flags & NSH_TNL_F_ETHERNET_PARSED) {
- miniflow_push_words(mf, tunnel, &md->tunnel,
- offsetof(struct flow_tnl, metadata) /
- sizeof(uint64_t));
-diff --git a/lib/match.c b/lib/match.c
-index 7f7bd4d..d2337c5 100644
---- a/lib/match.c
-+++ b/lib/match.c
-@@ -965,6 +965,7 @@ format_flow_tunnel(struct ds *s, const struct match *match)
- 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);
-+ format_eth_masked(s, "tun_eth_dst", tnl->eth_dst, wc->masks.tunnel.eth_dst);
-
- if (wc->masks.tunnel.nsi) {
- ds_put_format(s, "nsi=%"PRIu8",", tnl->nsi);
-diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
-index 6e0d5ba..0a3da8d 100644
---- a/lib/netdev-vport.c
-+++ b/lib/netdev-vport.c
-@@ -66,6 +66,8 @@ static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5);
- sizeof(struct ip_header) + \
- sizeof(struct udp_header) + \
- sizeof(struct genevehdr))
-+#define ETH_NSH_HLEN (sizeof(struct eth_header) + \
-+ sizeof(struct nshhdr))
-
- #define VXNSH_HLEN (sizeof(struct eth_header) + \
- sizeof(struct ip_header) + \
-@@ -734,7 +736,7 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
- }
- }
-
-- if (!tnl_cfg.ip_dst && !tnl_cfg.ip_dst_flow) {
-+ if (!tnl_cfg.ip_dst && !tnl_cfg.ip_dst_flow && strcmp(type, "eth_nsh")) {
- VLOG_ERR("%s: %s type requires valid 'remote_ip' argument",
- name, type);
- return EINVAL;
-@@ -756,7 +758,7 @@ 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)) {
-+ if (tnl_cfg.dst_port == htons(VXGPE_DST_PORT) || !strcmp(type, "eth_nsh")) {
- tnl_cfg.in_nsp = parse_nsp(args, "in_nsp",
- &tnl_cfg.in_nsp_present,
- &tnl_cfg.in_nsp_flow);
-@@ -1541,7 +1543,6 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
- tnl->flags |= FLOW_TNL_F_NSI;
- tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
- FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
--
- dp_packet_reset_packet(packet, VXNSH_HLEN);
- } else {
- VLOG_WARN("Unsupported vxlan GPE + NSH format!");
-@@ -1614,7 +1615,7 @@ vxlan_extract_md_convert_to_eth_nsh(struct dp_packet *packet, const struct ovs_a
- tnl->flags |= FLOW_TNL_F_NSI;
- tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
- FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
-- tnl->nsh_flags = NSH_TNL_F_ETHERNET;
-+ tnl->nsh_flags = NSH_TNL_F_ETHERNET_PRST;
-
- dp_packet_reset_packet(packet, VXNSH_HLEN - sizeof (struct nshhdr));
- eth = (struct eth_header *) dp_packet_push_uninit(packet, data->header_len);
-@@ -1698,7 +1699,7 @@ vxlan_extract_md_no_decap(struct dp_packet *packet)
- tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
- FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
- tnl->tun_len = VXNSH_HLEN;
-- tnl->nsh_flags = NSH_TNL_F_NODECAP;
-+ tnl->nsh_flags = NSH_TNL_F_NODECAP | NSH_TNL_F_VXLAN_PRST;
- } else {
- VLOG_WARN("Unsupported vxlan GPE + NSH format!");
- return EINVAL;;
-@@ -1827,6 +1828,90 @@ netdev_vxlan_push_header(struct dp_packet *packet,
- }
-
- static int
-+netdev_nsh_pop_header(struct dp_packet *packet)
-+{
-+
-+ struct pkt_metadata *md = &packet->md;
-+ struct flow_tnl *tnl = &md->tunnel;
-+ struct eth_header *eth;
-+ struct nshhdr *nsh;
-+
-+ pkt_metadata_init_tnl(md);
-+ if (ETH_NSH_HLEN > dp_packet_size(packet)) {
-+ return EINVAL;
-+ }
-+
-+ eth = (struct eth_header *) dp_packet_data(packet);
-+ memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
-+ memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
-+
-+ nsh = (struct nshhdr *) (eth + 1);
-+ tnl->nsp = nsh->b.b2 << 8;
-+ tnl->nsi = nsh->b.svc_idx;
-+ tnl->nshc1 = nsh->c.nshc1;
-+ tnl->nshc2 = nsh->c.nshc2;
-+ tnl->nshc3 = nsh->c.nshc3;
-+ tnl->nshc4 = nsh->c.nshc4;
-+
-+ tnl->flags |= FLOW_TNL_F_NSP;
-+ tnl->flags |= FLOW_TNL_F_NSI;
-+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
-+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
-+ tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED;
-+
-+ dp_packet_reset_packet(packet, ETH_NSH_HLEN);
-+
-+ return 0;
-+}
-+
-+static int
-+netdev_nsh_build_header(const struct netdev *netdev,
-+ struct ovs_action_push_tnl *data,
-+ const struct flow *tnl_flow)
-+{
-+ struct netdev_vport *dev = netdev_vport_cast(netdev);
-+ struct eth_header *eth;
-+ struct nshhdr *nsh;
-+
-+ ovs_mutex_lock(&dev->mutex);
-+
-+ eth = (struct eth_header *) (data->header);
-+ nsh = (struct nshhdr *) (eth + 1);
-+ memset(nsh, 0, sizeof *nsh);
-+ nsh->b.ver = 0x01;
-+ nsh->b.len = 6;
-+ nsh->b.mdtype = NSH_M_TYPE1;
-+ nsh->b.proto = NSH_P_ETHERNET;
-+
-+ nsh->b.b2 = tnl_flow->tunnel.nsp >> 8;
-+ nsh->b.svc_idx = tnl_flow->tunnel.nsi;
-+
-+ nsh->c.nshc1 = tnl_flow->tunnel.nshc1; //uncertain
-+ nsh->c.nshc2 = tnl_flow->tunnel.nshc2;
-+ nsh->c.nshc3 = tnl_flow->tunnel.nshc3;
-+ nsh->c.nshc4 = tnl_flow->tunnel.nshc4;
-+
-+ data->header_len = ETH_NSH_HLEN;
-+ data->tnl_type = OVS_VPORT_TYPE_NSH;
-+
-+ ovs_mutex_unlock(&dev->mutex);
-+
-+ return 0;
-+}
-+
-+static void
-+netdev_nsh_push_header(struct dp_packet *packet,
-+ const struct ovs_action_push_tnl *data)
-+{
-+ int size = data->header_len;
-+ const void *header = data->header;
-+ struct eth_header *eth = NULL;
-+
-+ eth = (struct eth_header *) dp_packet_push_uninit(packet, size);
-+ memcpy(eth, header, size);
-+}
-+
-+static int
- netdev_geneve_pop_header(struct dp_packet *packet)
- {
- struct pkt_metadata *md = &packet->md;
-@@ -2056,6 +2141,10 @@ netdev_vport_tunnel_register(void)
- netdev_vxlan_pop_header_spec),
- TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL,NULL),
- TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL,NULL),
-+ TUNNEL_CLASS("eth_nsh", "nsh_sys", netdev_nsh_build_header,
-+ netdev_nsh_push_header,
-+ netdev_nsh_pop_header,
-+ NULL),
- };
- static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
-
-diff --git a/lib/odp-util.c b/lib/odp-util.c
-index 6da2d5b..c2af063 100644
---- a/lib/odp-util.c
-+++ b/lib/odp-util.c
-@@ -447,68 +447,69 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
-
- eth = (const struct eth_header *)data->header;
-
-- l3 = eth + 1;
-- ip = (const struct ip_header *)l3;
--
-- /* Ethernet */
-- ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
-- data->header_len, data->tnl_type);
-- ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
-- ds_put_format(ds, ",src=");
-- ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
-- ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));
--
-- /* IPv4 */
-- ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
-- ",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
-- IP_ARGS(get_16aligned_be32(&ip->ip_src)),
-- IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
-- ip->ip_proto, ip->ip_tos,
-- ip->ip_ttl,
-- ip->ip_frag_off);
--
-- if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
-- const struct vxlanhdr *vxh;
-- const struct udp_header *udp;
-- const struct vxgpehdr *vxg;
--
-- /* UDP */
-- udp = (const struct udp_header *) (ip + 1);
-- ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16",csum=0x%"PRIx16"),",
-- ntohs(udp->udp_src), ntohs(udp->udp_dst),
-- ntohs(udp->udp_csum));
--
-- /* VxLan & VxLan GPE(UDP port: 4790) */
-- if (ntohs(udp->udp_dst) == 4790) {
-- vxg = (const struct vxgpehdr *) (udp + 1);
--
-- ds_put_format(ds, "vxlangpe(vni=0x%"PRIx32",",
-- ntohl(get_16aligned_be32(&vxg->vx_vni)));
-- ds_put_format(ds, "proto=%"PRIu8"),", vxg->proto);
-- if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
-- const struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
--
-- /* NSH */
-- ds_put_format(ds, "nsh(mdtype=%"PRIu8",proto=%"PRIu8",",
-- nsh->b.mdtype, nsh->b.proto);
-- ds_put_format(ds, "nsp=%"PRIx32",nsi=%"PRIu8",",
-- nsh->b.b2 & 0x00FFFFFF, nsh->b.svc_idx);
-- ds_put_format(ds, "nshc1=%"PRIx32",nshc2=%"PRIx32",",
-- ntohl(nsh->c.nshc1), ntohl(nsh->c.nshc2));
-- ds_put_format(ds, "nshc3=%"PRIx32",nshc4=%"PRIx32",",
-- ntohl(nsh->c.nshc3), ntohl(nsh->c.nshc4));
-- ds_put_format(ds, ")");
-+ if (data->tnl_type != OVS_VPORT_TYPE_NSH) {
-+ l3 = eth + 1;
-+ ip = (const struct ip_header *)l3;
-+
-+ /* Ethernet */
-+ ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
-+ data->header_len, data->tnl_type);
-+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
-+ ds_put_format(ds, ",src=");
-+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
-+ ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));
-+
-+ /* IPv4 */
-+ ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
-+ ",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
-+ IP_ARGS(get_16aligned_be32(&ip->ip_src)),
-+ IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
-+ ip->ip_proto, ip->ip_tos,
-+ ip->ip_ttl,
-+ ip->ip_frag_off);
-+
-+ if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
-+ const struct vxlanhdr *vxh;
-+ const struct udp_header *udp;
-+ const struct vxgpehdr *vxg;
-+
-+ /* UDP */
-+ udp = (const struct udp_header *) (ip + 1);
-+ ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16",csum=0x%"PRIx16"),",
-+ ntohs(udp->udp_src), ntohs(udp->udp_dst),
-+ ntohs(udp->udp_csum));
-+
-+ /* VxLan & VxLan GPE(UDP port: 4790) */
-+ if (ntohs(udp->udp_dst) == 4790) {
-+ vxg = (const struct vxgpehdr *) (udp + 1);
-+
-+ ds_put_format(ds, "vxlangpe(vni=0x%"PRIx32",",
-+ ntohl(get_16aligned_be32(&vxg->vx_vni)));
-+ ds_put_format(ds, "proto=%"PRIu8"),", vxg->proto);
-+ if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
-+ const struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
-+
-+ /* NSH */
-+ ds_put_format(ds, "nsh(mdtype=%"PRIu8",proto=%"PRIu8",",
-+ nsh->b.mdtype, nsh->b.proto);
-+ ds_put_format(ds, "nsp=%"PRIx32",nsi=%"PRIu8",",
-+ nsh->b.b2 & 0x00FFFFFF, nsh->b.svc_idx);
-+ ds_put_format(ds, "nshc1=%"PRIx32",nshc2=%"PRIx32",",
-+ ntohl(nsh->c.nshc1), ntohl(nsh->c.nshc2));
-+ ds_put_format(ds, "nshc3=%"PRIx32",nshc4=%"PRIx32",",
-+ ntohl(nsh->c.nshc3), ntohl(nsh->c.nshc4));
-+ ds_put_format(ds, ")");
-+ }
-+ } else {
-+ vxh = (const struct vxlanhdr *) (udp + 1);
-+ ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
-+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
-+ ntohl(get_16aligned_be32(&vxh->vx_vni))>>8);
- }
-- } else {
-- vxh = (const struct vxlanhdr *) (udp + 1);
-- ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
-- ntohl(get_16aligned_be32(&vxh->vx_flags)),
-- ntohl(get_16aligned_be32(&vxh->vx_vni))>>8);
-- }
-- } else if (data->tnl_type == OVS_VPORT_TYPE_GENEVE) {
-- const struct genevehdr *gnh;
-+ } else if (data->tnl_type == OVS_VPORT_TYPE_GENEVE) {
-+ const struct genevehdr *gnh;
-
-- gnh = format_udp_tnl_push_header(ds, ip);
-+ gnh = format_udp_tnl_push_header(ds, ip);
-
- ds_put_format(ds, "geneve(%s%svni=0x%"PRIx32,
- gnh->oam ? "oam," : "",
-@@ -523,32 +524,55 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
- }
- ds_put_char(ds, ')');
-
-- } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
-- const struct gre_base_hdr *greh;
-- ovs_16aligned_be32 *options;
-- void *l4;
-+ } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
-+ const struct gre_base_hdr *greh;
-+ ovs_16aligned_be32 *options;
-+ void *l4;
-
-- l4 = ((uint8_t *)l3 + sizeof(struct ip_header));
-- greh = (const struct gre_base_hdr *) l4;
-+ l4 = ((uint8_t *)l3 + sizeof(struct ip_header));
-+ greh = (const struct gre_base_hdr *) l4;
-
-- ds_put_format(ds, "gre((flags=0x%"PRIx16",proto=0x%"PRIx16")",
-- ntohs(greh->flags), ntohs(greh->protocol));
-- options = (ovs_16aligned_be32 *)(greh + 1);
-- if (greh->flags & htons(GRE_CSUM)) {
-- ds_put_format(ds, ",csum=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
-- options++;
-- }
-- if (greh->flags & htons(GRE_KEY)) {
-- ds_put_format(ds, ",key=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
-- options++;
-- }
-- if (greh->flags & htons(GRE_SEQ)) {
-- ds_put_format(ds, ",seq=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
-- options++;
-+ ds_put_format(ds, "gre((flags=0x%"PRIx16",proto=0x%"PRIx16")",
-+ ntohs(greh->flags), ntohs(greh->protocol));
-+ options = (ovs_16aligned_be32 *)(greh + 1);
-+ if (greh->flags & htons(GRE_CSUM)) {
-+ ds_put_format(ds, ",csum=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
-+ options++;
-+ }
-+ if (greh->flags & htons(GRE_KEY)) {
-+ ds_put_format(ds, ",key=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
-+ options++;
-+ }
-+ if (greh->flags & htons(GRE_SEQ)) {
-+ ds_put_format(ds, ",seq=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
-+ options++;
-+ }
-+ ds_put_format(ds, ")");
- }
- ds_put_format(ds, ")");
-+ } else {
-+ const struct nshhdr *nsh = (const struct nshhdr *) (eth + 1);
-+
-+ /* Ethernet */
-+ ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
-+ data->header_len, data->tnl_type);
-+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
-+ ds_put_format(ds, ",src=");
-+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
-+ ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));
-+
-+ /* NSH */
-+ ds_put_format(ds, "nsh(mdtype=%"PRIu8",proto=%"PRIu8",",
-+ nsh->b.mdtype, nsh->b.proto);
-+ ds_put_format(ds, "nsp=%"PRIx32",nsi=%"PRIu8",",
-+ ntohl(nsh->b.b2 & 0x00FFFFFF), nsh->b.svc_idx);
-+ ds_put_format(ds, "nshc1=%"PRIx32",nshc2=%"PRIx32",",
-+ ntohl(nsh->c.nshc1), ntohl(nsh->c.nshc2));
-+ ds_put_format(ds, "nshc3=%"PRIx32",nshc4=%"PRIx32",",
-+ ntohl(nsh->c.nshc3), ntohl(nsh->c.nshc4));
-+ ds_put_format(ds, ")");
-+ ds_put_format(ds, ")");
- }
-- ds_put_format(ds, ")");
- }
-
- static void
-@@ -877,9 +901,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
- return -EINVAL;
- }
- eth = (struct eth_header *) data->header;
-- l3 = (data->header + sizeof *eth);
-- l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
-- ip = (struct ip_header *) l3;
-+
- if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
- "eth(dst="ETH_ADDR_SCAN_FMT",",
- &data->header_len,
-@@ -897,165 +919,189 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
- }
- eth->eth_type = htons(dl_type);
-
-- /* IPv4 */
-- if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
-- ",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
-- IP_SCAN_ARGS(&sip),
-- IP_SCAN_ARGS(&dip),
-- &ip->ip_proto, &ip->ip_tos,
-- &ip->ip_ttl, &ip->ip_frag_off)) {
-- return -EINVAL;
-- }
-- put_16aligned_be32(&ip->ip_src, sip);
-- put_16aligned_be32(&ip->ip_dst, dip);
-+ /* NSH */
-+ nsh = (struct nshhdr *) (data->header + sizeof *eth);
-+ if (ovs_scan_len(s, &n, "nsh(mdtype=%"SCNi8",proto=%"SCNi8",nsp=0x%"SCNx32
-+ ",nsi=%"SCNi8",nshc1=0x%"SCNx32",nshc2=0x%"SCNx32
-+ ",nshc3=0x%"SCNx32",nshc4=0x%"SCNx32"))",
-+ &nsh->b.mdtype, &nsh->b.proto,
-+ &nsp, &nsi,
-+ &nshc1, &nshc2,
-+ &nshc3, &nshc4)) {
-+ nsh->b.ver = 0x01;
-+ nsh->b.len = 6;
-+ nsh->b.b2 = nsp;
-+ nsh->b.svc_idx = nsi;
-+ nsh->c.nshc1=nshc1;
-+ nsh->c.nshc2=nshc2;
-+ nsh->c.nshc3=nshc3;
-+ nsh->c.nshc4=nshc4;
-+ tnl_type = OVS_VPORT_TYPE_NSH;
-+ header_len = sizeof *eth + sizeof *nsh;
-+ } else {
-+ l3 = (data->header + sizeof *eth);
-+ l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
-+ ip = (struct ip_header *) l3;
-+
-+ /* IPv4 */
-+ if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
-+ ",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
-+ IP_SCAN_ARGS(&sip),
-+ IP_SCAN_ARGS(&dip),
-+ &ip->ip_proto, &ip->ip_tos,
-+ &ip->ip_ttl, &ip->ip_frag_off)) {
-+ return -EINVAL;
-+ }
-+ put_16aligned_be32(&ip->ip_src, sip);
-+ put_16aligned_be32(&ip->ip_dst, dip);
-
-- /* Tunnel header */
-- udp = (struct udp_header *) l4;
-- greh = (struct gre_base_hdr *) l4;
-- if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
-- &udp_src, &udp_dst, &csum)) {
-- struct vxlanhdr *vxh;
-- struct vxgpehdr *vxg;
-- uint32_t vx_flags, vx_vni;
-- uint32_t geneve_vni;
-+ /* Tunnel header */
-+ udp = (struct udp_header *) l4;
-+ greh = (struct gre_base_hdr *) l4;
-+ if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
-+ &udp_src, &udp_dst, &csum)) {
-+ struct vxlanhdr *vxh;
-+ struct vxgpehdr *vxg;
-+ uint32_t vx_flags, vx_vni;
-+ uint32_t geneve_vni;
-
-- udp->udp_src = htons(udp_src);
-- udp->udp_dst = htons(udp_dst);
-- udp->udp_len = 0;
-- udp->udp_csum = htons(csum);
-+ udp->udp_src = htons(udp_src);
-+ udp->udp_dst = htons(udp_dst);
-+ udp->udp_len = 0;
-+ udp->udp_csum = htons(csum);
-
-- vxh = (struct vxlanhdr *) (udp + 1);
-- vxg = (struct vxgpehdr *) (udp + 1);
-+ vxh = (struct vxlanhdr *) (udp + 1);
-+ vxg = (struct vxgpehdr *) (udp + 1);
-
-- if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
-- &vx_flags, &vx_vni)) {
-- tnl_type = OVS_VPORT_TYPE_VXLAN;
-+ if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
-+ &vx_flags, &vx_vni)) {
-+ tnl_type = OVS_VPORT_TYPE_VXLAN;
-
-- put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
-- put_16aligned_be32(&vxh->vx_vni, htonl(vx_vni<<8));
-+ put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
-+ put_16aligned_be32(&vxh->vx_vni, htonl(vx_vni<<8));
-
-- header_len = sizeof *eth + sizeof *ip +
-- sizeof *udp + sizeof *vxh;
--
-- } else if (ovs_scan_len(s, &n, "vxlangpe(vni=0x%"SCNx32",proto="SCNi8"),",
-- &vx_vni, &vxg->proto)) {
-- struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
--
-- tnl_type = OVS_VPORT_TYPE_VXLAN;
-- vxg->i = 0x01;
-- vxg->p = 0x01;
-- vxg->ver = 0x01;
-- put_16aligned_be32(&vxg->vx_vni, htonl(vx_vni));
--
-- if (ovs_scan_len(s, &n, "nsh(mdtype=%"SCNi8",proto=%"SCNi8",nsp=0x%"SCNx32
-- ",nsi=%"SCNi8",nshc1=0x%"SCNx32",nshc2=0x%"SCNx32
-- ",nshc3=0x%"SCNx32",nshc4=0x%"SCNx32"))",
-- &nsh->b.mdtype, &nsh->b.proto,
-- &nsp, &nsi,
-- &nshc1, &nshc2,
-- &nshc3, &nshc4)) {
-- nsh->b.ver = 0x01;
-- nsh->b.len = 6;
-- nsh->b.b2 = nsp;
-- nsh->b.svc_idx = nsi;
-- nsh->c.nshc1=nshc1;
-- nsh->c.nshc2=nshc2;
-- nsh->c.nshc3=nshc3;
-- nsh->c.nshc4=nshc4;
- header_len = sizeof *eth + sizeof *ip +
-- sizeof *udp + sizeof *vxh + sizeof *nsh;
-- } else {
-+ sizeof *udp + sizeof *vxh;
-+
-+ } else if (ovs_scan_len(s, &n, "vxlangpe(vni=0x%"SCNx32",proto="SCNi8"),",
-+ &vx_vni, &vxg->proto)) {
-+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
-+
-+ tnl_type = OVS_VPORT_TYPE_VXLAN;
-+ vxg->i = 0x01;
-+ vxg->p = 0x01;
-+ vxg->ver = 0x01;
-+ put_16aligned_be32(&vxg->vx_vni, htonl(vx_vni));
-+
-+ if (ovs_scan_len(s, &n, "nsh(mdtype=%"SCNi8",proto=%"SCNi8",nsp=0x%"SCNx32
-+ ",nsi=%"SCNi8",nshc1=0x%"SCNx32",nshc2=0x%"SCNx32
-+ ",nshc3=0x%"SCNx32",nshc4=0x%"SCNx32"))",
-+ &nsh->b.mdtype, &nsh->b.proto,
-+ &nsp, &nsi,
-+ &nshc1, &nshc2,
-+ &nshc3, &nshc4)) {
-+ nsh->b.ver = 0x01;
-+ nsh->b.len = 6;
-+ nsh->b.b2 = nsp;
-+ nsh->b.svc_idx = nsi;
-+ nsh->c.nshc1=nshc1;
-+ nsh->c.nshc2=nshc2;
-+ nsh->c.nshc3=nshc3;
-+ nsh->c.nshc4=nshc4;
-+ header_len = sizeof *eth + sizeof *ip +
-+ sizeof *udp + sizeof *vxh + sizeof *nsh;
-+ } else {
-+ return -EINVAL;
-+ }
-+ } else if (ovs_scan_len(s, &n, "geneve(")) {
-+ struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
-+
-+ memset(gnh, 0, sizeof *gnh);
-+ header_len = sizeof *eth + sizeof *ip +
-+ sizeof *udp + sizeof *gnh;
-+
-+ if (ovs_scan_len(s, &n, "oam,")) {
-+ gnh->oam = 1;
-+ }
-+ if (ovs_scan_len(s, &n, "crit,")) {
-+ gnh->critical = 1;
-+ }
-+ if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &geneve_vni)) {
- return -EINVAL;
- }
-- } else if (ovs_scan_len(s, &n, "geneve(")) {
-- struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
-+ if (ovs_scan_len(s, &n, ",options(")) {
-+ struct geneve_scan options;
-+ int len;
-+
-+ memset(&options, 0, sizeof options);
-+ len = scan_geneve(s + n, &options, NULL);
-+ if (!len) {
-+ return -EINVAL;
-+ }
-
-- memset(gnh, 0, sizeof *gnh);
-- header_len = sizeof *eth + sizeof *ip +
-- sizeof *udp + sizeof *gnh;
-+ memcpy(gnh->options, options.d, options.len);
-+ gnh->opt_len = options.len / 4;
-+ header_len += options.len;
-
-- if (ovs_scan_len(s, &n, "oam,")) {
-- gnh->oam = 1;
-- }
-- if (ovs_scan_len(s, &n, "crit,")) {
-- gnh->critical = 1;
-- }
-- if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &geneve_vni)) {
-- return -EINVAL;
-- }
-- if (ovs_scan_len(s, &n, ",options(")) {
-- struct geneve_scan options;
-- int len;
--
-- memset(&options, 0, sizeof options);
-- len = scan_geneve(s + n, &options, NULL);
-- if (!len) {
-+ n += len;
-+ }
-+ if (!ovs_scan_len(s, &n, "))")) {
- return -EINVAL;
- }
-
-- memcpy(gnh->options, options.d, options.len);
-- gnh->opt_len = options.len / 4;
-- header_len += options.len;
--
-- n += len;
-- }
-- if (!ovs_scan_len(s, &n, "))")) {
-+ gnh->proto_type = htons(ETH_TYPE_TEB);
-+ put_16aligned_be32(&gnh->vni, htonl(geneve_vni << 8));
-+ tnl_type = OVS_VPORT_TYPE_GENEVE;
-+ } else {
- return -EINVAL;
- }
-+ } else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
-+ &gre_flags, &gre_proto)){
-
-- gnh->proto_type = htons(ETH_TYPE_TEB);
-- put_16aligned_be32(&gnh->vni, htonl(geneve_vni << 8));
-- tnl_type = OVS_VPORT_TYPE_GENEVE;
-- } else {
-- return -EINVAL;
-- }
--} else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
-- &gre_flags, &gre_proto)){
-+ tnl_type = OVS_VPORT_TYPE_GRE;
-+ greh->flags = htons(gre_flags);
-+ greh->protocol = htons(gre_proto);
-+ ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
-
-- tnl_type = OVS_VPORT_TYPE_GRE;
-- greh->flags = htons(gre_flags);
-- greh->protocol = htons(gre_proto);
-- ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
-+ if (greh->flags & htons(GRE_CSUM)) {
-+ if (!ovs_scan_len(s, &n, ",csum=0x%"SCNx16, &csum)) {
-+ return -EINVAL;
-+ }
-
-- if (greh->flags & htons(GRE_CSUM)) {
-- if (!ovs_scan_len(s, &n, ",csum=0x%"SCNx16, &csum)) {
-- return -EINVAL;
-+ memset(options, 0, sizeof *options);
-+ *((ovs_be16 *)options) = htons(csum);
-+ options++;
- }
-+ if (greh->flags & htons(GRE_KEY)) {
-+ uint32_t key;
-
-- memset(options, 0, sizeof *options);
-- *((ovs_be16 *)options) = htons(csum);
-- options++;
-- }
-- if (greh->flags & htons(GRE_KEY)) {
-- uint32_t key;
-+ if (!ovs_scan_len(s, &n, ",key=0x%"SCNx32, &key)) {
-+ return -EINVAL;
-+ }
-
-- if (!ovs_scan_len(s, &n, ",key=0x%"SCNx32, &key)) {
-- return -EINVAL;
-+ put_16aligned_be32(options, htonl(key));
-+ options++;
- }
-+ if (greh->flags & htons(GRE_SEQ)) {
-+ uint32_t seq;
-
-- put_16aligned_be32(options, htonl(key));
-- options++;
-- }
-- if (greh->flags & htons(GRE_SEQ)) {
-- uint32_t seq;
-+ if (!ovs_scan_len(s, &n, ",seq=0x%"SCNx32, &seq)) {
-+ return -EINVAL;
-+ }
-+ put_16aligned_be32(options, htonl(seq));
-+ options++;
-+ }
-
-- if (!ovs_scan_len(s, &n, ",seq=0x%"SCNx32, &seq)) {
-+ if (!ovs_scan_len(s, &n, "))")) {
- return -EINVAL;
- }
-- put_16aligned_be32(options, htonl(seq));
-- options++;
-- }
-
-- if (!ovs_scan_len(s, &n, "))")) {
-+ header_len = sizeof *eth + sizeof *ip +
-+ ((uint8_t *) options - (uint8_t *) greh);
-+ } else {
- return -EINVAL;
- }
--
-- header_len = sizeof *eth + sizeof *ip +
-- ((uint8_t *) options - (uint8_t *) greh);
-- } else {
-- return -EINVAL;
-- }
--
-+ }
- /* check tunnel meta data. */
- if (data->tnl_type != tnl_type) {
- return -EINVAL;
-@@ -1072,6 +1118,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
- return n;
- }
-
-+
- static int
- ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
- {
-@@ -1492,6 +1539,12 @@ odp_tun_key_from_attr__(const struct nlattr *attr,
- case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
- tun->ip_dst = nl_attr_get_be32(a);
- break;
-+ case OVS_TUNNEL_KEY_ATTR_ETH_DST:
-+ memcpy(tun->eth_dst.ea, nl_attr_get(a), ETH_ADDR_LEN);
-+ break;
-+ case OVS_TUNNEL_KEY_ATTR_ETH_SRC:
-+ memcpy(tun->eth_src.ea, nl_attr_get(a), ETH_ADDR_LEN);
-+ break;
- case OVS_TUNNEL_KEY_ATTR_TOS:
- tun->ip_tos = nl_attr_get_u8(a);
- break;
-@@ -1609,6 +1662,18 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key,
- if (tun_key->ip_dst) {
- nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ip_dst);
- }
-+ if (memcmp(tun_key->eth_dst.ea, ð_addr_zero, ETH_ADDR_LEN)) {
-+ struct eth_addr *eth_dst;
-+ eth_dst = nl_msg_put_unspec_uninit(a, OVS_TUNNEL_KEY_ATTR_ETH_DST,
-+ sizeof *eth_dst);
-+ memcpy(eth_dst->ea, tun_key->eth_dst.ea, ETH_ADDR_LEN) ;
-+ }
-+ if (memcmp(tun_key->eth_src.ea, ð_addr_zero, ETH_ADDR_LEN)) {
-+ struct eth_addr *eth_src;
-+ eth_src = nl_msg_put_unspec_uninit(a, OVS_TUNNEL_KEY_ATTR_ETH_SRC,
-+ sizeof *eth_src);
-+ memcpy(eth_src->ea, tun_key->eth_src.ea, ETH_ADDR_LEN) ;
-+ }
- if (tun_key->ip_tos) {
- nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ip_tos);
- }
-@@ -2191,6 +2256,14 @@ format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
- format_ipv4(ds, "dst", nl_attr_get_be32(a),
- ma ? nl_attr_get(ma) : NULL, verbose);
- break;
-+ case OVS_TUNNEL_KEY_ATTR_ETH_SRC:
-+ format_eth(ds, "eth_src", * (struct eth_addr *) nl_attr_get(a),
-+ ma ? nl_attr_get(ma) : NULL, verbose);
-+ break;
-+ case OVS_TUNNEL_KEY_ATTR_ETH_DST:
-+ format_eth(ds, "eth_dst", * (struct eth_addr *) nl_attr_get(a),
-+ ma ? nl_attr_get(ma) : NULL, verbose);
-+ break;
- case OVS_TUNNEL_KEY_ATTR_TOS:
- format_u8x(ds, "tos", nl_attr_get_u8(a),
- ma ? nl_attr_get(ma) : NULL, verbose);
-@@ -3693,7 +3766,9 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
-
- nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
-
-- if (flow->tunnel.ip_dst || export_mask) {
-+ if (flow->tunnel.ip_dst ||
-+ flow->tunnel.nsh_flags & NSH_TNL_F_ETHERNET_PARSED ||
-+ export_mask) {
- tun_key_to_attr(buf, &data->tunnel, &parms->flow->tunnel,
- parms->key_buf);
- }
-diff --git a/lib/packets.c b/lib/packets.c
-index d69d006..7dab4b5 100644
---- a/lib/packets.c
-+++ b/lib/packets.c
-@@ -933,17 +933,19 @@ packet_set_nsh(struct dp_packet *packet, struct flow_tnl *tun_key)
- struct nshhdr *nsh;
-
- eth = (struct eth_header *) dp_packet_data(packet);
-- struct ip_header *ip = (struct ip_header *) (eth + 1);
-- struct udp_header *udp = (struct udp_header *) (ip + 1);
-- struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
--
-- nsh = (struct nshhdr *) (vxg + 1);
-- nsh->b.b2 = tun_key->nsp >> 8;
-- nsh->b.svc_idx = tun_key->nsi;
-- nsh->c.nshc1 = tun_key->nshc1;
-- nsh->c.nshc2 = tun_key->nshc2;
-- nsh->c.nshc3 = tun_key->nshc3;
-- nsh->c.nshc4 = tun_key->nshc4;
-+
-+ if (tun_key->nsh_flags & NSH_TNL_F_VXLAN_PRST) {
-+ struct ip_header *ip = (struct ip_header *) (eth + 1);
-+ struct udp_header *udp = (struct udp_header *) (ip + 1);
-+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
-+ nsh = (struct nshhdr *) (vxg + 1);
-+ nsh->b.b2 = tun_key->nsp >> 8;
-+ nsh->b.svc_idx = tun_key->nsi;
-+ nsh->c.nshc1 = tun_key->nshc1;
-+ nsh->c.nshc2 = tun_key->nshc2;
-+ nsh->c.nshc3 = tun_key->nshc3;
-+ nsh->c.nshc4 = tun_key->nshc4;
-+ }
- }
-
- const char *
-diff --git a/lib/packets.h b/lib/packets.h
-index c586390..3f58970 100644
---- a/lib/packets.h
-+++ b/lib/packets.h
-@@ -48,15 +48,16 @@ struct flow_tnl {
- ovs_be16 gbp_id;
- uint8_t gbp_flags;
- uint8_t nsh_flags;
-- uint8_t nsi;
-- ovs_be32 nsp;
- ovs_be32 nshc1;
- ovs_be32 nshc2;
- ovs_be32 nshc3;
- ovs_be32 nshc4;
-- struct eth_addr eth_dst;
-+ ovs_be32 nsp;
-+ uint8_t nsi;
- uint8_t tun_len;
-- uint8_t pad1[4]; /* Pad to 64 bits. */
-+ struct eth_addr eth_dst;
-+ struct eth_addr eth_src;
-+ uint8_t pad1[2]; /* Pad to 64 bits. */
- struct tun_metadata metadata;
- };
-
-@@ -87,9 +88,10 @@ struct flow_tnl {
- #define FLOW_TNL_F_NSH_C3 (1 << 9)
- #define FLOW_TNL_F_NSH_C4 (1 << 10)
-
--#define NSH_TNL_F_ETHERNET (1 << 0)
--#define NSH_TNL_F_VXLAN (1 << 1)
-+#define NSH_TNL_F_ETHERNET_PRST (1 << 0)
-+#define NSH_TNL_F_VXLAN_PRST (1 << 1)
- #define NSH_TNL_F_NODECAP (1 << 2)
-+#define NSH_TNL_F_ETHERNET_PARSED (1 << 3)
-
- /* Returns an offset to 'src' covering all the meaningful fields in 'src'. */
- static inline size_t
-@@ -160,6 +162,7 @@ pkt_metadata_init(struct pkt_metadata *md, odp_port_t port)
- * looked at. */
- memset(md, 0, offsetof(struct pkt_metadata, tunnel));
- md->tunnel.ip_dst = 0;
-+ md->tunnel.nsh_flags = 0;
-
- md->in_port.odp_port = port;
- }
-diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
-index 60dc06f..45ad58b 100644
---- a/lib/tnl-ports.c
-+++ b/lib/tnl-ports.c
-@@ -162,6 +162,38 @@ out:
- ovs_mutex_unlock(&mutex);
- }
-
-+void
-+tnl_l2_port_map_insert(odp_port_t port, ovs_be16 dl_type, const char dev_name[])
-+{
-+ const struct cls_rule *cr;
-+ struct tnl_port_in *p;
-+ struct match match;
-+
-+ memset(&match, 0, sizeof match);
-+ match.flow.dl_type = htons(dl_type);
-+
-+ ovs_mutex_lock(&mutex);
-+ do {
-+ cr = classifier_lookup(&cls,CLS_MAX_VERSION, &match.flow, NULL);
-+ p = tnl_port_cast(cr);
-+ /* Try again if the rule was released before we get the reference. */
-+ } while (p && !ovs_refcount_try_ref_rcu(&p->ref_cnt));
-+
-+ if (!p) {
-+ p = xzalloc(sizeof *p);
-+ p->portno = port;
-+
-+ match.wc.masks.dl_type = OVS_BE16_MAX;
-+
-+ cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */
-+ ovs_refcount_init(&p->ref_cnt);
-+ strncpy(p->dev_name, dev_name, IFNAMSIZ);
-+
-+ classifier_insert(&cls, &p->cr,CLS_MIN_VERSION, NULL, 0);
-+ }
-+ ovs_mutex_unlock(&mutex);
-+}
-+
- static void
- tnl_port_unref(const struct cls_rule *cr)
- {
-diff --git a/lib/tnl-ports.h b/lib/tnl-ports.h
-index 4195e6a..81c9a12 100644
---- a/lib/tnl-ports.h
-+++ b/lib/tnl-ports.h
-@@ -28,6 +28,8 @@ odp_port_t tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc);
-
- void tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port,
- const char dev_name[]);
-+void tnl_l2_port_map_insert(odp_port_t port, ovs_be16 dl_type,
-+ const char dev_name[]);
-
- void tnl_port_map_delete(ovs_be16 udp_port);
- void tnl_port_map_insert_ipdev(const char dev[]);
-diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
-index 9ad8fa2..bf19a57 100644
---- a/ofproto/ofproto-dpif-ipfix.c
-+++ b/ofproto/ofproto-dpif-ipfix.c
-@@ -67,6 +67,7 @@ enum dpif_ipfix_tunnel_type {
- DPIF_IPFIX_TUNNEL_STT = 0x04,
- DPIF_IPFIX_TUNNEL_IPSEC_GRE = 0x05,
- DPIF_IPFIX_TUNNEL_GENEVE = 0x07,
-+ DPIF_IPFIX_TUNNEL_NSH = 0x08,
- NUM_DPIF_IPFIX_TUNNEL
- };
-
-@@ -595,6 +596,9 @@ dpif_ipfix_add_tunnel_port(struct dpif_ipfix *di, struct ofport *ofport,
- } else if (strcmp(type, "vxlan") == 0) {
- dip->tunnel_type = DPIF_IPFIX_TUNNEL_VXLAN;
- dip->tunnel_key_length = 3;
-+ } else if (strcmp(type, "eth_nsh") == 0) {
-+ dip->tunnel_type = DPIF_IPFIX_TUNNEL_NSH;
-+ dip->tunnel_key_length = 3;
- } else if (strcmp(type, "lisp") == 0) {
- dip->tunnel_type = DPIF_IPFIX_TUNNEL_LISP;
- dip->tunnel_key_length = 3;
-diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
-index bff0a83..90b5a95 100644
---- a/ofproto/ofproto-dpif-xlate.c
-+++ b/ofproto/ofproto-dpif-xlate.c
-@@ -2829,7 +2829,6 @@ build_tunnel_pop(const struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct
- struct ovs_action_pop_tnl tnl_pop_data;
- struct xport *out_dev = NULL;
- struct eth_addr smac;
--
- int err;
-
- err = tnl_outdev_lookup_mac(&cfg->eth_dst, &out_dev);
-@@ -2880,6 +2879,40 @@ build_tunnel_pop(const struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct
- return 0;
- }
-
-+static int
-+build_nsh_tunnel_send(const struct xlate_ctx *ctx, const struct xport *xport,
-+ const struct flow *flow, odp_port_t tunnel_odp_port)
-+{
-+ struct ovs_action_push_tnl tnl_push_data;
-+ struct xport *out_dev = NULL;
-+ struct eth_addr smac;
-+ int err;
-+
-+ err = tnl_outdev_lookup_mac(&flow->tunnel.eth_dst, &out_dev);
-+ if (err) {
-+ VLOG_WARN("tnl_outdev_lookup_mac failed...");
-+ return err;
-+ }
-+
-+ /* Use mac addr of bridge port of the peer. */
-+ err = netdev_get_etheraddr(out_dev->netdev, &smac);
-+ if (err) {
-+ VLOG_WARN("netdev_get_etheraddr failed...");
-+ return err;
-+ }
-+
-+ err = tnl_port_build_nsh_header(xport->ofport, flow,
-+ &flow->tunnel.eth_dst, &smac, &tnl_push_data);
-+ if (err) {
-+ VLOG_WARN("tnl_port_build_nsh_header failed...");
-+ return err;
-+ }
-+ tnl_push_data.tnl_port = odp_to_u32(tunnel_odp_port);
-+ tnl_push_data.out_port = odp_to_u32(out_dev->odp_port);
-+ odp_put_tnl_push_action(ctx->odp_actions, &tnl_push_data);
-+ return 0;
-+}
-+
- static void
- xlate_commit_actions(struct xlate_ctx *ctx)
- {
-@@ -2942,6 +2975,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
- }
- }
-
-+ const char * xport_type = netdev_get_type(xport->netdev);
-+
- if (xport->peer) {
- const struct xport *peer = xport->peer;
- struct flow old_flow = ctx->xin->flow;
-@@ -3063,8 +3098,14 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
- xlate_report(ctx, "Tunneling decided against output");
- goto out; /* restore flow_nw_tos */
- }
-- if (flow->tunnel.ip_dst == ctx->orig_tunnel.ip_dst) {
-- xlate_report(ctx, "Not tunneling to our own address");
-+ if (flow->tunnel.ip_dst == ctx->orig_tunnel.ip_dst &&
-+ strcmp(xport_type, "eth_nsh")) {
-+ xlate_report(ctx, "Not tunneling to our own ip address");
-+ goto out; /* restore flow_nw_tos */
-+ }
-+ if (!strcmp(xport_type, "eth_nsh") && !memcmp(flow->tunnel.eth_dst.ea,
-+ ctx->orig_tunnel.eth_dst.ea, ETH_ADDR_LEN)) {
-+ xlate_report(ctx, "Not tunneling to our own mac address");
- goto out; /* restore flow_nw_tos */
- }
- if (ctx->xin->resubmit_stats) {
-@@ -3122,9 +3163,13 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
- nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_RECIRC,
- xr->recirc_id);
- } else {
--
- if (tnl_push_pop_send) {
-- build_tunnel_send(ctx, xport, flow, odp_port);
-+ if (!strcmp(xport_type, "eth_nsh")) {
-+ build_nsh_tunnel_send(ctx, xport, flow, odp_port);
-+ }
-+ else {
-+ build_tunnel_send(ctx, xport, flow, odp_port);
-+ }
- flow->tunnel = flow_tnl; /* Restore tunnel metadata */
- } else {
- odp_port_t odp_tnl_port = ODPP_NONE;
-diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
-index b0e46e6..cc0c91a 100644
---- a/ofproto/tunnel.c
-+++ b/ofproto/tunnel.c
-@@ -59,6 +59,7 @@ struct tnl_match {
- odp_port_t odp_port;
- uint32_t pkt_mark;
- uint8_t in_nsi;
-+ struct eth_addr eth_dst;
- bool in_key_flow;
- bool in_nsp_flow;
- bool in_nshc1_flow;
-@@ -197,6 +198,7 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
- struct tnl_port *tnl_port;
- struct hmap **map;
-
-+ const char * xport_type = netdev_get_type(netdev);
- cfg = netdev_get_tunnel_config(netdev);
- ovs_assert(cfg);
-
-@@ -205,15 +207,21 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
- tnl_port->netdev = netdev_ref(netdev);
- tnl_port->change_seq = netdev_get_change_seq(tnl_port->netdev);
-
-- tnl_port->match.in_key = cfg->in_key;
-+ if(!cfg->nsh_convert || strcmp(xport_type, "eth_nsh")){
-+ tnl_port->match.in_key = cfg->in_key;
-+ tnl_port->match.ip_src = cfg->ip_src;
-+ tnl_port->match.ip_dst = cfg->ip_dst;
-+ }
-+ if(!cfg->nsh_convert || strcmp(xport_type, "vxlan")){
-+ memcpy(tnl_port->match.eth_dst.ea, cfg->eth_dst.ea, ETH_ADDR_LEN); ///////only add this line
-+ }
-+
- 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;
-@@ -252,7 +260,12 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
- tnl_port_mod_log(tnl_port, "adding");
-
- if (native_tnl) {
-- tnl_port_map_insert(odp_port, cfg->dst_port, name);
-+ if (!strcmp("eth_nsh", netdev_get_type(netdev))) {
-+ tnl_l2_port_map_insert(odp_port, ETH_TYPE_NSH, name);
-+ } else {
-+ tnl_port_map_insert(odp_port, cfg->dst_port, name);
-+ }
-+
- }
- return true;
- }
-@@ -494,6 +507,9 @@ tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow,
- flow->tunnel.ip_tos = cfg->tos;
- }
-
-+ if (!cfg->eth_dst_flow) {
-+ memcpy(flow->tunnel.eth_dst.ea, cfg->eth_dst.ea, ETH_ADDR_LEN);
-+ }
- if (!cfg->out_key_flow) {
- flow->tunnel.tun_id = cfg->out_key;
- }
-@@ -786,6 +802,7 @@ tnl_match_idx_to_m(const struct flow *flow, unsigned int idx,
- 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;
-+ memcpy(m->eth_dst.ea, flow->tunnel.eth_src.ea, ETH_ADDR_LEN);
- m->pkt_mark = flow->pkt_mark;
- m->in_key_flow = in_key_flow;
- m->ip_dst_flow = ip_dst_flow;
-@@ -1071,6 +1088,35 @@ tnl_port_build_header(const struct ofport_dpif *ofport,
- }
-
- int
-+tnl_port_build_nsh_header(const struct ofport_dpif *ofport,
-+ const struct flow *tnl_flow,
-+ const struct eth_addr *dmac,
-+ const struct eth_addr *smac,
-+ struct ovs_action_push_tnl *data)
-+{
-+ struct tnl_port *tnl_port;
-+ struct eth_header *eth;
-+ int res;
-+
-+ fat_rwlock_rdlock(&rwlock);
-+ tnl_port = tnl_find_ofport(ofport);
-+ ovs_assert(tnl_port);
-+
-+ /* Build Ethernet and IP headers. */
-+ memset(data->header, 0, sizeof data->header);
-+
-+ eth = (struct eth_header *)data->header;
-+ memcpy(eth->eth_dst.ea, dmac->ea, ETH_ADDR_LEN);
-+ memcpy(eth->eth_src.ea, smac->ea, ETH_ADDR_LEN);
-+ eth->eth_type = htons(ETH_TYPE_NSH);
-+
-+ res = netdev_build_header(tnl_port->netdev, data, tnl_flow);
-+ fat_rwlock_unlock(&rwlock);
-+
-+ return res;
-+}
-+
-+int
- tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port,
- const struct flow *tnl_flow OVS_UNUSED,
- const struct eth_addr *dmac,
-diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h
-index 0c51a4e..d771476 100644
---- a/ofproto/tunnel.h
-+++ b/ofproto/tunnel.h
-@@ -51,7 +51,8 @@ tnl_port_cfg(odp_port_t odp_port, struct flow *flow);
- static inline bool
- tnl_port_should_receive(const struct flow *flow)
- {
-- return flow->tunnel.ip_dst != 0;
-+ return (flow->tunnel.ip_dst != 0 ||
-+ memcmp(flow->tunnel.eth_dst.ea, ð_addr_zero, ETH_ADDR_LEN));
- }
-
- int tnl_port_build_header(const struct ofport_dpif *ofport,
-@@ -59,6 +60,11 @@ 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);
-+int tnl_port_build_nsh_header(const struct ofport_dpif *ofport,
-+ const struct flow *tnl_flow,
-+ const struct eth_addr *dmac,
-+ const struct eth_addr *smac,
-+ struct ovs_action_push_tnl *data);
- int tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port,
- const struct flow *tnl_flow OVS_UNUSED,
- const struct eth_addr *dmac,
-diff --git a/tests/tunnel.at b/tests/tunnel.at
-index dc35809..19221fb 100644
---- a/tests/tunnel.at
-+++ b/tests/tunnel.at
-@@ -705,6 +705,66 @@ AT_CHECK([tail -1 stdout], [0],
- OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
- AT_CLEANUP
-
-+AT_SETUP([tunnel ETHERNET NSH - encap - nsh/nsi/nshc user space])
-+OVS_VSWITCHD_START([dnl
-+ add-port br0 p1 -- set interface p1 type=eth_nsh options:remote_mac=00:00:00:11:11:22 options:out_nsp=flow \
-+ options:out_nsi=flow options:in_nshc1=flow options:in_nshc2=flow options:in_nshc3=flow options:in_nshc4=flow ofport_request=1 \
-+ -- add-port br0 p2 -- set Interface p2 type=eth_nsh \
-+ options:remote_mac=00:00:00:11:11:33 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=eth_nsh \
-+ options:remote_mac=00:00:00:11:11:44 options:nsp=222 options:nsi=22 options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=3 \
-+ -- add-port br0 p4 -- set Interface p4 type=eth_nsh \
-+ options:remote_mac=00:00:00:11:11:55 options:nsp=flow options:nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=4])
-+
-+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
-+])
-+AT_CHECK([ovs-vsctl add-port br0 p0 -- set Interface p0 type=dummy ofport_request=5])
-+
-+AT_CHECK([
-+ovs-appctl ovs/route/addmac 00:00:00:11:11:22 br0
-+ovs-appctl ovs/route/addmac 00:00:00:11:11:33 br0
-+ovs-appctl ovs/route/addmac 00:00:00:11:11:44 br0
-+ovs-appctl ovs/route/addmac 00:00:00:11:11:55 br0
-+],[0],[stdout])
-+
-+AT_DATA([flows.txt], [dnl
-+in_port=5 actions=resubmit:1,resubmit:2,resubmit:3,resubmit:4
-+in_port=1 actions=output:1
-+in_port=2 actions=output:2
-+in_port=3 actions=set_nshc1:22,set_nshc2:23,set_nshc3:24,set_nshc4:25,output:3
-+in_port=4 actions=set_nsp:333,set_nsi:33,set_nshc1:33,set_nshc2:34,set_nshc3:35,set_nshc4:36,output:4
-+])
-+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-+
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),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: tnl_push(tnl_port(1),header(size=38,type=107,eth(dst=00:00:00:11:11:22,src=aa:55:aa:55:00:00,dl_type=0x894f),nsh(mdtype=1,proto=3,nsp=0,nsi=0,nshc1=0,nshc2=0,nshc3=0,nshc4=0,)),out_port(100)),tnl_push(tnl_port(1),header(size=38,type=107,eth(dst=00:00:00:11:11:33,src=aa:55:aa:55:00:00,dl_type=0x894f),nsh(mdtype=1,proto=3,nsp=6f00,nsi=11,nshc1=b,nshc2=c,nshc3=d,nshc4=e,)),out_port(100)),tnl_push(tnl_port(1),header(size=38,type=107,eth(dst=00:00:00:11:11:44,src=aa:55:aa:55:00:00,dl_type=0x894f),nsh(mdtype=1,proto=3,nsp=de00,nsi=22,nshc1=16,nshc2=17,nshc3=18,nshc4=19,)),out_port(100)),tnl_push(tnl_port(1),header(size=38,type=107,eth(dst=00:00:00:11:11:55,src=aa:55:aa:55:00:00,dl_type=0x894f),nsh(mdtype=1,proto=3,nsp=14d00,nsi=33,nshc1=21,nshc2=22,nshc3=23,nshc4=24,)),out_port(100))
-+])
-+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
-+AT_CLEANUP
-+
-+AT_SETUP([tunnel - ETHERNET NSH decap - user space])
-+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
-+AT_CHECK([ovs-vsctl add-port br0 p1 -- set interface p1 type=eth_nsh options:remote_mac=00:00:00:11:11:22 options:out_nsp=flow \
-+ options:out_nsi=flow options:in_nshc1=flow options:in_nshc2=flow options:in_nshc3=flow options:in_nshc4=flow ofport_request=2], [0])
-+
-+
-+AT_CHECK([ovs-ofctl add-flow br0 "priority=16, in_port=1, action=local"])
-+
-+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
-+ br0 65534/100: (dummy)
-+ p0 1/1: (dummy)
-+ p1 2/2: (eth_nsh: in_nshc1=flow, in_nshc2=flow, in_nshc3=flow, in_nshc4=flow, out_nsi=flow, out_nsp=flow, remote_mac=00:00:00:11:11:22)
-+])
-+
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=00:00:00:11:11:22,dst=50:54:00:00:00:07),eth_type(0x894f)'], [0], [stdout])
-+AT_CHECK([tail -1 stdout], [0],
-+ [Datapath actions: tnl_pop(2)
-+])
-+
-+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/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 \
---
-1.9.3
-
diff --git a/ovs-nsh/patches/060684.patch b/ovs-nsh/patches/060684.patch
deleted file mode 100644
index 329ac5c..0000000
--- a/ovs-nsh/patches/060684.patch
+++ /dev/null
@@ -1,151 +0,0 @@
-decapsulation-reencapsulation case.
-
-When Ethernet NSH packets are received and then resent to Ethernet NSH port.
-The decapsulation and encapsulation will be implemented. However, tunnel pop
-and tunnel push actions are very time-consuming when decapsulation and
-encapsulation.
-
-With this feature (options:tun_nodecap=true), tunnel port will parse the input
-tunnel packets, but the tunnel header will be kept. And the tunnel header can
-be modified by the set field actions. This feature can improve performance.
-
-Signed-off-by: Ricky Li <ricky.li at intel.com>
-Signed-off-by: Mengke Liu <mengke.liu at intel.com>
----
- lib/netdev-vport.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
- lib/odp-util.c | 3 ++-
- lib/packets.c | 10 +++++++++-
- tests/tunnel.at | 21 +++++++++++++++++++++
- 4 files changed, 74 insertions(+), 3 deletions(-)
-
-diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
-index 0a3da8d..038f1e1 100644
---- a/lib/netdev-vport.c
-+++ b/lib/netdev-vport.c
-@@ -1865,6 +1865,47 @@ netdev_nsh_pop_header(struct dp_packet *packet)
- }
-
- static int
-+netdev_nsh_pop_header_spec(struct dp_packet *packet,
-+ const struct ovs_action_pop_tnl *data)
-+{
-+ struct pkt_metadata *md = &packet->md;
-+ struct flow_tnl *tnl = &md->tunnel;
-+ struct eth_header *eth;
-+ struct nshhdr *nsh;
-+
-+ if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
-+
-+ pkt_metadata_init_tnl(md);
-+ if (ETH_NSH_HLEN > dp_packet_size(packet)) {
-+ return EINVAL;
-+ }
-+
-+ eth = (struct eth_header *) dp_packet_data(packet);
-+ memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
-+ memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
-+
-+ nsh = (struct nshhdr *) (eth + 1);
-+ tnl->nsp = nsh->b.b2 << 8;
-+ tnl->nsi = nsh->b.svc_idx;
-+ tnl->nshc1 = nsh->c.nshc1;
-+ tnl->nshc2 = nsh->c.nshc2;
-+ tnl->nshc3 = nsh->c.nshc3;
-+ tnl->nshc4 = nsh->c.nshc4;
-+ tnl->flags |= FLOW_TNL_F_NSP;
-+ tnl->flags |= FLOW_TNL_F_NSI;
-+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
-+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
-+
-+ tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_ETHERNET_PRST| NSH_TNL_F_NODECAP;
-+ tnl->tun_len = ETH_NSH_HLEN;
-+
-+ return 0;
-+ }
-+
-+ return EINVAL;
-+}
-+
-+static int
- netdev_nsh_build_header(const struct netdev *netdev,
- struct ovs_action_push_tnl *data,
- const struct flow *tnl_flow)
-@@ -2144,7 +2185,7 @@ netdev_vport_tunnel_register(void)
- TUNNEL_CLASS("eth_nsh", "nsh_sys", netdev_nsh_build_header,
- netdev_nsh_push_header,
- netdev_nsh_pop_header,
-- NULL),
-+ netdev_nsh_pop_header_spec),
- };
- static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
-
-diff --git a/lib/odp-util.c b/lib/odp-util.c
-index c2af063..a87b3be 100644
---- a/lib/odp-util.c
-+++ b/lib/odp-util.c
-@@ -4837,7 +4837,8 @@ commit_odp_tunnel_set_action(const struct flow_tnl *tunnel, struct flow_tnl *bas
- struct ofpbuf *odp_actions)
- {
- /* A valid IPV4_TUNNEL must have non-zero ip_dst. */
-- if (tunnel->ip_dst) {
-+ if (tunnel->ip_dst ||
-+ tunnel->nsh_flags & NSH_TNL_F_ETHERNET_PARSED) {
-
- if (!memcmp(tunnel, base, sizeof *tunnel)) {
- return;
-diff --git a/lib/packets.c b/lib/packets.c
-index 7dab4b5..14a19b1 100644
---- a/lib/packets.c
-+++ b/lib/packets.c
-@@ -934,7 +934,15 @@ packet_set_nsh(struct dp_packet *packet, struct flow_tnl *tun_key)
-
- eth = (struct eth_header *) dp_packet_data(packet);
-
-- if (tun_key->nsh_flags & NSH_TNL_F_VXLAN_PRST) {
-+ if (tun_key->nsh_flags & NSH_TNL_F_ETHERNET_PRST) {
-+ nsh = (struct nshhdr *) (eth + 1);
-+ nsh->b.b2 = tun_key->nsp >> 8;
-+ nsh->b.svc_idx = tun_key->nsi;
-+ nsh->c.nshc1 = tun_key->nshc1;
-+ nsh->c.nshc2 = tun_key->nshc2;
-+ nsh->c.nshc3 = tun_key->nshc3;
-+ nsh->c.nshc4 = tun_key->nshc4;
-+ } else if (tun_key->nsh_flags & NSH_TNL_F_VXLAN_PRST) {
- struct ip_header *ip = (struct ip_header *) (eth + 1);
- struct udp_header *udp = (struct udp_header *) (ip + 1);
- struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
-diff --git a/tests/tunnel.at b/tests/tunnel.at
-index 19221fb..1bbf5e2 100644
---- a/tests/tunnel.at
-+++ b/tests/tunnel.at
-@@ -765,6 +765,27 @@ AT_CHECK([tail -1 stdout], [0],
- OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
- AT_CLEANUP
-
-+AT_SETUP([tunnel - ETHERNET NSH tun_nodecap - user space])
-+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
-+AT_CHECK([ovs-vsctl add-port br0 p1 -- set interface p1 type=eth_nsh options:tun_nodecap=true options:remote_mac=00:00:00:11:11:22 \
-+options:out_nsp=flow options:out_nsi=flow options:in_nshc1=flow options:in_nshc2=flow options:in_nshc3=flow options:in_nshc4=flow ofport_request=2], [0])
-+
-+AT_CHECK([ovs-ofctl add-flow br0 "priority=16, in_port=1, action=local"])
-+
-+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
-+ br0 65534/100: (dummy)
-+ p0 1/1: (dummy)
-+ p1 2/2: (eth_nsh: in_nshc1=flow, in_nshc2=flow, in_nshc3=flow, in_nshc4=flow, out_nsi=flow, out_nsp=flow, remote_mac=00:00:00:11:11:22, tun_nodecap=true)
-+])
-+
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=00:00:00:11:11:22,dst=50:54:00:00:00:07),eth_type(0x894f)'], [0], [stdout])
-+AT_CHECK([tail -1 stdout], [0],
-+ [Datapath actions: tnl_pop_spec(tnl_port(2),pop_type=2)
-+])
-+
-+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/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 \
---
-1.9.3
-
diff --git a/ovs-nsh/patches/060685.patch b/ovs-nsh/patches/060685.patch
deleted file mode 100644
index a9da911..0000000
--- a/ovs-nsh/patches/060685.patch
+++ /dev/null
@@ -1,675 +0,0 @@
-VxLAN-GPE NSH packet.
-
-With this feature (options:nsh-convert=true),when Ethernet-NSH packet (Outer
-MAC header + original packet) are received by Ethernet-NSH port, the vport
-will remove Outer MAC header, and then modify and push the
-outer MAC header + Outer IP header + UDP header + VxLAN-GPE. Then the packet
-with Ethernet+NSH format is converted to VxLAN-GPE NSH packet.
-
-Signed-off-by: Ricky Li <ricky.li at intel.com>
-Signed-off-by: Mengke Liu <mengke.liu at intel.com>
----
- datapath/linux/compat/include/linux/openvswitch.h | 3 +-
- lib/netdev-vport.c | 128 +++++++++++++++++-----
- lib/odp-util.c | 121 +++++++++++++++++++-
- ofproto/ofproto-dpif-xlate.c | 113 ++++++++++++++++++-
- ofproto/tunnel.c | 75 +++++++++++++
- ofproto/tunnel.h | 7 ++
- tests/tunnel.at | 29 +++++
- 7 files changed, 440 insertions(+), 36 deletions(-)
-
-diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
-index 045a1f4..916aeae 100644
---- a/datapath/linux/compat/include/linux/openvswitch.h
-+++ b/datapath/linux/compat/include/linux/openvswitch.h
-@@ -651,7 +651,8 @@ struct ovs_action_push_tnl {
-
- enum ovs_pop_spec_action_type {
- OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH,
-- OVS_POP_SPEC_ACTION_NO_DECAP = 2,
-+ OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH,
-+ OVS_POP_SPEC_ACTION_NO_DECAP,
- };
-
- /*
-diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
-index 038f1e1..6142935 100644
---- a/lib/netdev-vport.c
-+++ b/lib/netdev-vport.c
-@@ -167,7 +167,8 @@ netdev_vport_needs_dst_port(const struct netdev *dev)
-
- return (class->get_config == get_tunnel_config &&
- (!strcmp("geneve", type) || !strcmp("vxlan", type) ||
-- !strcmp("lisp", type) || !strcmp("stt", type)) );
-+ !strcmp("lisp", type) || !strcmp("stt", type) ||
-+ !strcmp("eth_nsh", type)) );
- }
-
- const char *
-@@ -890,7 +891,8 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
- if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
- (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
- (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
-- (!strcmp("stt", type) && dst_port != STT_DST_PORT)) {
-+ (!strcmp("stt", type) && dst_port != STT_DST_PORT) ||
-+ (!strcmp("eth_nsh", type) && tnl_cfg.nsh_convert)) {
- smap_add_format(args, "dst_port", "%d", dst_port);
- }
- }
-@@ -1864,42 +1866,116 @@ netdev_nsh_pop_header(struct dp_packet *packet)
- return 0;
- }
-
-+
- static int
--netdev_nsh_pop_header_spec(struct dp_packet *packet,
-- const struct ovs_action_pop_tnl *data)
-+eth_nsh_extract_md_no_decap(struct dp_packet *packet)
- {
- struct pkt_metadata *md = &packet->md;
- struct flow_tnl *tnl = &md->tunnel;
- struct eth_header *eth;
- struct nshhdr *nsh;
-
-- if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
-+ pkt_metadata_init_tnl(md);
-+ if (ETH_NSH_HLEN > dp_packet_size(packet)) {
-+ return EINVAL;
-+ }
-
-- pkt_metadata_init_tnl(md);
-- if (ETH_NSH_HLEN > dp_packet_size(packet)) {
-- return EINVAL;
-- }
-+ eth = (struct eth_header *) dp_packet_data(packet);
-+ memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
-+ memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
-+
-+ nsh = (struct nshhdr *) (eth + 1);
-+ tnl->nsp = nsh->b.b2 << 8;
-+ tnl->nsi = nsh->b.svc_idx;
-+ tnl->nshc1 = nsh->c.nshc1;
-+ tnl->nshc2 = nsh->c.nshc2;
-+ tnl->nshc3 = nsh->c.nshc3;
-+ tnl->nshc4 = nsh->c.nshc4;
-+ tnl->flags |= FLOW_TNL_F_NSP;
-+ tnl->flags |= FLOW_TNL_F_NSI;
-+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
-+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
-+ tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_ETHERNET_PRST| NSH_TNL_F_NODECAP;
-+ tnl->tun_len = ETH_NSH_HLEN;
-+
-+ return 0;
-+}
-+
-+static int
-+eth_nsh_extract_md_convert_to_vxlan_gpe_nsh(struct dp_packet *packet,
-+ const struct ovs_action_pop_tnl *data)
-+{
-+ struct pkt_metadata *md = &packet->md;
-+ struct flow_tnl *tnl = &md->tunnel;
-+ struct eth_header *eth;
-+ struct ip_header *ip;
-+ struct udp_header *udp;
-+ struct nshhdr *nsh;
-+
-+ pkt_metadata_init_tnl(md);
-+ if (ETH_NSH_HLEN > dp_packet_size(packet)) {
-+ return EINVAL;
-+ }
-+
-+ eth = (struct eth_header *) dp_packet_data(packet);
-+ memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
-+ memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
-
-- eth = (struct eth_header *) dp_packet_data(packet);
-- memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
-- memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
--
-- nsh = (struct nshhdr *) (eth + 1);
-- tnl->nsp = nsh->b.b2 << 8;
-- tnl->nsi = nsh->b.svc_idx;
-- tnl->nshc1 = nsh->c.nshc1;
-- tnl->nshc2 = nsh->c.nshc2;
-- tnl->nshc3 = nsh->c.nshc3;
-- tnl->nshc4 = nsh->c.nshc4;
-- tnl->flags |= FLOW_TNL_F_NSP;
-- tnl->flags |= FLOW_TNL_F_NSI;
-- tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
-+ nsh = (struct nshhdr *) (eth + 1);
-+ tnl->nsp = nsh->b.b2 << 8;
-+ tnl->nsi = nsh->b.svc_idx;
-+ tnl->nshc1 = nsh->c.nshc1;
-+ tnl->nshc2 = nsh->c.nshc2;
-+ tnl->nshc3 = nsh->c.nshc3;
-+ tnl->nshc4 = nsh->c.nshc4;
-+
-+ tnl->flags |= FLOW_TNL_F_NSP;
-+ tnl->flags |= FLOW_TNL_F_NSI;
-+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
- FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
-
-- tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_ETHERNET_PRST| NSH_TNL_F_NODECAP;
-- tnl->tun_len = ETH_NSH_HLEN;
-+ tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_VXLAN_PRST;
-
-- return 0;
-+ dp_packet_reset_packet(packet, ETH_NSH_HLEN - sizeof (struct nshhdr));
-+ eth = (struct eth_header *) dp_packet_push_uninit(packet, data->header_len);
-+ memcpy(eth, data->header, data->header_len);
-+
-+
-+ /* set IP length, csum */
-+ int ip_tot_size = dp_packet_size(packet) - sizeof (struct eth_header);
-+ ip = ip_hdr(eth);
-+ ip->ip_tot_len = htons(ip_tot_size);
-+ ip->ip_csum = recalc_csum16(ip->ip_csum, 0, ip->ip_tot_len);
-+
-+ /* set udp src port */
-+ udp = (struct udp_header *) (ip + 1);
-+ udp->udp_src = get_src_port(packet);
-+ udp->udp_len = htons(ip_tot_size - sizeof (struct ip_header));
-+
-+ /* udp_csum is zero */
-+ if (udp->udp_csum) {
-+ uint32_t csum = packet_csum_pseudoheader(ip);
-+
-+ csum = csum_continue(csum, udp,
-+ ip_tot_size - sizeof (struct ip_header));
-+ udp->udp_csum = csum_finish(csum);
-+
-+ if (!udp->udp_csum) {
-+ udp->udp_csum = htons(0xffff);
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+netdev_nsh_pop_header_spec(struct dp_packet *packet,
-+ const struct ovs_action_pop_tnl *data)
-+{
-+ if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH) {
-+ return eth_nsh_extract_md_convert_to_vxlan_gpe_nsh(packet, data);
-+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
-+ return eth_nsh_extract_md_no_decap(packet);
- }
-
- return EINVAL;
-diff --git a/lib/odp-util.c b/lib/odp-util.c
-index a87b3be..183844f 100644
---- a/lib/odp-util.c
-+++ b/lib/odp-util.c
-@@ -515,7 +515,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
- gnh->oam ? "oam," : "",
- gnh->critical ? "crit," : "",
- ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
--
-+
- if (gnh->opt_len) {
- ds_put_cstr(ds, ",options(");
- format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
-@@ -579,10 +579,11 @@ static void
- format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data)
- {
- const struct eth_header *eth;
-+ const struct ip_header *ip;
-+ const void *l3;
-
- eth = (const struct eth_header *)data->header;
- if (data->tnl_type == OVS_VPORT_TYPE_NSH) {
-- const struct nshhdr *nsh = (const struct nshhdr *) (eth + 1);
-
- /* Ethernet */
- ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
-@@ -591,7 +592,37 @@ format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data)
- ds_put_format(ds, ",src=");
- ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
- ds_put_format(ds, ",dl_type=0x%04"PRIx16")", ntohs(eth->eth_type));
-- ds_put_format(ds, "),");
-+ ds_put_format(ds, "),");
-+ } else if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
-+ l3 = eth + 1;
-+ ip = (const struct ip_header *)l3;
-+
-+ /* Ethernet */
-+ ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
-+ data->header_len, data->tnl_type);
-+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
-+ ds_put_format(ds, ",src=");
-+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
-+ ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));
-+
-+ /* IPv4 */
-+ ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
-+ ",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
-+ IP_ARGS(get_16aligned_be32(&ip->ip_src)),
-+ IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
-+ ip->ip_proto, ip->ip_tos,
-+ ip->ip_ttl,
-+ ip->ip_frag_off);
-+ if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
-+ const struct vxlanhdr *vxh;
-+
-+ vxh = format_udp_tnl_push_header(ds, ip);
-+
-+ ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
-+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
-+ ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
-+ }
-+ ds_put_format(ds, "),");
- }
- }
-
-@@ -615,9 +646,10 @@ format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
- data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
-
- ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
-- if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
-+ if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH ||
-+ data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH) {
- ds_put_format(ds, "pop_type=%"PRIu16",",
-- OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH);
-+ data->pop_type);
- format_odp_tnl_pop_header(ds, data);
- ds_put_format(ds, "out_port(%"PRIu32"))", data->out_port);
-
-@@ -1174,6 +1206,85 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
- if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
- return -EINVAL;
- }
-+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH) {
-+ struct eth_header *eth;
-+ struct ip_header *ip;
-+ struct udp_header *udp;
-+ uint16_t dl_type, udp_src, udp_dst, csum;
-+ ovs_be32 sip, dip;
-+ uint32_t tnl_type = 0, header_len = 0;
-+ void *l3, *l4;
-+ int n = 0;
-+
-+ eth = (struct eth_header *) data->header;
-+ l3 = (data->header + sizeof *eth);
-+ l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
-+ ip = (struct ip_header *) l3;
-+ if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
-+ "eth(dst="ETH_ADDR_SCAN_FMT",",
-+ &data->header_len,
-+ &data->tnl_type,
-+ ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
-+ return -EINVAL;
-+ }
-+
-+ if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
-+ ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
-+ return -EINVAL;
-+ }
-+ if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
-+ return -EINVAL;
-+ }
-+ eth->eth_type = htons(dl_type);
-+
-+ /* IPv4 */
-+ if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
-+ ",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
-+ IP_SCAN_ARGS(&sip),
-+ IP_SCAN_ARGS(&dip),
-+ &ip->ip_proto, &ip->ip_tos,
-+ &ip->ip_ttl, &ip->ip_frag_off)) {
-+ return -EINVAL;
-+ }
-+ put_16aligned_be32(&ip->ip_src, sip);
-+ put_16aligned_be32(&ip->ip_dst, dip);
-+
-+ /* Tunnel header */
-+ udp = (struct udp_header *) l4;
-+ if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
-+ &udp_src, &udp_dst, &csum)) {
-+ uint32_t vx_flags, vni;
-+
-+ udp->udp_src = htons(udp_src);
-+ udp->udp_dst = htons(udp_dst);
-+ udp->udp_len = 0;
-+ udp->udp_csum = htons(csum);
-+
-+ if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
-+ &vx_flags, &vni)) {
-+ struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
-+
-+ put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
-+ put_16aligned_be32(&vxh->vx_vni, htonl(vni << 8));
-+ tnl_type = OVS_VPORT_TYPE_VXLAN;
-+ header_len = sizeof *eth + sizeof *ip +
-+ sizeof *udp + sizeof *vxh;
-+ } else {
-+ return -EINVAL;
-+ }
-+ /* check tunnel meta data. */
-+ if (data->tnl_type != tnl_type) {
-+ return -EINVAL;
-+ }
-+ if (data->header_len != header_len) {
-+ return -EINVAL;
-+ }
-+
-+ /* Out port */
-+ if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
-+ return -EINVAL;
-+ }
-+ }
- } else {
- return -EINVAL;
- }
-diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
-index 90b5a95..1578a0c 100644
---- a/ofproto/ofproto-dpif-xlate.c
-+++ b/ofproto/ofproto-dpif-xlate.c
-@@ -2653,7 +2653,7 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
- }
-
- static int
--tnl_route_lookup_flow(const struct flow *oflow,
-+tnl_route_lookup_flow__(ovs_be32 ip_dst,
- ovs_be32 *ip, struct xport **out_port)
- {
- char out_dev[IFNAMSIZ];
-@@ -2661,14 +2661,14 @@ tnl_route_lookup_flow(const struct flow *oflow,
- struct xlate_cfg *xcfg;
- ovs_be32 gw;
-
-- if (!ovs_router_lookup(oflow->tunnel.ip_dst, out_dev, &gw)) {
-+ if (!ovs_router_lookup(ip_dst, out_dev, &gw)) {
- return -ENOENT;
- }
-
- if (gw) {
- *ip = gw;
- } else {
-- *ip = oflow->tunnel.ip_dst;
-+ *ip = ip_dst;
- }
-
- xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
-@@ -2690,6 +2690,12 @@ tnl_route_lookup_flow(const struct flow *oflow,
- }
-
- static int
-+tnl_route_lookup_flow(const struct flow *oflow,
-+ ovs_be32 *ip, struct xport **out_port){
-+ return tnl_route_lookup_flow__(oflow->tunnel.ip_dst, ip, out_port);
-+}
-+
-+static int
- tnl_outdev_lookup_mac(const struct eth_addr *mac,
- struct xport **out_port)
- {
-@@ -2818,6 +2824,100 @@ build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport,
- }
-
- static int
-+build_eth_nsh_tunnel_pop(struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct flow *flow)
-+{
-+ const struct netdev_tunnel_config * cfg;
-+
-+ cfg = tnl_port_cfg(tunnel_odp_port, flow);
-+
-+ if (cfg) {
-+ if (cfg->nsh_convert && (ntohs(cfg->dst_port) == VXGPE_DST_PORT)) {
-+
-+ struct ovs_action_pop_tnl tnl_pop_data;
-+ struct xport *out_dev = NULL;
-+ ovs_be32 s_ip, d_ip = 0;
-+ struct eth_addr smac;
-+ struct eth_addr dmac;
-+ int err;
-+
-+ err = tnl_route_lookup_flow__(cfg->ip_dst, &d_ip, &out_dev);
-+ if (err) {
-+ xlate_report(ctx, "native tunnel routing failed");
-+ return err;
-+ }
-+ xlate_report(ctx, "tunneling to "IP_FMT" via %s",
-+ IP_ARGS(d_ip), netdev_get_name(out_dev->netdev));
-+
-+ /* Use mac addr of bridge port of the peer. */
-+ err = netdev_get_etheraddr(out_dev->netdev, &smac);
-+ if (err) {
-+ xlate_report(ctx, "tunnel output device lacks Ethernet address");
-+ return err;
-+ }
-+
-+ err = netdev_get_in4(out_dev->netdev, (struct in_addr *) &s_ip, NULL);
-+ if (err) {
-+ xlate_report(ctx, "tunnel output device lacks IPv4 address");
-+ return err;
-+ }
-+
-+ err = tnl_arp_lookup(out_dev->xbridge->name, d_ip, &dmac);
-+ if (err) {
-+ xlate_report(ctx, "ARP cache miss for "IP_FMT" on bridge %s, "
-+ "sending ARP request",
-+ IP_ARGS(d_ip), out_dev->xbridge->name);
-+ tnl_send_arp_request(ctx, out_dev, smac, s_ip, d_ip);
-+ return err;
-+ }
-+ if (ctx->xin->xcache) {
-+ struct xc_entry *entry;
-+
-+ entry = xlate_cache_add_entry(ctx->xin->xcache, XC_TNL_ARP);
-+ ovs_strlcpy(entry->u.tnl_arp_cache.br_name, out_dev->xbridge->name,
-+ sizeof entry->u.tnl_arp_cache.br_name);
-+ entry->u.tnl_arp_cache.d_ip = d_ip;
-+ }
-+
-+ xlate_report(ctx, "tunneling from "ETH_ADDR_FMT" "IP_FMT
-+ " to "ETH_ADDR_FMT" "IP_FMT,
-+ ETH_ADDR_ARGS(smac), IP_ARGS(s_ip),
-+ ETH_ADDR_ARGS(dmac), IP_ARGS(d_ip));
-+ err = tnl_port_build_header_odport_popspec(tunnel_odp_port, cfg,
-+ dmac, smac, s_ip, &tnl_pop_data);
-+ if (err) {
-+ return err;
-+ }
-+ tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
-+ tnl_pop_data.out_port = odp_to_u32(out_dev->odp_port);
-+ tnl_pop_data.tnl_type = OVS_VPORT_TYPE_VXLAN;
-+ tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH;
-+ odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
-+
-+ } else if (cfg->tun_nodecap) {
-+ struct ovs_action_pop_tnl tnl_pop_data;
-+ memset(&tnl_pop_data, 0, sizeof tnl_pop_data);
-+
-+ tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
-+ tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_NO_DECAP;
-+ odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
-+
-+ } else {
-+ nl_msg_put_odp_port(ctx->odp_actions,
-+ OVS_ACTION_ATTR_TUNNEL_POP,
-+ tunnel_odp_port);
-+ }
-+
-+ } else {
-+ nl_msg_put_odp_port(ctx->odp_actions,
-+ OVS_ACTION_ATTR_TUNNEL_POP,
-+ tunnel_odp_port);
-+ }
-+
-+ return 0;
-+}
-+
-+
-+static int
- build_tunnel_pop(const struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct flow *flow)
- {
- const struct netdev_tunnel_config * cfg;
-@@ -3185,7 +3285,12 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
- if (odp_tnl_port != ODPP_NONE &&
- !(flow->tunnel.nsh_flags & NSH_TNL_F_NODECAP)) {
- flow_tnl = flow->tunnel;
-- build_tunnel_pop(ctx, odp_tnl_port, flow);
-+ if(flow->dl_type == htons(ETH_TYPE_NSH)) {
-+ build_eth_nsh_tunnel_pop(ctx, odp_tnl_port, flow);
-+ }
-+ else {
-+ build_tunnel_pop(ctx, odp_tnl_port, flow);
-+ }
- flow->tunnel = flow_tnl;
- } else {
- /* Tunnel push-pop action is not compatible with
-diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
-index cc0c91a..e279e92 100644
---- a/ofproto/tunnel.c
-+++ b/ofproto/tunnel.c
-@@ -47,6 +47,13 @@ VLOG_DEFINE_THIS_MODULE(tunnel);
-
- #define ETH_NSH_HLEN (sizeof(struct eth_header) + \
- sizeof(struct nshhdr))
-+
-+#define VXNSH_HLEN (sizeof(struct eth_header) + \
-+ sizeof(struct ip_header) + \
-+ sizeof(struct udp_header) + \
-+ sizeof(struct vxgpehdr) + \
-+ sizeof(struct nshhdr))
-+
- struct tnl_match {
- ovs_be64 in_key;
- ovs_be32 in_nsp;
-@@ -1044,6 +1051,74 @@ tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock)
- }
-
- int
-+tnl_port_build_header_odport_popspec(const odp_port_t odp_port,
-+ const struct netdev_tunnel_config *cfg,
-+ const struct eth_addr dmac,
-+ const struct eth_addr smac,
-+ ovs_be32 ip_src, struct ovs_action_pop_tnl *data)
-+{
-+ struct tnl_port *tnl_port;
-+ struct eth_header *eth;
-+ struct ip_header *ip;
-+ struct udp_header *udp;
-+ void *l3;
-+
-+ fat_rwlock_rdlock(&rwlock);
-+ tnl_port = tnl_find_odp_port(odp_port);
-+ ovs_assert(tnl_port);
-+
-+ /* Build Ethernet headers. */
-+ memset(data->header, 0, sizeof data->header);
-+
-+ eth = (struct eth_header *)data->header;
-+ eth->eth_dst = dmac;
-+ eth->eth_src = smac;
-+ eth->eth_type = htons(ETH_TYPE_IP);
-+
-+ l3 = (eth + 1);
-+ ip = (struct ip_header *) l3;
-+
-+ /* Build IP header */
-+ ip->ip_ihl_ver = IP_IHL_VER(5, 4);
-+ ip->ip_tos = cfg->tos;
-+ ip->ip_ttl = cfg->ttl;
-+ ip->ip_frag_off = cfg->dont_fragment ? htons(IP_DF) : 0;
-+ put_16aligned_be32(&ip->ip_src, ip_src);
-+ put_16aligned_be32(&ip->ip_dst, cfg->ip_dst);
-+ ip->ip_proto = IPPROTO_UDP;
-+ ip->ip_csum = csum(ip, sizeof *ip);
-+
-+ /* Build UDP header */
-+ udp = (struct udp_header *) (ip + 1);
-+ udp->udp_dst = cfg->dst_port;
-+
-+ if (cfg->csum) {
-+ /* Write a value in now to mark that we should compute the checksum
-+ * later. 0xffff is handy because it is transparent to the
-+ * calculation. */
-+ udp->udp_csum = htons(0xffff);
-+ }
-+ /* Build VxLAN-GPE header */
-+ if (ntohs(udp->udp_dst) == VXGPE_DST_PORT){
-+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
-+
-+ memset(vxg, 0, sizeof *vxg);
-+ vxg->i = 0x01;
-+ vxg->p = 0x01;
-+ vxg->ver = 0x01;
-+ vxg->proto = VXG_P_NSH;
-+ put_16aligned_be32(&vxg->vx_vni, htonl(ntohll(cfg->out_key) << 8));
-+
-+ }
-+
-+ data->header_len = VXNSH_HLEN - sizeof (struct nshhdr);
-+ data->tnl_type = OVS_VPORT_TYPE_VXLAN;
-+
-+ fat_rwlock_unlock(&rwlock);
-+ return 0;
-+}
-+
-+int
- tnl_port_build_header(const struct ofport_dpif *ofport,
- const struct flow *tnl_flow,
- const struct eth_addr dmac,
-diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h
-index d771476..9f2f11d 100644
---- a/ofproto/tunnel.h
-+++ b/ofproto/tunnel.h
-@@ -55,6 +55,13 @@ tnl_port_should_receive(const struct flow *flow)
- memcmp(flow->tunnel.eth_dst.ea, ð_addr_zero, ETH_ADDR_LEN));
- }
-
-+int
-+tnl_port_build_header_odport_popspec(const odp_port_t odp_port,
-+ const struct netdev_tunnel_config *cfg,
-+ const struct eth_addr dmac,
-+ const struct eth_addr smac,
-+ ovs_be32 ip_src, struct ovs_action_pop_tnl *data);
-+
- int tnl_port_build_header(const struct ofport_dpif *ofport,
- const struct flow *tnl_flow,
- const struct eth_addr dmac,
-diff --git a/tests/tunnel.at b/tests/tunnel.at
-index 1bbf5e2..c740966 100644
---- a/tests/tunnel.at
-+++ b/tests/tunnel.at
-@@ -786,6 +786,35 @@ AT_CHECK([tail -1 stdout], [0],
- OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
- AT_CLEANUP
-
-+AT_SETUP([tunnel - ETHERNET - nsh_convert from Ethernet NSH to VXLAN-GPE NSH - user space])
-+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
-+AT_CHECK([ovs-vsctl add-port br0 p1 -- set interface p1 type=eth_nsh options:remote_mac=00:00:00:11:11:22 options:nsh_convert=true \
-+options:nsi=flow options:nsp=flow options:nshc1=flow options:in_key=flow options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=2], [0])
-+
-+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
-+])
-+
-+AT_CHECK([
-+ovs-appctl ovs/route/add 1.1.1.1/24 br0
-+ovs-appctl tnl/arp/set br0 1.1.1.1 68:05:ca:30:6b:d1
-+],[0],[stdout])
-+
-+AT_CHECK([ovs-ofctl add-flow br0 "priority=16, in_port=1, action=local"])
-+
-+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
-+ br0 65534/100: (dummy)
-+ p0 1/1: (dummy)
-+ p1 2/4790: (eth_nsh: dst_port=4790, in_key=flow, nsh_convert=true, nshc1=flow, nsi=flow, nsp=flow, remote_ip=1.1.1.1, remote_mac=00:00:00:11:11:22)
-+])
-+
-+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=00:00:00:11:11:22,dst=50:54:00:00:00:07),eth_type(0x894f)'], [0], [stdout])
-+AT_CHECK([tail -1 stdout], [0],
-+ [Datapath actions: tnl_pop_spec(tnl_port(4790),pop_type=1,header(size=50,type=4,eth(dst=68:05:ca:30:6b:d1,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=1.1.1.1,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlan(flags=0xc400004,vni=0x0)),out_port(100))
-+])
-+
-+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/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 \
---
-1.9.3
-
diff --git a/ovs-nsh/patches/17a6124.patch b/ovs-nsh/patches/17a6124.patch
new file mode 100644
index 0000000..47de814
--- /dev/null
+++ b/ovs-nsh/patches/17a6124.patch
@@ -0,0 +1,259 @@
+commit 17a6124ec1879fd7bb95872cf534165ae21131f7
+Author: Yi Yang
+Date: Wed Apr 13 18:17:21 2016 +0800
+
+ Fix too large stack frame size
+
+ Signed-off-by: Yi Yang
+
+diff --git a/datapath/datapath.c b/datapath/datapath.c
+index 5bec072..4baf242 100644
+--- a/datapath/datapath.c
++++ b/datapath/datapath.c
+@@ -928,7 +928,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
+ struct sw_flow_mask mask;
+ struct sk_buff *reply;
+ struct datapath *dp;
+- struct sw_flow_key key;
++ struct sw_flow_key *key = NULL;
+ struct sw_flow_actions *acts;
+ struct sw_flow_match match;
+ u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]);
+@@ -946,6 +946,12 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
+ goto error;
+ }
+
++ error = -ENOMEM;
++ key = kzalloc(sizeof(struct sw_flow_key), GFP_KERNEL);
++ if (key == NULL) {
++ goto error;
++ }
++
+ /* Most of the time we need to allocate a new flow, do it before
+ * locking.
+ */
+@@ -956,17 +962,17 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
+ }
+
+ /* Extract key. */
+- ovs_match_init(&match, &key, &mask);
++ ovs_match_init(&match, key, &mask);
+ error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
+ a[OVS_FLOW_ATTR_MASK], log);
+ if (error)
+ goto err_kfree_flow;
+
+- ovs_flow_mask_key(&new_flow->key, &key, true, &mask);
++ ovs_flow_mask_key(&new_flow->key, key, true, &mask);
+
+ /* Extract flow identifier. */
+ error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID],
+- &key, log);
++ key, log);
+ if (error)
+ goto err_kfree_flow;
+
+@@ -996,7 +1002,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
+ if (ovs_identifier_is_ufid(&new_flow->id))
+ flow = ovs_flow_tbl_lookup_ufid(&dp->table, &new_flow->id);
+ if (!flow)
+- flow = ovs_flow_tbl_lookup(&dp->table, &key);
++ flow = ovs_flow_tbl_lookup(&dp->table, key);
+ if (likely(!flow)) {
+ rcu_assign_pointer(new_flow->sf_acts, acts);
+
+@@ -1066,6 +1072,10 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
+
+ if (reply)
+ ovs_notify(&dp_flow_genl_family, &ovs_dp_flow_multicast_group, reply, info);
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return 0;
+
+ err_unlock_ovs:
+@@ -1076,6 +1086,10 @@ err_kfree_acts:
+ err_kfree_flow:
+ ovs_flow_free(new_flow, false);
+ error:
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return error;
+ }
+
+@@ -1106,7 +1120,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
+ struct net *net = sock_net(skb->sk);
+ struct nlattr **a = info->attrs;
+ struct ovs_header *ovs_header = info->userhdr;
+- struct sw_flow_key key;
++ struct sw_flow_key *key = NULL;
+ struct sw_flow *flow;
+ struct sw_flow_mask mask;
+ struct sk_buff *reply = NULL;
+@@ -1119,6 +1133,12 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
+ bool log = !a[OVS_FLOW_ATTR_PROBE];
+ bool ufid_present;
+
++ error = -ENOMEM;
++ key = kzalloc(sizeof(struct sw_flow_key), GFP_KERNEL);
++ if (key == NULL) {
++ goto error;
++ }
++
+ /* Extract key. */
+ error = -EINVAL;
+ if (!a[OVS_FLOW_ATTR_KEY]) {
+@@ -1127,7 +1147,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
+ }
+
+ ufid_present = ovs_nla_get_ufid(&sfid, a[OVS_FLOW_ATTR_UFID], log);
+- ovs_match_init(&match, &key, &mask);
++ ovs_match_init(&match, key, &mask);
+ error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
+ a[OVS_FLOW_ATTR_MASK], log);
+ if (error)
+@@ -1135,7 +1155,7 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
+
+ /* Validate actions. */
+ if (a[OVS_FLOW_ATTR_ACTIONS]) {
+- acts = get_flow_actions(net, a[OVS_FLOW_ATTR_ACTIONS], &key,
++ acts = get_flow_actions(net, a[OVS_FLOW_ATTR_ACTIONS], key,
+ &mask, log);
+ if (IS_ERR(acts)) {
+ error = PTR_ERR(acts);
+@@ -1203,6 +1223,10 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
+ if (old_acts)
+ ovs_nla_free_flow_actions_rcu(old_acts);
+
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return 0;
+
+ err_unlock_ovs:
+@@ -1211,6 +1235,10 @@ err_unlock_ovs:
+ err_kfree_acts:
+ ovs_nla_free_flow_actions(acts);
+ error:
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return error;
+ }
+
+@@ -1219,7 +1247,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
+ struct nlattr **a = info->attrs;
+ struct ovs_header *ovs_header = info->userhdr;
+ struct net *net = sock_net(skb->sk);
+- struct sw_flow_key key;
++ struct sw_flow_key *key = NULL;
+ struct sk_buff *reply;
+ struct sw_flow *flow;
+ struct datapath *dp;
+@@ -1230,9 +1258,15 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
+ bool log = !a[OVS_FLOW_ATTR_PROBE];
+ bool ufid_present;
+
++ err = -ENOMEM;
++ key = kzalloc(sizeof(struct sw_flow_key), GFP_KERNEL);
++ if (key == NULL) {
++ return err;
++ }
++
+ ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log);
+ if (a[OVS_FLOW_ATTR_KEY]) {
+- ovs_match_init(&match, &key, NULL);
++ ovs_match_init(&match, key, NULL);
+ err = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], NULL,
+ log);
+ } else if (!ufid_present) {
+@@ -1240,9 +1274,13 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
+ "Flow get message rejected, Key attribute missing.");
+ err = -EINVAL;
+ }
+- if (err)
++ if (err) {
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return err;
+-
++ }
+ ovs_lock();
+ dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+ if (!dp) {
+@@ -1267,9 +1305,17 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
+ }
+
+ ovs_unlock();
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return genlmsg_reply(reply, info);
+ unlock:
+ ovs_unlock();
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return err;
+ }
+
+@@ -1278,7 +1324,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
+ struct nlattr **a = info->attrs;
+ struct ovs_header *ovs_header = info->userhdr;
+ struct net *net = sock_net(skb->sk);
+- struct sw_flow_key key;
++ struct sw_flow_key *key = NULL;
+ struct sk_buff *reply;
+ struct sw_flow *flow = NULL;
+ struct datapath *dp;
+@@ -1289,12 +1335,22 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
+ bool log = !a[OVS_FLOW_ATTR_PROBE];
+ bool ufid_present;
+
++ err = -ENOMEM;
++ key = kzalloc(sizeof(struct sw_flow_key), GFP_KERNEL);
++ if (key == NULL) {
++ return err;
++ }
++
+ ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log);
+ if (a[OVS_FLOW_ATTR_KEY]) {
+- ovs_match_init(&match, &key, NULL);
++ ovs_match_init(&match, key, NULL);
+ err = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
+ NULL, log);
+ if (unlikely(err))
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return err;
+ }
+
+@@ -1344,9 +1400,17 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
+ }
+
+ ovs_flow_free(flow, true);
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return 0;
+ unlock:
+ ovs_unlock();
++ if (key != NULL) {
++ kfree(key);
++ key = NULL;
++ }
+ return err;
+ }
+
diff --git a/ovs-nsh/patches/21bd423.patch b/ovs-nsh/patches/21bd423.patch
new file mode 100644
index 0000000..9e03003
--- /dev/null
+++ b/ovs-nsh/patches/21bd423.patch
@@ -0,0 +1,2291 @@
+commit 21bd423871eea767aa125745128a4600abebe8f3
+Author: Yi Yang
+Date: Wed Apr 13 16:39:27 2016 +0800
+
+ Add userspace dataplane nsh support and remove push_eth and pop_eth actions
+
+ Signed-off-by: Mengke Liu
+ Signed-off-by: Ricky Li
+ Signed-off-by: Johnson Li
+ Signed-off-by: Yi Yang
+
+diff --git a/datapath/actions.c b/datapath/actions.c
+index 16fc58f..7072638 100644
+--- a/datapath/actions.c
++++ b/datapath/actions.c
+@@ -254,10 +254,10 @@ static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
+
+ static int pop_nsh(struct sk_buff *skb, struct sw_flow_key *key)
+ {
+- if (!pskb_may_pull(skb, NSH_M_TYPE1_LEN))
++ if (!pskb_may_pull(skb, ETH_NSH_TYPE1_HEADER_SIZE))
+ return -ENOMEM;
+ else
+- __skb_pull(skb, NSH_M_TYPE1_LEN);
++ __skb_pull(skb, ETH_NSH_TYPE1_HEADER_SIZE);
+
+ return 0;
+ }
+@@ -267,48 +267,21 @@ static int push_nsh(struct sk_buff *skb, struct sw_flow_key *key,
+ {
+
+ if (nsh->nsh_mdtype == NSH_M_TYPE1) {
+- if (skb_cow_head(skb, NSH_M_TYPE1_LEN) < 0)
+- return -ENOMEM;
+-
+- skb_push(skb, NSH_M_TYPE1_LEN);
+- OVS_CB(skb)->nsh_header = skb->data;
+- memcpy(OVS_CB(skb)->nsh_header, nsh->header, NSH_M_TYPE1_LEN);
+- }
+- else
+- return -EINVAL;
+-
+- return 0;
+-}
+-
+-static int pop_eth(struct sk_buff *skb, struct sw_flow_key *key)
+-{
+- if (!pskb_may_pull(skb, ENCAP_ETH_PUSH_HEADER_SIZE))
+- return -ENOMEM;
+- else
+- __skb_pull(skb, ENCAP_ETH_PUSH_HEADER_SIZE);
+-
+- return 0;
+-}
+-
+-static int push_eth(struct sk_buff *skb, struct sw_flow_key *key,
+- const struct ovs_action_push_eth *encap_eth)
+-{
+- if (encap_eth->encap_eth_type == htons(ETH_P_NSH)) {
+- if (skb_cow_head(skb, ENCAP_ETH_PUSH_HEADER_SIZE) < 0)
++ if (skb_cow_head(skb, ETH_NSH_TYPE1_HEADER_SIZE) < 0) {
+ return -ENOMEM;
++ }
+
+- skb_push(skb, ENCAP_ETH_PUSH_HEADER_SIZE);
+- OVS_CB(skb)->encap_eth_header = skb->data;
+- memcpy(OVS_CB(skb)->encap_eth_header, encap_eth->header, ENCAP_ETH_PUSH_HEADER_SIZE);
++ skb_push(skb, ETH_NSH_TYPE1_HEADER_SIZE);
++ OVS_CB(skb)->encap_eth_header = (struct encap_eth_hdr *)skb->data;
++ OVS_CB(skb)->nsh_header = (struct nsh_hdr *)(skb->data + ENCAP_ETH_LEN);
++ memcpy(skb->data, nsh->header, ETH_NSH_TYPE1_HEADER_SIZE);
+ }
+- else {
++ else
+ return -EINVAL;
+- }
+
+ return 0;
+ }
+
+-
+ /* 'src' is already properly masked. */
+ static void ether_addr_copy_masked(u8 *dst_, const u8 *src_, const u8 *mask_)
+ {
+@@ -1144,14 +1117,6 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
+ err = pop_nsh(skb, key);
+ break;
+
+- case OVS_ACTION_ATTR_PUSH_ETH:
+- err = push_eth(skb, key, nla_data(a));
+- break;
+-
+- case OVS_ACTION_ATTR_POP_ETH:
+- err = pop_eth(skb, key);
+- break;
+-
+ case OVS_ACTION_ATTR_RECIRC:
+ err = execute_recirc(dp, skb, key, a, rem);
+ if (nla_is_last(a, rem)) {
+diff --git a/datapath/flow.c b/datapath/flow.c
+index 67b2f1d..e3f33f8 100644
+--- a/datapath/flow.c
++++ b/datapath/flow.c
+@@ -320,8 +320,12 @@ static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
+ }
+
+ static int parse_nsh(struct sk_buff *skb, struct sw_flow_key *key){
+- struct nsh_hdr *nsh_hdr = (struct nsh_hdr *)skb->data;
++ struct nsh_hdr *nsh_hdr = NULL;
++
++ OVS_CB(skb)->encap_eth_header = (struct encap_eth_hdr *)skb->data;
++ nsh_hdr = (struct nsh_hdr *)((const char *)skb->data + ENCAP_ETH_LEN);
+ OVS_CB(skb)->nsh_header = nsh_hdr;
++ memcpy(&key->nsh.encap_eth_dst, skb->data, ENCAP_ETH_LEN);
+ key->nsh.nsh_mdtype = nsh_hdr->base.mdtype;
+ if (key->nsh.nsh_mdtype != NSH_M_TYPE1)
+ return -EPERM;
+@@ -335,16 +339,6 @@ static int parse_nsh(struct sk_buff *skb, struct sw_flow_key *key){
+ return 0;
+ }
+
+-static void parse_encap_eth(struct sk_buff *skb, struct sw_flow_key *key){
+- struct encap_eth_hdr *encap_eth_header = (struct encap_eth_hdr *)skb->data;
+- OVS_CB(skb)->encap_eth_header = encap_eth_header;
+- key->encap_eth.encap_eth_type = encap_eth_header->encap_eth_type;
+- ether_addr_copy(key->encap_eth.encap_eth_src, encap_eth_header->encap_eth_src);
+- ether_addr_copy(key->encap_eth.encap_eth_dst, encap_eth_header->encap_eth_dst);
+-
+- return;
+-}
+-
+ static __be16 parse_ethertype(struct sk_buff *skb)
+ {
+ struct llc_snap_hdr {
+@@ -483,23 +477,21 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
+ {
+ int error;
+ struct ethhdr *eth;
++ int is_eth_nsh = 0;
+
+ /* Extract ethernet+nsh if ethernet type is 0x894F */
+ eth = (struct ethhdr *)skb->data;
+ if (eth->h_proto == htons(ETH_P_NSH)) {
+- parse_encap_eth(skb, key);
+- __skb_pull(skb, ENCAP_ETH_LEN);
+-
+ if (unlikely(parse_nsh(skb, key)))
+ return -EINVAL;
+ if (key->nsh.nsh_mdtype == NSH_M_TYPE1 && key->nsh.nsh_np == NSH_P_ETHERNET) {
+- __skb_pull(skb, NSH_M_TYPE1_LEN);
++ __skb_pull(skb, ENCAP_ETH_LEN + NSH_M_TYPE1_LEN);
++ is_eth_nsh = 1;
+ } else {
+ return -EINVAL;
+ }
+ } else {
+- void *encap_eth_hdr = &(key->encap_eth);
+- memset(encap_eth_hdr, 0, ENCAP_ETH_LEN);
++ memset(&key->nsh, 0, sizeof(key->nsh));
+ }
+
+ /* Flags are always used as part of stats */
+@@ -722,15 +714,15 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
+ }
+ }
+
+- if (key->encap_eth.encap_eth_type == htons(ETH_P_NSH))
++ if (is_eth_nsh == 1) {
+ __skb_push(skb, ENCAP_ETH_LEN + NSH_M_TYPE1_LEN);
++ }
+
+ return 0;
+ }
+
+-static void ovs_key_nsh_init(struct sw_flow_key *key, __u16 len) {
+- void *nsh_hdr = &(key->nsh);
+- memset(nsh_hdr, 0, len);
++static void ovs_key_nsh_init(struct sw_flow_key *key) {
++ memset(&key->nsh, 0, sizeof(key->nsh));
+ }
+
+ static int nsh_extract(struct sk_buff *skb, struct sw_flow_key *key) {
+@@ -740,16 +732,16 @@ static int nsh_extract(struct sk_buff *skb, struct sw_flow_key *key) {
+ if (unlikely(parse_nsh(skb, key)))
+ return -EINVAL;
+ if (key->nsh.nsh_mdtype == NSH_M_TYPE1) {
+- __skb_pull(skb, NSH_M_TYPE1_LEN);
++ __skb_pull(skb, ENCAP_ETH_LEN + NSH_M_TYPE1_LEN);
+ if(key->nsh.nsh_np == NSH_P_ETHERNET)
+ ret = key_extract(skb, key);
+ else
+ return -EINVAL;
+- __skb_push(skb, NSH_M_TYPE1_LEN);
++ __skb_push(skb, ENCAP_ETH_LEN + NSH_M_TYPE1_LEN);
+
+ return ret;
+ } else {
+- ovs_key_nsh_init(key, NSH_M_TYPE1_LEN);
++ ovs_key_nsh_init(key);
+ }
+
+ return 0;
+@@ -760,7 +752,7 @@ static bool is_nsh_header(const void *tun_opts, __be16 tun_flags) {
+ if (tun_opts && (tun_flags & TUNNEL_VXLAN_OPT))
+ md = (struct vxlan_metadata *)tun_opts;
+
+- if (md && (md->gpe & VXLAN_GPE_NP_MASK) == VXLAN_GPE_NP_NSH)
++ if (md && ((md->gpe & VXLAN_GPE_NP_MASK) == VXLAN_GPE_NP_NSH))
+ return true;
+ else
+ return false;
+@@ -768,7 +760,7 @@ static bool is_nsh_header(const void *tun_opts, __be16 tun_flags) {
+
+ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key)
+ {
+- ovs_key_nsh_init(key, NSH_M_TYPE1_LEN);
++ ovs_key_nsh_init(key);
+ return key_extract(skb, key);
+ }
+
+@@ -804,13 +796,13 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
+ key->recirc_id = 0;
+
+ /* Extract NSH and inner Ethernet if NSH header exists */
+- if (tun_info && is_nsh_header(ip_tunnel_info_opts(tun_info), key->tun_key.tun_flags)) {
++ if (tun_info && is_nsh_header(TUN_METADATA_OPTS(key, key->tun_opts_len), key->tun_key.tun_flags)) {
+ int ret ;
+ ret = nsh_extract(skb, key);
+ if (key->nsh.nsh_mdtype)
+ return ret;
+ } else {
+- ovs_key_nsh_init(key, NSH_M_TYPE1_LEN);
++ ovs_key_nsh_init(key);
+ }
+ return key_extract(skb, key);
+ }
+@@ -833,7 +825,7 @@ int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr,
+ if (key->nsh.nsh_mdtype)
+ return ret;
+ } else {
+- ovs_key_nsh_init(key, NSH_M_TYPE1_LEN);
++ ovs_key_nsh_init(key);
+ }
+
+ return key_extract(skb, key);
+diff --git a/datapath/flow.h b/datapath/flow.h
+index c8ccd5f..c00144a 100644
+--- a/datapath/flow.h
++++ b/datapath/flow.h
+@@ -63,21 +63,7 @@ struct sw_flow_key {
+ u32 skb_mark; /* SKB mark. */
+ u16 in_port; /* Input switch port (or DP_MAX_PORTS). */
+ } __packed phy; /* Safe when right after 'tun_key'. */
+- struct {
+- u32 nshc1; /* NSH context C1-C4 */
+- u32 nshc2;
+- u32 nshc3;
+- u32 nshc4;
+- u32 nsp; /* NSH path id */
+- u8 nsi; /* NSH index */
+- u8 nsh_mdtype; /* NSH metadata type */
+- u8 nsh_np; /* NSH next protocol */
+- }__packed nsh; /* network service header */
+- struct {
+- u8 encap_eth_src[ETH_ALEN]; /* ENCAP ethernet source address. */
+- u8 encap_eth_dst[ETH_ALEN]; /* ENCAP ethernet destination address. */
+- u16 encap_eth_type; /* ENCAP ethernet type. */
+- } encap_eth;
++ struct ovs_key_nsh nsh; /* network service header */
+ u32 ovs_flow_hash; /* Datapath computed hash value. */
+ u32 recirc_id; /* Recirculation ID. */
+ struct {
+diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
+index ebfae37..3e26ac5 100644
+--- a/datapath/flow_netlink.c
++++ b/datapath/flow_netlink.c
+@@ -284,7 +284,7 @@ size_t ovs_key_attr_size(void)
+ /* Whenever adding new OVS_KEY_ FIELDS, we should consider
+ * updating this function.
+ */
+- BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 28);
++ BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 27);
+
+ return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */
+ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */
+@@ -297,8 +297,7 @@ size_t ovs_key_attr_size(void)
+ + nla_total_size(2) /* OVS_KEY_ATTR_CT_ZONE */
+ + nla_total_size(4) /* OVS_KEY_ATTR_CT_MARK */
+ + nla_total_size(16) /* OVS_KEY_ATTR_CT_LABELS */
+- + nla_total_size(24) /* OVS_KEY_ATTR_NSH */
+- + nla_total_size(14) /* OVS_KEY_ATTR_ENCAP_ETH */
++ + nla_total_size(40) /* OVS_KEY_ATTR_NSH */
+ + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */
+ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
+ + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */
+@@ -337,7 +336,6 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
+ [OVS_KEY_ATTR_IN_PORT] = { .len = sizeof(u32) },
+ [OVS_KEY_ATTR_SKB_MARK] = { .len = sizeof(u32) },
+ [OVS_KEY_ATTR_NSH] = { .len = sizeof(struct ovs_key_nsh) },
+- [OVS_KEY_ATTR_ENCAP_ETH] = { .len = sizeof(struct ovs_key_encap_eth) },
+ [OVS_KEY_ATTR_ETHERNET] = { .len = sizeof(struct ovs_key_ethernet) },
+ [OVS_KEY_ATTR_VLAN] = { .len = sizeof(__be16) },
+ [OVS_KEY_ATTR_ETHERTYPE] = { .len = sizeof(__be16) },
+@@ -877,6 +875,12 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
+ const struct ovs_key_nsh *nsh_key;
+
+ nsh_key = nla_data(a[OVS_KEY_ATTR_NSH]);
++ SW_FLOW_KEY_MEMCPY(match, nsh.encap_eth_src,
++ nsh_key->encap_eth_src, ETH_ALEN, is_mask);
++ SW_FLOW_KEY_MEMCPY(match, nsh.encap_eth_dst,
++ nsh_key->encap_eth_dst, ETH_ALEN, is_mask);
++ SW_FLOW_KEY_PUT(match, nsh.encap_eth_type,
++ nsh_key->encap_eth_type, is_mask);
+ SW_FLOW_KEY_PUT(match, nsh.nshc1,
+ nsh_key->nshc1, is_mask);
+ SW_FLOW_KEY_PUT(match, nsh.nshc2,
+@@ -896,19 +900,6 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
+ attrs &= ~(1ULL << OVS_KEY_ATTR_NSH);
+ }
+
+- if (attrs & (1ULL << OVS_KEY_ATTR_ENCAP_ETH)) {
+- const struct ovs_key_encap_eth *encap_eth_key;
+-
+- encap_eth_key = nla_data(a[OVS_KEY_ATTR_ENCAP_ETH]);
+- SW_FLOW_KEY_MEMCPY(match, encap_eth.encap_eth_src,
+- encap_eth_key->encap_eth_src, ETH_ALEN, is_mask);
+- SW_FLOW_KEY_MEMCPY(match, encap_eth.encap_eth_dst,
+- encap_eth_key->encap_eth_dst, ETH_ALEN, is_mask);
+- SW_FLOW_KEY_PUT(match, encap_eth.encap_eth_type,
+- encap_eth_key->encap_eth_type, is_mask);
+- attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP_ETH);
+- }
+-
+ if (attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) {
+ const struct ovs_key_ethernet *eth_key;
+
+@@ -1433,6 +1424,9 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
+ if (!nla)
+ goto nla_put_failure;
+ nsh_key = nla_data(nla);
++ memcpy(nsh_key->encap_eth_dst, output->nsh.encap_eth_dst, ETH_ALEN);
++ memcpy(nsh_key->encap_eth_src, output->nsh.encap_eth_src, ETH_ALEN);
++ nsh_key->encap_eth_type = output->nsh.encap_eth_type;
+ nsh_key->nsi = output->nsh.nsi;
+ nsh_key->nsp = output->nsh.nsp;
+ nsh_key->nsh_mdtype= output->nsh.nsh_mdtype;
+@@ -1443,18 +1437,6 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
+ nsh_key->nshc4 = output->nsh.nshc4;
+ }
+
+- if ((swkey->encap_eth.encap_eth_type || is_mask)) {
+- struct ovs_key_encap_eth *encap_eth_key;
+-
+- nla = nla_reserve(skb, OVS_KEY_ATTR_ENCAP_ETH, sizeof(*encap_eth_key));
+- if (!nla)
+- goto nla_put_failure;
+- encap_eth_key = nla_data(nla);
+- memcpy(encap_eth_key->encap_eth_src, output->encap_eth.encap_eth_src, ETH_ALEN);
+- memcpy(encap_eth_key->encap_eth_dst, output->encap_eth.encap_eth_dst, ETH_ALEN);
+- encap_eth_key->encap_eth_type= output->encap_eth.encap_eth_type;
+- }
+-
+ if ((swkey->tun_key.u.ipv4.dst || is_mask)) {
+ const void *opts = NULL;
+
+@@ -2253,8 +2235,6 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
+ [OVS_ACTION_ATTR_POP_VLAN] = 0,
+ [OVS_ACTION_ATTR_PUSH_NSH] = sizeof(struct ovs_action_push_nsh),
+ [OVS_ACTION_ATTR_POP_NSH] = 0,
+- [OVS_ACTION_ATTR_PUSH_ETH] = sizeof(struct ovs_action_push_eth),
+- [OVS_ACTION_ATTR_POP_ETH] = 0,
+ [OVS_ACTION_ATTR_SET] = (u32)-1,
+ [OVS_ACTION_ATTR_SET_MASKED] = (u32)-1,
+ [OVS_ACTION_ATTR_SAMPLE] = (u32)-1,
+@@ -2301,8 +2281,6 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
+
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
+- case OVS_ACTION_ATTR_PUSH_ETH:
+- case OVS_ACTION_ATTR_POP_ETH:
+ break;
+
+ case OVS_ACTION_ATTR_POP_VLAN:
+diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
+index 187bb9b..bf1be4e 100644
+--- a/datapath/linux/compat/include/linux/openvswitch.h
++++ b/datapath/linux/compat/include/linux/openvswitch.h
+@@ -331,7 +331,6 @@ enum ovs_key_attr {
+ OVS_KEY_ATTR_IN_PORT, /* u32 OVS dp port number */
+ OVS_KEY_ATTR_NSH, /* struct ovs_key_nsh. Only support NSH MD
+ type 1 now */
+- OVS_KEY_ATTR_ENCAP_ETH, /* struct ovs_key_encap_eth */
+ OVS_KEY_ATTR_ETHERNET, /* struct ovs_key_ethernet */
+ OVS_KEY_ATTR_VLAN, /* be16 VLAN TCI */
+ OVS_KEY_ATTR_ETHERTYPE, /* be16 Ethernet type */
+@@ -406,6 +405,10 @@ enum ovs_frag_type {
+ #define OVS_FRAG_TYPE_MAX (__OVS_FRAG_TYPE_MAX - 1)
+
+ struct ovs_key_nsh {
++ __u8 encap_eth_dst[ETH_ALEN];
++ __u8 encap_eth_src[ETH_ALEN];
++ __u16 encap_eth_type;
++ __u16 pad1;
+ __u32 nshc1;
+ __u32 nshc2;
+ __u32 nshc3;
+@@ -414,14 +417,8 @@ struct ovs_key_nsh {
+ __u8 nsi;
+ __u8 nsh_mdtype;
+ __u8 nsh_np;
+- __u8 reserved;
+-};
+-
+-struct ovs_key_encap_eth {
+- __u8 encap_eth_src[ETH_ALEN];
+- __u8 encap_eth_dst[ETH_ALEN];
+- __u16 encap_eth_type;
+-};
++ __u8 pad2;
++} __packed;
+
+ struct ovs_key_ethernet {
+ __u8 eth_src[ETH_ALEN];
+@@ -652,6 +649,85 @@ struct ovs_action_push_vlan {
+ __be16 vlan_tci; /* 802.1Q TCI (VLAN ID and priority). */
+ };
+
++#ifndef __VXLAN_GPE_HEADER
++#define __VXLAN_GPE_HEADER 1
++
++/*
++ * VXLAN Generic Protocol Extension:
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * |R|R|Ver|I|P|R|O|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R| Next Proto |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | VXLAN Network Identifier (VNI) | Reserved |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * Ver = Version. Indicates VXLAN GPE protocol version. The initial
++ * version is 0. If a receiver does not support the version
++ * indicated it MUST drop the packet.
++ *
++ * I = Instance Bit. The I bit MUST be set to indicate a valid VNI.
++ *
++ * P = Next Protocol Bit. The P bit is set to indicate that the
++ * Next Protocol field is present.
++ *
++ * O = OAM Flag Bit. The O bit is set to indicate that the packet
++ * is an OAM packet.
++ *
++ * Next Protocol = This 8 bit field indicates the protocol header
++ * immediately following the VXLAN GPE header.
++ *
++ * [1] https://www.ietf.org/id/draft-ietf-nvo3-vxlan-gpe-01.txt
++ */
++
++struct vxlanhdr_gpe {
++#ifdef __LITTLE_ENDIAN_BITFIELD
++ uint8_t oam_flag:1;
++ uint8_t reserved_flags1:1;
++ uint8_t np_applied:1;
++ uint8_t instance_applied:1;
++ uint8_t gpe_version:2;
++ uint8_t reserved_flags2:2;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++ uint8_t reserved_flags2:2;
++ uint8_t gpe_version:2;
++ uint8_t instance_applied:1;
++ uint8_t np_applied:1;
++ uint8_t reserved_flags1:1;
++ uint8_t oam_flag:1;
++#else
++#error "Please fix "
++#endif
++ uint8_t reserved_flags3;
++ uint8_t reserved_flags4;
++ uint8_t next_proto;
++ __be32 vx_vni;
++};
++
++/* VxLAN-GPE Header Next Protocol */
++#define VXLAN_GPE_NP_IPV4 0x01
++#define VXLAN_GPE_NP_IPV6 0x02
++#define VXLAN_GPE_NP_ETHERNET 0x03
++#define VXLAN_GPE_NP_NSH 0x04
++
++/* skb->mark mapping
++ *
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * |R|R|Ver|I|P|R|O|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R| Next Proto |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ */
++
++#define VXLAN_GPE_OAM_FLAG (1UL << 24)
++#define VXLAN_GPE_NP_APPLIED (1UL << 26)
++#define VXLAN_GPE_INSTANCE_APPLIED (1UL << 27)
++#define VXLAN_GPE_VERSION ((1UL << 28) | (1UL << 29))
++
++#define VXLAN_GPE_NP_MASK (0xFF)
++#define VXLAN_GPE_USED_BITS (VXLAN_GPE_OAM_FLAG | VXLAN_GPE_NP_APPLIED \
++ | VXLAN_GPE_INSTANCE_APPLIED | VXLAN_GPE_VERSION | 0xFF)
++
++#define VXLAN_F_GPE 0x4000
++#define VXLAN_HF_GPE 0x04000000
++
++#endif /* __VXLAN_GPE_HEADER */
++
+ /*
+ * Network Service Header:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@@ -769,6 +845,7 @@ struct nsh_hdr {
+
+ #define ENCAP_ETH_LEN 14
+
++#define ETH_NSH_TYPE1_HEADER_SIZE (NSH_PUSH_TYPE1_HEADER_SIZE + ENCAP_ETH_LEN)
+ /**
+ * struct encap_eth - encap ethernet header for ethernet NSH
+ * @encap_eth_src: encap ethernet source address.
+@@ -781,6 +858,11 @@ struct encap_eth_hdr {
+ __u16 encap_eth_type;
+ };
+
++struct eth_nsh_hdr {
++ struct encap_eth_hdr encap_eth_header;
++ struct nsh_hdr nsh_hdr __attribute__((packed));
++};
++
+ #define ENCAP_ETH_PUSH_HEADER_SIZE 14
+ /**
+ * struct ovs_action_push_nsh - %OVS_ACTION_ATTR_PUSH_NSH action argument.
+@@ -791,15 +873,6 @@ struct ovs_action_push_nsh {
+ uint8_t header[NSH_PUSH_HEADER_SIZE];
+ };
+
+-/**
+- * struct ovs_action_push_nsh - %OVS_ACTION_ATTR_PUSH_NSH action argument.
+- * @header
+- */
+-struct ovs_action_push_eth {
+- uint16_t encap_eth_type;
+- uint8_t header[ENCAP_ETH_PUSH_HEADER_SIZE];
+-};
+-
+ /* Data path hash algorithm for computing Datapath hash.
+ *
+ * The algorithm type only specifies the fields in a flow
+@@ -839,6 +912,7 @@ struct ovs_action_push_tnl {
+ uint32_t out_port;
+ uint32_t header_len;
+ uint32_t tnl_type; /* For logging. */
++ uint32_t exts;
+ uint8_t header[TNL_PUSH_HEADER_SIZE];
+ };
+ #endif
+@@ -965,8 +1039,6 @@ enum ovs_action_attr {
+ OVS_ACTION_ATTR_POP_VLAN, /* No argument. */
+ OVS_ACTION_ATTR_PUSH_NSH, /* struct ovs_action_push_nsh. */
+ OVS_ACTION_ATTR_POP_NSH, /* No argument. */
+- OVS_ACTION_ATTR_PUSH_ETH, /* struct ovs_action_push_eth. */
+- OVS_ACTION_ATTR_POP_ETH, /* No argument. */
+ OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */
+ OVS_ACTION_ATTR_RECIRC, /* u32 recirc_id. */
+ OVS_ACTION_ATTR_HASH, /* struct ovs_action_hash. */
+diff --git a/datapath/linux/compat/include/net/vxlan.h b/datapath/linux/compat/include/net/vxlan.h
+index 3aeac88..31f9e5e 100644
+--- a/datapath/linux/compat/include/net/vxlan.h
++++ b/datapath/linux/compat/include/net/vxlan.h
+@@ -84,6 +84,8 @@ struct vxlanhdr_gbp {
+ #define VXLAN_GBP_POLICY_APPLIED (BIT(3) << 16)
+ #define VXLAN_GBP_ID_MASK (0xFFFF)
+
++#ifndef __VXLAN_GPE_HEADER
++#define __VXLAN_GPE_HEADER 1
+ /*
+ * VXLAN Generic Protocol Extension:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@@ -156,6 +158,11 @@ struct vxlanhdr_gpe {
+ #define VXLAN_GPE_USED_BITS (VXLAN_GPE_OAM_FLAG | VXLAN_GPE_NP_APPLIED \
+ | VXLAN_GPE_INSTANCE_APPLIED | VXLAN_GPE_VERSION | 0xFF)
+
++#define VXLAN_HF_GPE BIT(26)
++#define VXLAN_F_GPE 0x4000
++
++#endif /* __VXLAN_GPE_HEADER */
++
+ /* VXLAN protocol header:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |G|R|R|R|I|R|R|C| Reserved |
+@@ -176,7 +183,6 @@ struct vxlanhdr {
+ #define VXLAN_HF_RCO BIT(21)
+ #define VXLAN_HF_VNI BIT(27)
+ #define VXLAN_HF_GBP BIT(31)
+-#define VXLAN_HF_GPE BIT(26)
+
+ /* Remote checksum offload header option */
+ #define VXLAN_RCO_MASK 0x7f /* Last byte of vni field */
+@@ -279,7 +285,6 @@ struct vxlan_dev {
+ #define VXLAN_F_GBP 0x800
+ #define VXLAN_F_REMCSUM_NOPARTIAL 0x1000
+ #define VXLAN_F_COLLECT_METADATA 0x2000
+-#define VXLAN_F_GPE 0x4000
+
+ /* Flags that are used in the receive path. These flags must match in
+ * order for a socket to be shareable
+diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c
+index 7ef051c..888d431 100644
+--- a/datapath/linux/compat/vxlan.c
++++ b/datapath/linux/compat/vxlan.c
+@@ -821,6 +821,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
+ struct vxlan_dev *vxlan;
+ struct pcpu_sw_netstats *stats;
+ union vxlan_addr saddr;
++ struct eth_nsh_hdr *eth_nsh_header = NULL;
+ int err = 0;
+
+ /* For flow based devices, map all packets to VNI 0 */
+@@ -888,6 +889,15 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+ u64_stats_update_end(&stats->syncp);
++
++ /* Add a faked encap_eth_header for NSH */
++ if (md && ((md->gpe & VXLAN_GPE_NP_MASK) == VXLAN_GPE_NP_NSH)) {
++ skb_push(skb, ENCAP_ETH_LEN);
++ eth_nsh_header = (struct eth_nsh_hdr *)skb->data;
++ memmove(eth_nsh_header->encap_eth_header.encap_eth_dst, skb_mac_header(skb), ENCAP_ETH_LEN);
++ eth_nsh_header->encap_eth_header.encap_eth_type = htons(ETH_P_NSH);
++ }
++
+ netdev_port_receive(skb, skb_tunnel_info(skb));
+ return;
+ drop:
+@@ -1178,6 +1188,11 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *sk
+ }
+ }
+
++ /* Skip encap_eth_header on sending Eth+NSH to vxlan-gpe port */
++ if (md && ((md->gpe & VXLAN_GPE_NP_MASK) == VXLAN_GPE_NP_NSH)) {
++ skb_pull(skb, ENCAP_ETH_LEN);
++ }
++
+ min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
+ + VXLAN_HLEN + sizeof(struct iphdr)
+ + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0);
+diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
+index 8e67bfd..d40cae4 100644
+--- a/lib/dpif-netdev.c
++++ b/lib/dpif-netdev.c
+@@ -467,7 +467,7 @@ static int get_port_by_name(struct dp_netdev *dp, const char *devname,
+ static void dp_netdev_free(struct dp_netdev *)
+ OVS_REQUIRES(dp_netdev_mutex);
+ static int do_add_port(struct dp_netdev *dp, const char *devname,
+- const char *type, odp_port_t port_no)
++ const char *type, odp_port_t port_no, uint32_t exts)
+ OVS_REQUIRES(dp->port_mutex);
+ static void do_del_port(struct dp_netdev *dp, struct dp_netdev_port *)
+ OVS_REQUIRES(dp->port_mutex);
+@@ -914,7 +914,7 @@ create_dp_netdev(const char *name, const struct dpif_class *class,
+ dp_netdev_set_nonpmd(dp);
+
+ ovs_mutex_lock(&dp->port_mutex);
+- error = do_add_port(dp, name, "internal", ODPP_LOCAL);
++ error = do_add_port(dp, name, "internal", ODPP_LOCAL, 0);
+ ovs_mutex_unlock(&dp->port_mutex);
+ if (error) {
+ dp_netdev_free(dp);
+@@ -1096,9 +1096,21 @@ hash_port_no(odp_port_t port_no)
+ return hash_int(odp_to_u32(port_no), 0);
+ }
+
++static void add_vxlan_gpe_exts(struct netdev *netdev, uint32_t exts)
++{
++ const char *type = netdev_get_type(netdev);
++ if (!strcmp(type, "vxlan")) {
++ struct netdev_tunnel_config *cfg;
++ cfg = netdev_get_tunnel_config(netdev);
++
++ if(exts & (1 << OVS_VXLAN_EXT_GPE))
++ cfg->exts |= (1 << OVS_VXLAN_EXT_GPE);
++ }
++}
++
+ static int
+ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
+- odp_port_t port_no)
++ odp_port_t port_no, uint32_t exts)
+ OVS_REQUIRES(dp->port_mutex)
+ {
+ struct netdev_saved_flags *sf;
+@@ -1145,7 +1157,8 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
+ VLOG_ERR("%s, cannot set multiq", devname);
+ return errno;
+ }
+- }
++ }
++ add_vxlan_gpe_exts(netdev, exts);
+ port = xzalloc(sizeof *port);
+ port->port_no = port_no;
+ port->netdev = netdev;
+@@ -1211,7 +1224,12 @@ dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
+ }
+ if (!error) {
+ *port_nop = port_no;
+- error = do_add_port(dp, dpif_port, netdev_get_type(netdev), port_no);
++ const struct netdev_tunnel_config *cfg;
++ uint32_t exts = 0;
++ cfg = netdev_get_tunnel_config(netdev);
++ if (cfg)
++ exts = cfg->exts;
++ error = do_add_port(dp, dpif_port, netdev_get_type(netdev), port_no, exts);
+ }
+ ovs_mutex_unlock(&dp->port_mutex);
+
+@@ -3876,8 +3894,6 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
+
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
+- case OVS_ACTION_ATTR_PUSH_ETH:
+- case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_PUSH_MPLS:
+diff --git a/lib/dpif.c b/lib/dpif.c
+index b5265c4..481f0f9 100644
+--- a/lib/dpif.c
++++ b/lib/dpif.c
+@@ -1141,8 +1141,6 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt,
+ case OVS_ACTION_ATTR_HASH:
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
+- case OVS_ACTION_ATTR_PUSH_ETH:
+- case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_PUSH_MPLS:
+diff --git a/lib/flow.c b/lib/flow.c
+index fc8b98c..887b023 100644
+--- a/lib/flow.c
++++ b/lib/flow.c
+@@ -470,6 +470,25 @@ flow_extract(struct dp_packet *packet, struct flow *flow)
+ miniflow_expand(&m.mf, flow);
+ }
+
++/* parse Ethernet+NSH */
++static int parse_nsh(const void *data, struct ovs_key_nsh *nsh)
++{
++ memcpy(&nsh->encap_eth_dst, data, ENCAP_ETH_LEN);
++ const struct nsh_hdr *nsh_hdr = (const struct nsh_hdr *)((const char *)data + ENCAP_ETH_LEN);
++
++ nsh->nsh_mdtype = nsh_hdr->base.mdtype;
++ if (nsh->nsh_mdtype != NSH_M_TYPE1)
++ return -1;
++ nsh->nsh_np = nsh_hdr->base.proto;
++ nsh->nsi = nsh_hdr->base.svc_idx;
++ nsh->nsp = nsh_hdr->base.path_hdr << 8;
++ nsh->nshc1 = nsh_hdr->ctx.nshc1;
++ nsh->nshc2 = nsh_hdr->ctx.nshc2;
++ nsh->nshc3 = nsh_hdr->ctx.nshc3;
++ nsh->nshc4 = nsh_hdr->ctx.nshc4;
++ return 0;
++}
++
+ /* Caller is responsible for initializing 'dst' with enough storage for
+ * FLOW_U64S * 8 bytes. */
+ void
+@@ -530,6 +549,32 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
+ }
+ }
+
++ /* Extract Etherenet + NSH if the Ethernet type is 0x894F in packet */
++ if (OVS_UNLIKELY(size < sizeof(struct eth_header)))
++ goto out;
++ else {
++ const struct eth_header *eth = data;
++ if (eth->eth_type == htons(ETH_P_NSH)) {
++ /* extract Ethernet+nsh */
++ struct ovs_key_nsh nsh;
++ if (OVS_UNLIKELY(parse_nsh(data, &nsh)))
++ goto out;
++
++ /* Now only support NSH mdtype 1 */
++ if (nsh.nsh_mdtype == NSH_M_TYPE1){
++ /* Push all field related with Ethernet+nsh at once. */
++ miniflow_push_words(mf, encap_eth_dst, &nsh,
++ (sizeof (struct ovs_key_nsh)+sizeof(uint64_t) - 1) /
++ sizeof(uint64_t));
++ data = (const char *)data + NSH_M_TYPE1_LEN + ENCAP_ETH_LEN;
++ } else
++ goto out;
++
++ if(nsh.nsh_np != NSH_P_ETHERNET)
++ goto out;
++ }
++ }
++
+ /* Initialize packet's layer pointer and offsets. */
+ l2 = data;
+ dp_packet_reset_offsets(packet);
+@@ -1291,6 +1336,9 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
+
+ /* NSH fields wildcarded */
+ if (flow->nsh_mdtype) {
++ WC_MASK_FIELD(wc, encap_eth_src);
++ WC_MASK_FIELD(wc, encap_eth_dst);
++ WC_MASK_FIELD(wc, encap_eth_type);
+ WC_MASK_FIELD(wc, nshc1);
+ WC_MASK_FIELD(wc, nshc2);
+ WC_MASK_FIELD(wc, nshc3);
+@@ -1301,13 +1349,6 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
+ WC_MASK_FIELD(wc, nsi);
+ }
+
+- /* ENCAP Eth wildcarded */
+- if (flow->encap_eth_type) {
+- WC_MASK_FIELD(wc, encap_eth_src);
+- WC_MASK_FIELD(wc, encap_eth_dst);
+- WC_MASK_FIELD(wc, encap_eth_type);
+- }
+-
+ /* metadata, regs, and conj_id wildcarded. */
+
+ WC_MASK_FIELD(wc, skb_priority);
+@@ -1424,6 +1465,19 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
+ FLOWMAP_SET(map, ct_mark);
+ FLOWMAP_SET(map, ct_label);
+
++ /* NSH fields */
++ FLOWMAP_SET(map, encap_eth_src);
++ FLOWMAP_SET(map, encap_eth_dst);
++ FLOWMAP_SET(map, encap_eth_type);
++ FLOWMAP_SET(map, nshc1);
++ FLOWMAP_SET(map, nshc2);
++ FLOWMAP_SET(map, nshc3);
++ FLOWMAP_SET(map, nshc4);
++ FLOWMAP_SET(map, nsp);
++ FLOWMAP_SET(map, nsi);
++ FLOWMAP_SET(map, nsh_mdtype);
++ FLOWMAP_SET(map, nsh_np);
++
+ /* Ethertype-dependent fields. */
+ if (OVS_LIKELY(flow->dl_type == htons(ETH_TYPE_IP))) {
+ FLOWMAP_SET(map, nw_src);
+@@ -2271,6 +2325,33 @@ flow_compose_l4(struct dp_packet *p, const struct flow *flow)
+ return l4_len;
+ }
+
++/* push ethernet+nsh header at the beginning of packets */
++static void
++flow_compose_nsh(struct dp_packet *p, const struct flow *flow)
++{
++ struct eth_nsh_hdr *eth_nsh_header;
++
++ /* Now only support mdtype1 */
++ if (flow->nsh_mdtype != NSH_M_TYPE1)
++ return;
++
++ eth_nsh_header = dp_packet_push_uninit(p, ETH_NSH_TYPE1_HEADER_SIZE);
++ eth_nsh_header->encap_eth_header.encap_eth_dst = flow->encap_eth_dst;
++ eth_nsh_header->encap_eth_header.encap_eth_src = flow->encap_eth_src;
++ eth_nsh_header->encap_eth_header.encap_eth_type = htons(ETH_P_NSH);
++
++ memset(ð_nsh_header->nsh_hdr, 0, sizeof (struct nsh_hdr));
++ eth_nsh_header->nsh_hdr.base.length = 6;
++ eth_nsh_header->nsh_hdr.base.proto = flow->nsh_np;
++ eth_nsh_header->nsh_hdr.base.mdtype = flow->nsh_mdtype;
++ eth_nsh_header->nsh_hdr.base.proto = flow->nsh_np;
++ eth_nsh_header->nsh_hdr.base.path_hdr = flow->nsp >> 8 | flow->nsi << 24;
++ eth_nsh_header->nsh_hdr.ctx.nshc1 = flow->nshc1;
++ eth_nsh_header->nsh_hdr.ctx.nshc2 = flow->nshc2;
++ eth_nsh_header->nsh_hdr.ctx.nshc3 = flow->nshc3;
++ eth_nsh_header->nsh_hdr.ctx.nshc4 = flow->nshc4;
++}
++
+ /* Puts into 'b' a packet that flow_extract() would parse as having the given
+ * 'flow'.
+ *
+@@ -2371,6 +2452,10 @@ flow_compose(struct dp_packet *p, const struct flow *flow)
+ push_mpls(p, flow->dl_type, flow->mpls_lse[--n]);
+ }
+ }
++
++ if (flow->nsh_mdtype) {
++ flow_compose_nsh(p, flow);
++ }
+ }
+
+ /* Compressed flow. */
+diff --git a/lib/flow.h b/lib/flow.h
+index a8677b1..31a2a16 100644
+--- a/lib/flow.h
++++ b/lib/flow.h
+@@ -111,7 +111,11 @@ struct flow {
+ ofp_port_t actset_output; /* Output port in action set. */
+ uint8_t pad2[2]; /* Pad to 64 bits. */
+
+- /* NSH (64-bit aligned) */
++ /* ETH + NSH (64-bit aligned) */
++ struct eth_addr encap_eth_dst; /* Encap ethernet destination address. */
++ struct eth_addr encap_eth_src; /* Encap ethernet source address. */
++ ovs_be16 encap_eth_type; /* Encap ethernet frame type. */
++ uint8_t pad3[2]; /* Pad to 64 bits. */
+ ovs_be32 nshc1;
+ ovs_be32 nshc2;
+ ovs_be32 nshc3;
+@@ -120,13 +124,7 @@ struct flow {
+ uint8_t nsi;
+ uint8_t nsh_mdtype;
+ uint8_t nsh_np;
+- uint8_t pad3;
+-
+- /* ENCAP_ETH (64-bit aligned) */
+- struct eth_addr encap_eth_src; /* Encap ethernet source address. */
+- struct eth_addr encap_eth_dst; /* Encap ethernet destination address. */
+- ovs_be16 encap_eth_type; /* Encap ethernet frame type. */
+- uint8_t pad4[2];
++ uint8_t pad4;
+
+ /* L2, Order the same as in the Ethernet header! (64-bit aligned) */
+ struct eth_addr dl_dst; /* Ethernet destination address. */
+diff --git a/lib/match.c b/lib/match.c
+index 3db2a2b..2b6648d 100644
+--- a/lib/match.c
++++ b/lib/match.c
+@@ -1002,12 +1002,12 @@ match_set_encap_eth_dst(struct match *match, const struct eth_addr encap_eth_dst
+ }
+
+ void
+-match_set_encap_eth_type(struct match *match, uint16_t encap_eth_type)
++match_set_encap_eth_type(struct match *match, ovs_be16 encap_eth_type)
+ {
+- match_set_encap_eth_type_masked(match, encap_eth_type, UINT16_MAX);
++ match->wc.masks.encap_eth_type = OVS_BE16_MAX;
++ match->flow.encap_eth_type = encap_eth_type;
+ }
+
+-
+ /* Returns true if 'a' and 'b' wildcard the same fields and have the same
+ * values for fixed fields, otherwise false. */
+ bool
+@@ -1338,12 +1338,11 @@ match_format(const struct match *match, struct ds *s, int priority)
+ wc->masks.nshc4);
+ }
+
+- if (wc->masks.encap_eth_type) {
+- ds_put_format(s, "encap_eth_type=%"PRIu16",", f->encap_eth_type);
+- }
+-
+ format_eth_masked(s, "encap_eth_src", f->encap_eth_src, wc->masks.encap_eth_src);
+ format_eth_masked(s, "encap_eth_dst", f->encap_eth_dst, wc->masks.encap_eth_dst);
++ if (wc->masks.encap_eth_type) {
++ ds_put_format(s, "encap_eth_type=0x%04"PRIx16",", ntohs(f->encap_eth_type));
++ }
+
+ if (wc->masks.dl_type) {
+ skip_type = true;
+diff --git a/lib/match.h b/lib/match.h
+index 529350e..bf27d38 100644
+--- a/lib/match.h
++++ b/lib/match.h
+@@ -187,7 +187,7 @@ void match_set_nshc3(struct match *, ovs_be32 nshc3);
+ void match_set_nshc4(struct match *, ovs_be32 nshc4);
+ void match_set_encap_eth_src(struct match *match, const struct eth_addr encap_eth_src);
+ void match_set_encap_eth_dst(struct match *match, const struct eth_addr encap_eth_dst);
+-void match_set_encap_eth_type(struct match *match, uint16_t encap_eth_type);
++void match_set_encap_eth_type(struct match *match, ovs_be16 encap_eth_type);
+
+ 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.h b/lib/meta-flow.h
+index a226b79..4c49adb 100644
+--- a/lib/meta-flow.h
++++ b/lib/meta-flow.h
+@@ -1915,7 +1915,7 @@ enum OVS_PACKED_ENUM mf_field_id {
+ * Maskable: no.
+ * Formatting: hexadecimal.
+ * Prerequisites: none.
+- * Access: read/write.
++ * Access: read-only.
+ * NXM: NXM_NX_ENCAP_ETH_TYPE(123) since v1.1.
+ * OXM: none.
+ */
+diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
+index 406a492..67a020a 100644
+--- a/lib/netdev-vport.c
++++ b/lib/netdev-vport.c
+@@ -584,12 +584,12 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
+ if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) {
+ if (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GPE))
+ VLOG_WARN("VXLAN_GPE extension exists, VxLAN_GBP extension can't be added.");
+- else
++ else
+ tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP);
+ } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) {
+ if (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GBP))
+ VLOG_WARN("VXLAN_GBP extension exists, VxLAN_GPE extension can't be added.");
+- else
++ else
+ tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE);
+ } else {
+ VLOG_WARN("%s: unknown extension '%s'", name, ext);
+@@ -1048,6 +1048,9 @@ push_udp_header(struct dp_packet *packet,
+ struct udp_header *udp;
+ int ip_tot_size;
+
++ if (data->tnl_type == OVS_VPORT_TYPE_VXLAN && data->exts & 1 << VXLAN_GPE_POP_ETH)
++ dp_packet_reset_packet(packet, ENCAP_ETH_LEN);
++
+ udp = push_ip_header(packet, data->header, data->header_len, &ip_tot_size);
+
+ /* set udp src port */
+@@ -1294,6 +1297,7 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
+ struct flow_tnl *tnl = &md->tunnel;
+ struct vxlanhdr *vxh;
+ unsigned int hlen;
++ ovs_be32 flag;
+
+ pkt_metadata_init_tnl(md);
+ if (VXLAN_HLEN > dp_packet_l4_size(packet)) {
+@@ -1305,17 +1309,42 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
+ return EINVAL;
+ }
+
+- if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
+- (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
+- VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
+- ntohl(get_16aligned_be32(&vxh->vx_flags)),
+- ntohl(get_16aligned_be32(&vxh->vx_vni)));
+- return EINVAL;
+- }
+- tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
+- tnl->flags |= FLOW_TNL_F_KEY;
+
+- dp_packet_reset_packet(packet, hlen + VXLAN_HLEN);
++ /* vxlan-gpe packets*/
++ flag = get_16aligned_be32(&vxh->vx_flags);
++
++ if (flag & VXLAN_HF_GPE) {
++ flag &= ~VXLAN_GPE_USED_BITS;
++ if ((flag & ~VXLAN_GPE_USED_BITS) ||
++ get_16aligned_be32(&vxh->vx_vni) & htonl(0xff)) {
++
++ VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n for vxlan-gpe",
++ ntohl(get_16aligned_be32(&vxh->vx_flags)),
++ ntohl(get_16aligned_be32(&vxh->vx_vni)));
++ return EINVAL;
++ }
++
++ struct vxlanhdr_gpe *gpe;
++
++ gpe = (struct vxlanhdr_gpe *)vxh;
++ tnl->gpe_np = gpe->next_proto;
++
++ /* Drop the OAM packets */
++ if (gpe->oam_flag)
++ return EINVAL;
++ } else {
++ if (flag != htonl(VXLAN_FLAGS) ||
++ (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
++ VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
++ ntohl(get_16aligned_be32(&vxh->vx_flags)),
++ ntohl(get_16aligned_be32(&vxh->vx_vni)));
++ return EINVAL;
++ }
++ tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
++ tnl->flags |= FLOW_TNL_F_KEY;
++
++ dp_packet_reset_packet(packet, hlen + VXLAN_HLEN);
++ }
+
+ return 0;
+ }
+@@ -1336,8 +1365,27 @@ netdev_vxlan_build_header(const struct netdev *netdev,
+
+ vxh = udp_build_header(tnl_cfg, tnl_flow, data, &hlen);
+
+- put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
+- put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
++ if(tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
++ struct vxlanhdr_gpe *gpe;
++
++ gpe = (struct vxlanhdr_gpe *)vxh;
++ put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS | VXLAN_HF_GPE));
++ put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
++
++ if (!tnl_flow->tunnel.gpe_np)
++ return -1;
++ else
++ gpe->next_proto = tnl_flow->tunnel.gpe_np;
++
++ if (tnl_flow->tunnel.gpe_flags & 0x01)
++ gpe->oam_flag = 1;
++
++ if (tnl_flow->tunnel.gpe_np == VXLAN_GPE_NP_NSH)
++ data->exts |= 1 << VXLAN_GPE_POP_ETH;
++ } else {
++ put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
++ put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
++ }
+
+ ovs_mutex_unlock(&dev->mutex);
+ data->header_len = hlen + VXLAN_HLEN;
+diff --git a/lib/netdev-vport.h b/lib/netdev-vport.h
+index be02cb5..a6975de 100644
+--- a/lib/netdev-vport.h
++++ b/lib/netdev-vport.h
+@@ -48,6 +48,12 @@ enum { NETDEV_VPORT_NAME_BUFSIZE = 16 };
+ #else
+ enum { NETDEV_VPORT_NAME_BUFSIZE = 256 };
+ #endif
++
++enum OVS_ACTION_PUSH_VXLAN_EXTS {
++ VXLAN_GPE_POP_ETH, /* Pop ethernet header when the field next_proto
++ * is 4 in vxlan-gpe header
++ */
++};
+ const char *netdev_vport_get_dpif_port(const struct netdev *,
+ char namebuf[], size_t bufsize)
+ OVS_WARN_UNUSED_RESULT;
+diff --git a/lib/nx-match.c b/lib/nx-match.c
+index 8d2bc4b..c17057f 100644
+--- a/lib/nx-match.c
++++ b/lib/nx-match.c
+@@ -959,14 +959,13 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
+ nxm_put_32m(b, MFF_NSH_C3, oxm, flow->nshc3, match->wc.masks.nshc3);
+ nxm_put_32m(b, MFF_NSH_C4, oxm, flow->nshc4, match->wc.masks.nshc4);
+
+- /* ENCAP Eth */
+ nxm_put_eth_masked(b, MFF_ENCAP_ETH_SRC, oxm,
+ flow->encap_eth_src, match->wc.masks.encap_eth_src);
+ nxm_put_eth_masked(b, MFF_ENCAP_ETH_DST, oxm,
+ flow->encap_eth_dst, match->wc.masks.encap_eth_dst);
+ nxm_put_16m(b, MFF_ENCAP_ETH_TYPE, oxm,
+ ofputil_dl_type_to_openflow(flow->encap_eth_type),
+- match->wc.masks.encap_eth_type); //uncertain
++ match->wc.masks.encap_eth_type);
+
+ /* Ethernet. */
+ nxm_put_eth_masked(b, MFF_ETH_SRC, oxm,
+diff --git a/lib/odp-execute.c b/lib/odp-execute.c
+index b6dcd98..342bbaf 100644
+--- a/lib/odp-execute.c
++++ b/lib/odp-execute.c
+@@ -338,6 +338,7 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
+ case OVS_KEY_ATTR_CT_ZONE:
+ case OVS_KEY_ATTR_CT_MARK:
+ case OVS_KEY_ATTR_CT_LABELS:
++ case OVS_KEY_ATTR_NSH:
+ case __OVS_KEY_ATTR_MAX:
+ default:
+ OVS_NOT_REACHED();
+@@ -435,7 +436,6 @@ odp_execute_masked_set_action(struct dp_packet *packet,
+ case OVS_KEY_ATTR_IN_PORT:
+ case OVS_KEY_ATTR_VLAN:
+ case OVS_KEY_ATTR_NSH:
+- case OVS_KEY_ATTR_ENCAP_ETH:
+ case OVS_KEY_ATTR_ICMP:
+ case OVS_KEY_ATTR_ICMPV6:
+ case OVS_KEY_ATTR_TCP_FLAGS:
+@@ -501,8 +501,6 @@ requires_datapath_assistance(const struct nlattr *a)
+ case OVS_ACTION_ATTR_SET_MASKED:
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
+- case OVS_ACTION_ATTR_PUSH_ETH:
+- case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_SAMPLE:
+@@ -589,6 +587,21 @@ odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal,
+ }
+ break;
+
++ case OVS_ACTION_ATTR_PUSH_NSH: {
++ const void *push_nsh_hdr = nl_attr_get(a);
++
++ for (i = 0; i < cnt; i++) {
++ push_nsh(packets[i], push_nsh_hdr);
++ }
++ break;
++ }
++
++ case OVS_ACTION_ATTR_POP_NSH:
++ for (i = 0; i < cnt; i++) {
++ pop_nsh(packets[i]);
++ }
++ break;
++
+ case OVS_ACTION_ATTR_PUSH_MPLS: {
+ const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
+
+@@ -629,10 +642,6 @@ odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal,
+ }
+ break;
+
+- case OVS_ACTION_ATTR_PUSH_NSH:
+- case OVS_ACTION_ATTR_POP_NSH:
+- case OVS_ACTION_ATTR_PUSH_ETH:
+- case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_OUTPUT:
+ case OVS_ACTION_ATTR_TUNNEL_PUSH:
+ case OVS_ACTION_ATTR_TUNNEL_POP:
+diff --git a/lib/odp-util.c b/lib/odp-util.c
+index 102dfd7..7721cc4 100644
+--- a/lib/odp-util.c
++++ b/lib/odp-util.c
+@@ -115,8 +115,6 @@ odp_action_len(uint16_t type)
+ case OVS_ACTION_ATTR_POP_VLAN: return 0;
+ case OVS_ACTION_ATTR_PUSH_NSH: return sizeof(struct ovs_action_push_nsh);
+ case OVS_ACTION_ATTR_POP_NSH: return 0;
+- case OVS_ACTION_ATTR_PUSH_ETH: return sizeof(struct ovs_action_push_eth);
+- case OVS_ACTION_ATTR_POP_ETH: return 0;
+ case OVS_ACTION_ATTR_PUSH_MPLS: return sizeof(struct ovs_action_push_mpls);
+ case OVS_ACTION_ATTR_POP_MPLS: return sizeof(ovs_be16);
+ case OVS_ACTION_ATTR_RECIRC: return sizeof(uint32_t);
+@@ -152,7 +150,6 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
+ case OVS_KEY_ATTR_CT_LABELS: return "ct_label";
+ case OVS_KEY_ATTR_TUNNEL: return "tunnel";
+ case OVS_KEY_ATTR_IN_PORT: return "in_port";
+- case OVS_KEY_ATTR_ENCAP_ETH: return "encap_eth";
+ case OVS_KEY_ATTR_NSH: return "nsh";
+ case OVS_KEY_ATTR_ETHERNET: return "eth";
+ case OVS_KEY_ATTR_VLAN: return "vlan";
+@@ -382,31 +379,26 @@ format_vlan_tci(struct ds *ds, ovs_be16 tci, ovs_be16 mask, bool verbose)
+ static void
+ format_nsh(struct ds *ds, const struct ovs_action_push_nsh * nsh)
+ {
+- const struct nsh_hdr *nsh_hdr =(struct nsh_hdr *)nsh->header;
++ const struct eth_nsh_hdr *eth_nsh_header = (struct eth_nsh_hdr *)nsh->header;
++ format_eth(ds, "encap_eth_src",
++ eth_nsh_header->encap_eth_header.encap_eth_src,
++ NULL, true);
++ format_eth(ds, "encap_eth_dst",
++ eth_nsh_header->encap_eth_header.encap_eth_dst,
++ NULL, true);
++ ds_put_format(ds, "encap_eth_type=0x%04"PRIx16",",
++ ntohs(eth_nsh_header->encap_eth_header.encap_eth_type));
+ ds_put_format(ds, "nsh_mdtype=%"PRIu8",nsh_np=%"PRIu8",nsp=%"PRIu32
+ ",nsi=%"PRIu8",nshc1=%"PRIu32",nshc2=%"PRIu32
+ ",nshc3=%"PRIu32",nshc4=%"PRIu32")",
+- nsh_hdr->base.mdtype,
+- nsh_hdr->base.proto,
+- ntohl(nsh_hdr->base.path_hdr << 8),
+- ntohl(nsh_hdr->base.path_hdr >> 24),
+- ntohl(nsh_hdr->ctx.nshc1),
+- ntohl(nsh_hdr->ctx.nshc2),
+- ntohl(nsh_hdr->ctx.nshc3),
+- ntohl(nsh_hdr->ctx.nshc4));
+-}
+-
+-static void
+-format_encap_eth(struct ds *ds, const struct ovs_action_push_eth *encap_eth)
+-{
+- const struct encap_eth_hdr *encap_eth_hdr = (struct encap_eth_hdr *)encap_eth->header;
+- ds_put_format(ds, "encap_eth_type=%"PRIu16",",
+- ntohs(encap_eth_hdr->encap_eth_type));
+- format_eth(ds, "encap_eth_src", encap_eth_hdr->encap_eth_src,
+- NULL, true);
+- format_eth(ds, "encap_eth_dst", encap_eth_hdr->encap_eth_dst,
+- NULL, true);
+- ds_put_format(ds, ")");
++ eth_nsh_header->nsh_hdr.base.mdtype,
++ eth_nsh_header->nsh_hdr.base.proto,
++ ntohl(eth_nsh_header->nsh_hdr.base.path_hdr << 8),
++ ntohl(eth_nsh_header->nsh_hdr.base.path_hdr >> 24),
++ ntohl(eth_nsh_header->nsh_hdr.ctx.nshc1),
++ ntohl(eth_nsh_header->nsh_hdr.ctx.nshc2),
++ ntohl(eth_nsh_header->nsh_hdr.ctx.nshc3),
++ ntohl(eth_nsh_header->nsh_hdr.ctx.nshc4));
+ }
+
+ static void
+@@ -798,7 +790,6 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
+ int expected_len;
+ enum ovs_action_attr type = nl_attr_type(a);
+ const struct ovs_action_push_nsh *nsh;
+- const struct ovs_action_push_eth *encap_eth;
+ size_t size;
+
+ expected_len = odp_action_len(nl_attr_type(a));
+@@ -877,15 +868,6 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
+ case OVS_ACTION_ATTR_POP_NSH:
+ ds_put_cstr(ds, "pop_nsh");
+ break;
+-
+- case OVS_ACTION_ATTR_PUSH_ETH:
+- encap_eth = nl_attr_get(a);
+- ds_put_cstr(ds, "push_eth(");
+- format_encap_eth(ds, encap_eth);
+- break;
+- case OVS_ACTION_ATTR_POP_ETH:
+- ds_put_cstr(ds, "pop_eth");
+- break;
+ case OVS_ACTION_ATTR_PUSH_MPLS: {
+ const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
+ ds_put_cstr(ds, "push_mpls(");
+@@ -1675,29 +1657,37 @@ parse_odp_action(const char *s, const struct simap *port_names,
+
+ {
+ struct ovs_action_push_nsh push;
+- struct nsh_hdr *nsh = (struct nsh_hdr *)push.header;
++ struct eth_nsh_hdr *eth_nsh_header = (struct eth_nsh_hdr *)push.header;
+ ovs_be32 nsp, nshc1,nshc2,nshc3,nshc4;
+ uint8_t nsi, nsh_mdtype, nsh_np;
+ int n = -1;
+
+- if (ovs_scan_len(s, &n, "push_nsh(nsh_mdtype=%"SCNi8",nsh_np=%"SCNi8",nsp=0x%"SCNx32
++ if (ovs_scan_len(s, &n, "push_nsh(encap_eth_dst="ETH_ADDR_SCAN_FMT
++ ",encap_eth_src="ETH_ADDR_SCAN_FMT
++ ",encap_eth_type=0x%"SCNx16
++ ",nsh_mdtype=%"SCNi8",nsh_np=%"SCNi8",nsp=0x%"SCNx32
+ ",nsi=%"SCNi8",nshc1=0x%"SCNx32",nshc2=0x%"SCNx32
+ ",nshc3=0x%"SCNx32",nshc4=0x%"SCNx32"))",
++ ETH_ADDR_SCAN_ARGS(eth_nsh_header->encap_eth_header.encap_eth_dst),
++ ETH_ADDR_SCAN_ARGS(eth_nsh_header->encap_eth_header.encap_eth_src),
++ ð_nsh_header->encap_eth_header.encap_eth_type,
+ &nsh_mdtype, &nsh_np,
+ &nsp, &nsi,
+ &nshc1, &nshc2,
+ &nshc3, &nshc4)) {
++ eth_nsh_header->encap_eth_header.encap_eth_type = htons(eth_nsh_header->encap_eth_header.encap_eth_type);
+ if (nsh_mdtype == NSH_M_TYPE1) {
+- nsh->base.mdtype = NSH_M_TYPE1;
+- nsh->base.version = 0x01;
+- nsh->base.length = 6;
+- nsh->base.proto = nsh_np;
+- nsh->base.path_hdr= nsp;
+- nsh->base.svc_idx = nsi;
+- nsh->ctx.nshc1=nshc1;
+- nsh->ctx.nshc2=nshc2;
+- nsh->ctx.nshc3=nshc3;
+- nsh->ctx.nshc4=nshc4;
++ eth_nsh_header->encap_eth_header.encap_eth_type = htons(ETH_P_NSH);
++ eth_nsh_header->nsh_hdr.base.mdtype = NSH_M_TYPE1;
++ eth_nsh_header->nsh_hdr.base.version = 0x01;
++ eth_nsh_header->nsh_hdr.base.length = 6;
++ eth_nsh_header->nsh_hdr.base.proto = nsh_np;
++ eth_nsh_header->nsh_hdr.base.path_hdr= nsp;
++ eth_nsh_header->nsh_hdr.base.svc_idx = nsi;
++ eth_nsh_header->nsh_hdr.ctx.nshc1=nshc1;
++ eth_nsh_header->nsh_hdr.ctx.nshc2=nshc2;
++ eth_nsh_header->nsh_hdr.ctx.nshc3=nshc3;
++ eth_nsh_header->nsh_hdr.ctx.nshc4=nshc4;
+ push.nsh_mdtype = NSH_M_TYPE1;
+ nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_NSH,
+ &push, sizeof push);
+@@ -1713,33 +1703,6 @@ parse_odp_action(const char *s, const struct simap *port_names,
+ }
+
+ {
+- struct ovs_action_push_eth push;
+- struct encap_eth_hdr *encap_eth = (struct encap_eth_hdr *)push.header;
+- uint16_t encap_eth_type;
+- int n = -1;
+-
+- if (ovs_scan_len(s, &n, "push_eth(encap_eth_type=0x%"SCNx16",encap_eth_dst="ETH_ADDR_SCAN_FMT",encap_eth_src="ETH_ADDR_SCAN_FMT")",
+- &encap_eth_type,
+- ETH_ADDR_SCAN_ARGS(encap_eth->encap_eth_dst),
+- ETH_ADDR_SCAN_ARGS(encap_eth->encap_eth_src))) {
+- if (encap_eth->encap_eth_type == ETH_P_NSH) {
+- push.encap_eth_type = ETH_P_NSH;
+- encap_eth->encap_eth_type = htons(ETH_P_NSH);
+- nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_ETH,
+- &push, sizeof push);
+- }
+-
+- return n;
+- }
+-
+- }
+-
+- if (!strncmp(s, "pop_eth", 7)) {
+- nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_ETH);
+- return 7;
+- }
+-
+- {
+ double percentage;
+ int n = -1;
+
+@@ -1881,8 +1844,7 @@ static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] =
+ .next = ovs_tun_key_attr_lens,
+ .next_max = OVS_TUNNEL_KEY_ATTR_MAX },
+ [OVS_KEY_ATTR_IN_PORT] = { .len = 4 },
+- [OVS_KEY_ATTR_ENCAP_ETH] = { .len = 14 },
+- [OVS_KEY_ATTR_NSH] = { .len = 24 },
++ [OVS_KEY_ATTR_NSH] = { .len = sizeof(struct ovs_key_nsh) },
+ [OVS_KEY_ATTR_ETHERNET] = { .len = sizeof(struct ovs_key_ethernet) },
+ [OVS_KEY_ATTR_VLAN] = { .len = 2 },
+ [OVS_KEY_ATTR_ETHERTYPE] = { .len = 2 },
+@@ -2943,6 +2905,9 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
+ const struct ovs_key_nsh *mask = ma ? nl_attr_get(ma) : NULL;
+ const struct ovs_key_nsh *key = nl_attr_get(a);
+
++ format_eth(ds, "encap_eth_src", key->encap_eth_src, MASK(mask, encap_eth_src), verbose);
++ format_eth(ds, "encap_eth_dst", key->encap_eth_dst, MASK(mask, encap_eth_dst), verbose);
++ format_be16x(ds, "encap_eth_type", key->encap_eth_type, MASK(mask, encap_eth_type), verbose);
+ format_u8u(ds, "nsi", key->nsi, MASK(mask, nsi), verbose);
+ format_be32(ds, "nsp", key->nsp, MASK(mask, nsp), verbose);
+ format_u8u(ds, "nsh_mdtype", key->nsh_mdtype, MASK(mask, nsh_mdtype), verbose);
+@@ -2955,18 +2920,6 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
+ break;
+ }
+
+- case OVS_KEY_ATTR_ENCAP_ETH: {
+- const struct ovs_key_encap_eth *mask = ma ? nl_attr_get(ma) : NULL;
+- const struct ovs_key_encap_eth *key = nl_attr_get(a);
+-
+- format_be16(ds, "encap_eth_type", key->encap_eth_type, MASK(mask, encap_eth_type), verbose);
+- format_eth(ds, "encap_eth_src", key->encap_eth_src, MASK(mask, encap_eth_src), verbose);
+- format_eth(ds, "encap_eth_dst", key->encap_eth_dst, MASK(mask, encap_eth_dst), verbose);
+- ds_chomp(ds, ',');
+-
+- break;
+- }
+-
+ case OVS_KEY_ATTR_ETHERNET: {
+ const struct ovs_key_ethernet *mask = ma ? nl_attr_get(ma) : NULL;
+ const struct ovs_key_ethernet *key = nl_attr_get(a);
+@@ -3374,27 +3327,6 @@ scan_nsp(const char *s, uint32_t *key, uint32_t *mask)
+ }
+
+ static int
+-scan_encap_eth_type(const char *s, uint16_t *key, uint16_t *mask)
+-{
+- int n;
+-
+- if (ovs_scan(s, "%"SCNi16"%n", key, &n)) {
+- int len = n;
+- *key = htons(*key);
+- if (mask) {
+- if (ovs_scan(s + len, "/%"SCNi16"%n", mask, &n)) {
+- len += n;
+- *mask = htons(*mask);
+- } else {
+- *mask = UINT16_MAX;
+- }
+- }
+- return len;
+- }
+- return 0;
+-}
+-
+-static int
+ scan_ipv4(const char *s, ovs_be32 *key, ovs_be32 *mask)
+ {
+ int n;
+@@ -4335,6 +4267,9 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
+ } SCAN_END_NESTED();
+
+ SCAN_BEGIN("nsh(", struct ovs_key_nsh) {
++ SCAN_FIELD("encap_eth_dst=", eth, encap_eth_dst);
++ SCAN_FIELD("encap_eth_src=", eth, encap_eth_src);
++ SCAN_FIELD("encap_eth_type=", be16, encap_eth_type);
+ SCAN_FIELD("nsh_mdtype=", u8, nsh_mdtype);
+ SCAN_FIELD("nsh_np=", u8, nsh_np);
+ SCAN_FIELD("nsp=", nsp, nsp);
+@@ -4345,12 +4280,6 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
+ SCAN_FIELD("nshc4=", u32, nshc4);
+ } SCAN_END(OVS_KEY_ATTR_NSH);
+
+- SCAN_BEGIN("encap_eth(", struct ovs_key_encap_eth) {
+- SCAN_FIELD("encap_eth_src=", eth, encap_eth_src);
+- SCAN_FIELD("encap_eth_dst=", eth, encap_eth_dst);
+- SCAN_FIELD("encap_eth_type=", encap_eth_type, encap_eth_type);
+- } SCAN_END(OVS_KEY_ATTR_ENCAP_ETH);
+-
+ SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT);
+
+ SCAN_BEGIN("eth(", struct ovs_key_ethernet) {
+@@ -4560,19 +4489,15 @@ get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh)
+ nsh->nsp = flow->nsp;
+ nsh->nsh_mdtype = flow->nsh_mdtype;
+ nsh->nsh_np = flow->nsh_np;
+- nsh->reserved = 0;
+ nsh->nshc1 = flow->nshc1;
+ nsh->nshc2 = flow->nshc2;
+ nsh->nshc3 = flow->nshc3;
+ nsh->nshc4 = flow->nshc4;
+-}
+-
+-void
+-get_encap_eth_key(const struct flow *flow, struct ovs_key_encap_eth *encap_eth)
+-{
+- encap_eth->encap_eth_type = flow->encap_eth_type;
+- encap_eth->encap_eth_src = flow->encap_eth_src;
+- encap_eth->encap_eth_dst = flow->encap_eth_dst;
++ nsh->pad2 = 0;
++ nsh->encap_eth_src = flow->encap_eth_src;
++ nsh->encap_eth_dst = flow->encap_eth_dst;
++ nsh->encap_eth_type = flow->encap_eth_type;
++ nsh->pad1 = 0;
+ }
+
+ static void
+@@ -4581,7 +4506,6 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
+ {
+ struct ovs_key_ethernet *eth_key;
+ struct ovs_key_nsh *nsh_key;
+- struct ovs_key_encap_eth *encap_eth_key;
+ size_t encap;
+ const struct flow *flow = parms->flow;
+ const struct flow *data = export_mask ? parms->mask : parms->flow;
+@@ -4626,12 +4550,6 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
+ get_nsh_key(data, nsh_key);
+ }
+
+- if (flow->encap_eth_type) {
+- encap_eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ENCAP_ETH,
+- sizeof *encap_eth_key);
+- get_encap_eth_key(data, encap_eth_key);
+- }
+-
+ eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
+ sizeof *eth_key);
+ get_ethernet_key(data, eth_key);
+@@ -5500,17 +5418,6 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
+ }
+ }
+
+- /* ENCAP Eth header. */
+- if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP_ETH)) {
+- const struct ovs_key_encap_eth *encap_eth_key;
+-
+- encap_eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ENCAP_ETH]);
+- put_encap_eth_key(encap_eth_key, flow);
+- if (is_mask) {
+- expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ENCAP_ETH;
+- }
+- }
+-
+ /* Ethernet header. */
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) {
+ const struct ovs_key_ethernet *eth_key;
+@@ -5823,28 +5730,16 @@ put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow)
+ flow->nshc2 = nsh->nshc2;
+ flow->nshc3 = nsh->nshc3;
+ flow->nshc4 = nsh->nshc4;
++ flow->encap_eth_src = nsh->encap_eth_src;
++ flow->encap_eth_dst = nsh->encap_eth_dst;
++ flow->encap_eth_type = nsh->encap_eth_type;
+ }
+
+ void
+ flow_zero_nsh(struct flow *flow)
+ {
+- void *dst_p = &(flow->nshc1);
+- memset(dst_p, 0, 24);
+-}
+-
+-void
+-flow_zero_encap_eth(struct flow *flow)
+-{
+- void *dst_p = &flow->encap_eth_src;
+- memset(dst_p, 0, 16);
+-}
+-
+-void
+-put_encap_eth_key(const struct ovs_key_encap_eth *encap_eth, struct flow *flow)
+-{
+- flow->encap_eth_type = encap_eth->encap_eth_type;
+- flow->encap_eth_src = encap_eth->encap_eth_src;
+- flow->encap_eth_dst = encap_eth->encap_eth_dst;
++ void *dst_p = &(flow->encap_eth_dst);
++ memset(dst_p, 0, 40);
+ }
+
+ static void
+@@ -5899,45 +5794,45 @@ commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
+ }
+
+ static void
+-commit_nsh_pop_action(const struct flow *flow, struct flow *base,
+- struct ofpbuf *odp_actions, struct flow_wildcards *wc)
++commit_nsh_pop_action(const struct ovs_key_nsh *flow_key,
++ const struct ovs_key_nsh *base_key,
++ struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+ {
+- struct ovs_key_nsh flow_key, base_key;
+- get_nsh_key(flow, &flow_key);
+- get_nsh_key(base, &base_key);
+
+- if (memcmp(&flow_key, &base_key, sizeof flow_key)) {
++ if (memcmp(flow_key, base_key, sizeof (struct ovs_key_nsh))) {
+ memset(&wc->masks.nsh_mdtype, 0xff, sizeof wc->masks.nsh_mdtype);
+
+- if (base->nsh_mdtype) {
++ if (base_key->nsh_mdtype) {
+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_NSH);
+ }
+ }
+ }
+
+ static void
+-commit_nsh_push_action(const struct flow *flow, struct flow *base,
+- struct ofpbuf *odp_actions)
++commit_nsh_push_action(const struct ovs_key_nsh *flow_key,
++ const struct ovs_key_nsh *base_key,
++ struct ofpbuf *odp_actions)
+ {
+- struct ovs_key_nsh flow_key, base_key;
+- get_nsh_key(flow, &flow_key);
+- get_nsh_key(base, &base_key);
+-
+- if (memcmp(&flow_key, &base_key, sizeof flow_key)) {
+- if (flow->nsh_mdtype) {
++ if (memcmp(flow_key, base_key, sizeof (struct ovs_key_nsh))) {
++ if (flow_key->nsh_mdtype) {
+ struct ovs_action_push_nsh nsh;
+- nsh.nsh_mdtype = flow->nsh_mdtype;
+- struct nsh_hdr *nsh_hdr = (struct nsh_hdr *)nsh.header;
+- memset(nsh_hdr, 0, sizeof *nsh_hdr);
+- nsh_hdr->base.length = 6;
+- nsh_hdr->base.proto = flow->nsh_np;
+- nsh_hdr->base.mdtype = flow->nsh_mdtype;
+- nsh_hdr->base.proto = flow->nsh_np;
+- nsh_hdr->base.path_hdr = flow->nsp >> 8 | flow->nsi << 24;
+- nsh_hdr->ctx.nshc1 = flow->nshc1;
+- nsh_hdr->ctx.nshc2 = flow->nshc2;
+- nsh_hdr->ctx.nshc3 = flow->nshc3;
+- nsh_hdr->ctx.nshc4 = flow->nshc4;
++ nsh.nsh_mdtype = flow_key->nsh_mdtype;
++ struct eth_nsh_hdr *eth_nsh_header = (struct eth_nsh_hdr *)nsh.header;
++ memset(eth_nsh_header, 0, sizeof *eth_nsh_header);
++ eth_nsh_header->nsh_hdr.base.length = 6;
++ eth_nsh_header->nsh_hdr.base.proto = flow_key->nsh_np;
++ eth_nsh_header->nsh_hdr.base.mdtype = flow_key->nsh_mdtype;
++ eth_nsh_header->nsh_hdr.base.proto = flow_key->nsh_np;
++ eth_nsh_header->nsh_hdr.base.path_hdr = flow_key->nsp >> 8 | flow_key->nsi << 24;
++ eth_nsh_header->nsh_hdr.ctx.nshc1 = flow_key->nshc1;
++ eth_nsh_header->nsh_hdr.ctx.nshc2 = flow_key->nshc2;
++ eth_nsh_header->nsh_hdr.ctx.nshc3 = flow_key->nshc3;
++ eth_nsh_header->nsh_hdr.ctx.nshc4 = flow_key->nshc4;
++
++ eth_nsh_header->encap_eth_header.encap_eth_type = htons(ETH_P_NSH);
++ eth_nsh_header->encap_eth_header.encap_eth_dst = flow_key->encap_eth_dst;
++ eth_nsh_header->encap_eth_header.encap_eth_src = flow_key->encap_eth_src;
++
+ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_NSH,
+ &nsh, sizeof nsh);
+ }
+@@ -5945,46 +5840,16 @@ commit_nsh_push_action(const struct flow *flow, struct flow *base,
+ }
+
+ static void
+-commit_encap_eth_pop_action(const struct flow *flow, struct flow *base,
++commit_nsh_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+ {
+- struct ovs_key_encap_eth flow_key, base_key;
+- get_encap_eth_key(flow, &flow_key);
+- get_encap_eth_key(base, &base_key);
+-
+- if (memcmp(&flow_key, &base_key, sizeof flow_key)) {
+- memset(&wc->masks.encap_eth_type, 0xff, sizeof wc->masks.encap_eth_type);
+-
+- if (base->encap_eth_type) {
+- nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_ETH);
+- }
+- }
++ struct ovs_key_nsh flow_key, base_key;
++ get_nsh_key(flow, &flow_key);
++ get_nsh_key(base, &base_key);
++ commit_nsh_pop_action(&flow_key, &base_key, odp_actions, wc);
++ commit_nsh_push_action(&flow_key, &base_key, odp_actions);
+ }
+
+-static void
+-commit_encap_eth_push_action(const struct flow *flow, struct flow *base,
+- struct ofpbuf *odp_actions)
+-{
+- struct ovs_key_encap_eth flow_key, base_key;
+- get_encap_eth_key(flow, &flow_key);
+- get_encap_eth_key(base, &base_key);
+-
+- if (memcmp(&flow_key, &base_key, sizeof flow_key)) {
+- if (flow->encap_eth_type) {
+- struct ovs_action_push_eth encap_eth;
+- encap_eth.encap_eth_type = flow->encap_eth_type;
+- struct encap_eth_hdr *encap_eth_header =
+- (struct encap_eth_hdr *)encap_eth.header;
+- void *dst_p = encap_eth_header->encap_eth_dst.ea;
+- void *src_p = encap_eth_header->encap_eth_src.ea;
+- memcpy(dst_p, flow->encap_eth_dst.ea, sizeof flow->encap_eth_dst);
+- memcpy(src_p, flow->encap_eth_src.ea, sizeof flow->encap_eth_src);
+- encap_eth_header->encap_eth_type = htons(ETH_P_NSH);
+- nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_ETH,
+- &encap_eth, sizeof encap_eth);
+- }
+- }
+-}
+
+ /* Wildcarding already done at action translation time. */
+ static void
+@@ -6417,10 +6282,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base,
+ slow2 = commit_set_icmp_action(flow, base, odp_actions, wc);
+ commit_mpls_action(flow, base, odp_actions);
+ commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
+- commit_encap_eth_pop_action(flow, base, odp_actions, wc);
+- commit_nsh_pop_action(flow, base, odp_actions, wc);
+- commit_nsh_push_action(flow, base, odp_actions);
+- commit_encap_eth_push_action(flow, base, odp_actions);
++ commit_nsh_action(flow, base, odp_actions, wc);
+ commit_set_priority_action(flow, base, odp_actions, wc, use_masked);
+ commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked);
+
+diff --git a/lib/odp-util.h b/lib/odp-util.h
+index 2cb04f1..26e036f 100644
+--- a/lib/odp-util.h
++++ b/lib/odp-util.h
+@@ -126,8 +126,7 @@ void odp_portno_names_destroy(struct hmap *portno_names);
+ * OVS_KEY_ATTR_CT_ZONE 2 2 4 8
+ * OVS_KEY_ATTR_CT_MARK 4 -- 4 8
+ * OVS_KEY_ATTR_CT_LABEL 16 -- 4 20
+- * OVS_KEY_ATTR_NSH 24 -- 4 28
+- * OVS_KEY_ATTR_ENCAP 14 -- 4 18
++ * OVS_KEY_ATTR_NSH 40 -- 4 44
+ * OVS_KEY_ATTR_ETHERNET 12 -- 4 16
+ * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype)
+ * OVS_KEY_ATTR_VLAN 2 2 4 8
+@@ -154,10 +153,7 @@ struct odputil_keybuf {
+
+ void put_nsh_key(const struct ovs_key_nsh *, struct flow *);
+ void get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh);
+-void put_encap_eth_key(const struct ovs_key_encap_eth *encap_eth, struct flow *flow);
+-void get_encap_eth_key(const struct flow *flow, struct ovs_key_encap_eth *encap_eth);
+ void flow_zero_nsh(struct flow *);
+-void flow_zero_encap_eth(struct flow *);
+ enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *, bool udpif,
+ struct flow_tnl *);
+
+diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
+index f4062b2..8cce6c9 100644
+--- a/lib/ofp-actions.c
++++ b/lib/ofp-actions.c
+@@ -305,12 +305,6 @@ enum ofp_raw_action_type {
+ /* NX1.0+(39): void. */
+ NXAST_RAW_POP_NSH,
+
+- /* NX1.0+(40): void. */
+- NXAST_RAW_PUSH_ETH,
+-
+- /* NX1.0+(41): void. */
+- NXAST_RAW_POP_ETH,
+-
+ /* ## ------------------ ## */
+ /* ## Debugging actions. ## */
+ /* ## ------------------ ## */
+@@ -397,6 +391,8 @@ ofpact_next_flattened(const struct ofpact *ofpact)
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_PUSH_VLAN:
++ case OFPACT_PUSH_NSH:
++ case OFPACT_POP_NSH:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+@@ -1697,66 +1693,6 @@ format_POP_NSH(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+ ds_put_format(s, "pop_nsh");
+ }
+
+-/* Push ENCAP Eth header actions. */
+-static enum ofperr
+-decode_NXAST_RAW_PUSH_ETH(struct ofpbuf * out)
+-{
+- ofpact_put_PUSH_ETH(out)->ofpact.raw = NXAST_RAW_PUSH_ETH;
+-
+- return 0;
+-}
+-
+-static void
+-encode_PUSH_ETH(const struct ofpact_null *null OVS_UNUSED,
+- enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
+-{
+- put_NXAST_PUSH_ETH(out);
+-}
+-
+-static char * OVS_WARN_UNUSED_RESULT
+-parse_PUSH_ETH(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+- enum ofputil_protocol *usable_protocols OVS_UNUSED)
+-{
+- ofpact_put_PUSH_ETH(ofpacts)->ofpact.raw = NXAST_RAW_PUSH_ETH;;
+- return NULL;
+-}
+-
+-static void
+-format_PUSH_ETH(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+-{
+- ds_put_format(s, "push_eth");
+-}
+-
+-/* Pop ENCAP ETH header actions. */
+-static enum ofperr
+-decode_NXAST_RAW_POP_ETH(struct ofpbuf * out)
+-{
+- ofpact_put_POP_ETH(out)->ofpact.raw = NXAST_RAW_POP_ETH;
+-
+- return 0;
+-}
+-
+-static void
+-encode_POP_ETH(const struct ofpact_null *null OVS_UNUSED,
+- enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
+-{
+- put_NXAST_POP_ETH(out);
+-}
+-
+-static char * OVS_WARN_UNUSED_RESULT
+-parse_POP_ETH(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+- enum ofputil_protocol *usable_protocols OVS_UNUSED)
+-{
+- ofpact_put_POP_ETH(ofpacts)->ofpact.raw = NXAST_RAW_POP_ETH;
+- return NULL;
+-}
+-
+-static void
+-format_POP_ETH(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+-{
+- ds_put_format(s, "pop_eth");
+-}
+-
+ /* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */
+ struct ofp_action_dl_addr {
+ ovs_be16 type; /* Type. */
+@@ -6044,8 +5980,6 @@ ofpact_is_set_or_move_action(const struct ofpact *a)
+ case OFPACT_PUSH_VLAN:
+ case OFPACT_PUSH_NSH:
+ case OFPACT_POP_NSH:
+- case OFPACT_PUSH_ETH:
+- case OFPACT_POP_ETH:
+ case OFPACT_RESUBMIT:
+ case OFPACT_SAMPLE:
+ case OFPACT_STACK_POP:
+@@ -6075,8 +6009,6 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
+ case OFPACT_PUSH_VLAN:
+ case OFPACT_PUSH_NSH:
+ case OFPACT_POP_NSH:
+- case OFPACT_PUSH_ETH:
+- case OFPACT_POP_ETH:
+ case OFPACT_REG_MOVE:
+ case OFPACT_SET_FIELD:
+ case OFPACT_SET_ETH_DST:
+@@ -6304,8 +6236,6 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
+ case OFPACT_PUSH_VLAN:
+ case OFPACT_PUSH_NSH:
+ case OFPACT_POP_NSH:
+- case OFPACT_PUSH_ETH:
+- case OFPACT_POP_ETH:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+@@ -6775,6 +6705,8 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
+ flow->vlan_tci |= htons(VLAN_CFI);
+ return 0;
+
++ case OFPACT_PUSH_NSH:
++ case OFPACT_POP_NSH:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ return 0;
+@@ -6853,10 +6785,6 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_POP_QUEUE:
+- case OFPACT_PUSH_NSH:
+- case OFPACT_POP_NSH:
+- case OFPACT_PUSH_ETH:
+- case OFPACT_POP_ETH:
+ case OFPACT_RESUBMIT:
+ return 0;
+
+@@ -7416,8 +7344,6 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
+ case OFPACT_PUSH_VLAN:
+ case OFPACT_PUSH_NSH:
+ case OFPACT_POP_NSH:
+- case OFPACT_PUSH_ETH:
+- case OFPACT_POP_ETH:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
+index 3897c0b..e212f87 100644
+--- a/lib/ofp-actions.h
++++ b/lib/ofp-actions.h
+@@ -96,8 +96,6 @@
+ /* NSH */ \
+ OFPACT(PUSH_NSH, ofpact_null, ofpact, "push_nsh") \
+ OFPACT(POP_NSH, ofpact_null, ofpact, "pop_nsh") \
+- OFPACT(PUSH_ETH, ofpact_null, ofpact, "push_eth") \
+- OFPACT(POP_ETH, ofpact_null, ofpact, "pop_eth") \
+ \
+ /* Flow table interaction. */ \
+ OFPACT(RESUBMIT, ofpact_resubmit, ofpact, "resubmit") \
+diff --git a/lib/packets.c b/lib/packets.c
+index d0c0e68..d314b2d 100644
+--- a/lib/packets.c
++++ b/lib/packets.c
+@@ -242,6 +242,29 @@ set_ethertype(struct dp_packet *packet, ovs_be16 eth_type)
+ }
+ }
+
++void
++push_nsh(struct dp_packet *packet, const void *nsh)
++{
++ void *header;
++ const struct ovs_action_push_nsh *push_nsh_hdr = (const struct ovs_action_push_nsh *)nsh;
++ const uint8_t *pdata = push_nsh_hdr->header;
++ /* Now only support MD type1 */
++ if (push_nsh_hdr->nsh_mdtype != NSH_M_TYPE1)
++ return;
++ header = dp_packet_push_uninit(packet, ETH_NSH_TYPE1_HEADER_SIZE);
++
++ memcpy(header, pdata, ETH_NSH_TYPE1_HEADER_SIZE);
++}
++
++void
++pop_nsh(struct dp_packet *packet)
++{
++ if (ETH_NSH_TYPE1_HEADER_SIZE > dp_packet_size(packet)) {
++ return;
++ }
++ dp_packet_reset_packet(packet, ETH_NSH_TYPE1_HEADER_SIZE);
++}
++
+ static bool is_mpls(struct dp_packet *packet)
+ {
+ return packet->l2_5_ofs != UINT16_MAX;
+diff --git a/lib/packets.h b/lib/packets.h
+index dc97333..057efab 100644
+--- a/lib/packets.h
++++ b/lib/packets.h
+@@ -313,6 +313,9 @@ void compose_rarp(struct dp_packet *, const struct eth_addr);
+ void eth_push_vlan(struct dp_packet *, ovs_be16 tpid, ovs_be16 tci);
+ void eth_pop_vlan(struct dp_packet *);
+
++void push_nsh(struct dp_packet *packet, const void *nsh);
++void pop_nsh(struct dp_packet *packet);
++
+ const char *eth_from_hex(const char *hex, struct dp_packet **packetp);
+ void eth_format_masked(const struct eth_addr ea,
+ const struct eth_addr *mask, struct ds *s);
+diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
+index e15e8d2..511e20a 100644
+--- a/ofproto/ofproto-dpif-sflow.c
++++ b/ofproto/ofproto-dpif-sflow.c
+@@ -977,7 +977,6 @@ sflow_read_set_action(const struct nlattr *attr,
+ case OVS_KEY_ATTR_ETHERNET:
+ case OVS_KEY_ATTR_VLAN:
+ case OVS_KEY_ATTR_NSH:
+- case OVS_KEY_ATTR_ENCAP_ETH:
+ break;
+
+ case OVS_KEY_ATTR_MPLS: {
+diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
+index f59eed8..ec19739 100644
+--- a/ofproto/ofproto-dpif-upcall.c
++++ b/ofproto/ofproto-dpif-upcall.c
+@@ -748,7 +748,6 @@ recv_upcalls(struct handler *handler)
+ struct flow *flow = &flows[n_upcalls];
+ unsigned int mru;
+ struct ovs_key_nsh reserve_nsh;
+- struct ovs_key_encap_eth reserve_encap_eth;
+ bool nsh_mdtype_flag, encap_eth_type_flag;
+ int error;
+
+@@ -802,21 +801,14 @@ recv_upcalls(struct handler *handler)
+ pkt_metadata_from_flow(&dupcall->packet.md, flow);
+
+ nsh_mdtype_flag = !!(flow->nsh_mdtype);
+- encap_eth_type_flag = !!(flow->encap_eth_type);
+ if(nsh_mdtype_flag)
+ get_nsh_key(flow, &reserve_nsh);
+
+- if(encap_eth_type_flag)
+- get_encap_eth_key(flow, &reserve_encap_eth);
+-
+ flow_extract(&dupcall->packet, flow);
+
+ if(nsh_mdtype_flag)
+ put_nsh_key(&reserve_nsh, flow);
+
+- if(encap_eth_type_flag)
+- put_encap_eth_key(&reserve_encap_eth, flow);
+-
+ error = process_upcall(udpif, upcall,
+ &upcall->odp_actions, &upcall->wc);
+ if (error) {
+diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
+index 4f91d0a..221c17d 100644
+--- a/ofproto/ofproto-dpif-xlate.c
++++ b/ofproto/ofproto-dpif-xlate.c
+@@ -4242,8 +4242,6 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end,
+ case OFPACT_PUSH_VLAN:
+ case OFPACT_PUSH_NSH:
+ case OFPACT_POP_NSH:
+- case OFPACT_PUSH_ETH:
+- case OFPACT_POP_ETH:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+@@ -4530,6 +4528,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+
+ case OFPACT_PUSH_NSH:
+ memset(&wc->masks.nsh_mdtype, 0xff, sizeof wc->masks.nsh_mdtype);
++ flow->encap_eth_dst = flow->dl_dst;
++ flow->encap_eth_src = flow->dl_src;
+ break;
+
+ case OFPACT_POP_NSH:
+@@ -4537,15 +4537,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+ flow_zero_nsh(flow);
+ break;
+
+- case OFPACT_PUSH_ETH:
+- memset(&wc->masks.encap_eth_type, 0xff, sizeof wc->masks.encap_eth_type);
+- break;
+-
+- case OFPACT_POP_ETH:
+- memset(&wc->masks.encap_eth_type, 0xff, sizeof wc->masks.encap_eth_type);
+- flow_zero_encap_eth(flow);
+- break;
+-
+ case OFPACT_STRIP_VLAN:
+ memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
+ flow->vlan_tci = htons(0);
+diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
+index 0064178..aee8ff5 100644
+--- a/ofproto/ofproto-dpif.h
++++ b/ofproto/ofproto-dpif.h
+@@ -93,7 +93,6 @@ struct dpif_backer_support {
+ /* Each member represents support for related OVS_KEY_ATTR_* fields. */
+ struct odp_support odp;
+ };
+-
+ bool ofproto_dpif_get_enable_ufid(const struct dpif_backer *backer);
+ struct dpif_backer_support *ofproto_dpif_get_support(const struct ofproto_dpif *);
+
+diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
+index 7430994..bc92b40 100644
+--- a/ofproto/tunnel.c
++++ b/ofproto/tunnel.c
+@@ -704,6 +704,7 @@ tnl_port_build_header(const struct ofport_dpif *ofport,
+
+ /* Build Ethernet and IP headers. */
+ memset(data->header, 0, sizeof data->header);
++ data->exts = 0;
+
+ eth = (struct eth_header *)data->header;
+ eth->eth_dst = dmac;
+diff --git a/tests/ofproto.at b/tests/ofproto.at
+index 7141e39..98f4872 100644
+--- a/tests/ofproto.at
++++ b/tests/ofproto.at
+@@ -1927,7 +1927,6 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
+ nsh_np: arbitrary mask
+ encap_eth_src: arbitrary mask
+ encap_eth_dst: arbitrary mask
+- encap_eth_type: exact match or wildcard
+
+ ' $1
+ }
+diff --git a/tests/tunnel.at b/tests/tunnel.at
+index d957574..7162ef0 100644
+--- a/tests/tunnel.at
++++ b/tests/tunnel.at
+@@ -427,6 +427,14 @@ in_port=1 actions=push_nsh,set_field:1->nsh_mdtype,set_field:0x3->nsh_np,set_fie
+ in_port=2 actions=push_nsh,set_field:1->nsh_mdtype,set_field:0x3->nsh_np,set_field:0x112233->nsp,set_field:0x44->nsi,set_field:0x11223344->nshc1,set_field:0x55667788->nshc2,set_field:0x99aabbcc->nshc3,set_field:0xddeeff00->nshc4,set_field:4->tun_gpe_np,output:2
+ ])
+ 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/1: (dummy)
++ p2 2/4790: (vxlan: dst_port=4790, key=flow, remote_ip=1.1.1.1)
++ p90 90/90: (dummy)
++])
++
+ 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: push_nsh(nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=287454020,nshc2=1432778632,nshc3=2578103244,nshc4=3723427584),1,set(tunnel(tun_id=0x0,dst=1.1.1.1,ttl=64,vxlan(gpe(np=0x4,flags=0)),flags(df|key))),push_nsh(nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=287454020,nshc2=1432778632,nshc3=2578103244,nshc4=3723427584),4790
+@@ -496,12 +504,6 @@ priority=200,in_port=2,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,encap_eth_typ
+
+ 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/1: (dummy)
+- p2 2/2: (dummy)
+-])
+-
+ AT_CHECK([ovs-appctl ofproto/trace ovs-dummy ',in_port(2),encap_eth(encap_eth_type=0x894f,encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa),nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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: pop_eth,pop_nsh,1
+@@ -525,12 +527,6 @@ priority=200,in_port=2,tun_gpe_np=4,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,
+
+ 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/1: (dummy)
+- p2 2/4790: (vxlan: dst_port=4790, key=flow, remote_ip=1.1.1.1)
+-])
+-
+ AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=1.2.3.4,ttl=64,flags(),vxlan(gpe(np=4,flags=0x0c))),in_port(4790),nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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: push_eth(encap_eth_type=35151,encap_eth_src=00:66:77:88:99:aa,encap_eth_dst=00:11:22:33:44:55,),1
+@@ -553,21 +549,184 @@ priority=200,in_port=1,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,encap_eth_typ
+
+ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy ',in_port(1),encap_eth(encap_eth_type=0x894f,encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa),,nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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,vxlan(gpe(np=0x4,flags=0)),flags(df|key))),pop_eth,4790
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([tunnel - VXLAN-GPE and NSH - Encapsulation - user space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
++ options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=2 \
++ options:exts=gpe])
++
++ADD_OF_PORTS([br0], [90])
++
++AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
++])
++AT_CHECK([
++ovs-appctl ovs/route/add 1.1.1.1/24 br0
++ovs-appctl tnl/arp/set br0 1.1.1.1 00:11:11:11:11:11
++],[0],[stdout])
++
++AT_DATA([flows.txt], [dnl
++in_port=90 actions=resubmit:1,resubmit:2
++in_port=1 actions=push_nsh,set_field:1->nsh_mdtype,set_field:0x3->nsh_np,set_field:0x112233->nsp,set_field:0x44->nsi,set_field:0x11223344->nshc1,set_field:0x55667788->nshc2,set_field:0x99aabbcc->nshc3,set_field:0xddeeff00->nshc4,output:1
++in_port=2 actions=push_nsh,set_field:1->nsh_mdtype,set_field:0x3->nsh_np,set_field:0x112233->nsp,set_field:0x44->nsi,set_field:0x11223344->nshc1,set_field:0x55667788->nshc2,set_field:0x99aabbcc->nshc3,set_field:0xddeeff00->nshc4,set_field:4->tun_gpe_np,output:2
++])
++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: push_nsh(encap_eth_src=00:00:00:00:00:00,encap_eth_dst=00:00:00:00:00:00,nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=287454020,nshc2=1432778632,nshc3=2578103244,nshc4=3723427584),1,push_nsh(encap_eth_src=00:00:00:00:00:00,encap_eth_dst=00:00:00:00:00:00,nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=287454020,nshc2=1432778632,nshc3=2578103244,nshc4=3723427584),tnl_push(tnl_port(4790),header(size=50,type=4,eth(dst=00:11:11:11:11:11,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=1.1.1.1,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(100))
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([tunnel - VXLAN-GPE and NSH - Decapsulation - user space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
++ options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=2 \
++ options:exts=gpe])
++
++AT_DATA([flows.txt], [dnl
++priority=200,in_port=2,tun_gpe_np=4,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,actions=pop_nsh,output=1
++priority=100,in_port=1,actions=local
++])
++
++AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
++
++AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.1.2/24], [0], [OK
++])
++
++AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK
++])
++
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.1.1,dst=1.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4790)'], [0], [stdout])
++AT_CHECK([tail -1 stdout], [0],
++ [Datapath actions: tnl_pop(4790)
++])
++
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=1.2.3.4,ttl=64,flags(),vxlan(gpe(np=4,flags=0x0c))),in_port(4790),nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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: pop_nsh,1
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([Eth and NSH - Encapsulation - user space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
++
++ADD_OF_PORTS([br0], [90])
++AT_DATA([flows.txt], [dnl
++in_port=90 actions=resubmit:1,resubmit:2
++in_port=1 actions=push_nsh,set_field:1->nsh_mdtype,set_field:0x3->nsh_np,set_field:0x112233->nsp,set_field:0x44->nsi,set_field:0x11223344->nshc1,set_field:0x55667788->nshc2,set_field:0x99aabbcc->nshc3,set_field:00:11:22:33:44:55->encap_eth_dst,set_field:00:66:77:88:99:aa->encap_eth_src,output:1
++])
++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: push_nsh(encap_eth_src=00:66:77:88:99:aa,encap_eth_dst=00:11:22:33:44:55,nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=287454020,nshc2=1432778632,nshc3=2578103244,nshc4=0),1
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([Eth and NSH - Decapsulation - user space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
++
++AT_DATA([flows.txt], [dnl
++priority=200,in_port=2,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa,actions=pop_nsh,output=1
++])
++
++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/1: (dummy)
+- p2 2/4790: (vxlan: dst_port=4790, key=flow, remote_ip=1.1.1.1)
++ p2 2/2: (dummy)
+ ])
+
+-AT_CHECK([ovs-appctl ofproto/trace ovs-dummy ',in_port(1),encap_eth(encap_eth_type=0x894f,encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa),,nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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([ovs-appctl ofproto/trace ovs-dummy ',in_port(2),nsh(encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa,nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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,vxlan(gpe(np=0x4,flags=0)),flags(df|key))),pop_eth,4790
++ [Datapath actions: pop_nsh,1
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([VXLANGPE+NSH to Eth+NSH - user space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 \
++ -- add-port br0 p3 -- set Interface p3 type=vxlan options:key=flow \
++ options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=3 \
++ options:exts=gpe])
++
++AT_DATA([flows.txt], [dnl
++priority=200,in_port=3,tun_gpe_np=4,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,actions=set_field:00:11:22:33:44:55->encap_eth_dst,set_field:00:66:77:88:99:aa->encap_eth_src,output:2
++priority=100,in_port=1,actions=local
++])
++
++AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
++
++AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.1.2/24], [0], [OK
++])
++
++AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK
++])
++
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.1.1,dst=1.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4790)'], [0], [stdout])
++AT_CHECK([tail -1 stdout], [0],
++ [Datapath actions: tnl_pop(4790)
++])
++
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=1.2.3.4,ttl=64,flags(),vxlan(gpe(np=4,flags=0x0c))),in_port(4790),nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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: pop_nsh,push_nsh(encap_eth_src=00:66:77:88:99:aa,encap_eth_dst=00:11:22:33:44:55,nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=1144201745,nshc2=2289526357,nshc3=3434850969,nshc4=16772829),2
+ ])
+
+ OVS_VSWITCHD_STOP
+ AT_CLEANUP
+
++AT_SETUP([Eth+NSH to VXLANGPE+NSH - user space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
++ options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=2 \
++ options:exts=gpe])
++
++AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.1.2/24], [0], [OK
++])
++AT_CHECK([
++ovs-appctl ovs/route/add 1.1.1.1/24 br0
++ovs-appctl tnl/arp/set br0 1.1.1.1 00:11:11:11:11:11
++],[0],[stdout])
++
++AT_DATA([flows.txt], [dnl
++priority=200,in_port=1,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa,actions=set_field:0x4->tun_gpe_np,output=2
++])
+
++AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
++
++
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy ',in_port(1),nsh(encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa,nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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: tnl_push(tnl_port(4790),header(size=50,type=4,eth(dst=00:11:11:11:11:11,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.1.2,dst=1.1.1.1,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(100))
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
+
+ AT_SETUP([tunnel - Geneve metadata])
+ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
diff --git a/ovs-nsh/patches/299fc5b.patch b/ovs-nsh/patches/299fc5b.patch
new file mode 100644
index 0000000..a6713c8
--- /dev/null
+++ b/ovs-nsh/patches/299fc5b.patch
@@ -0,0 +1,68 @@
+commit 299fc5b315e879258b024af98c9292565184b107
+Author: Yi Yang
+Date: Fri Apr 15 14:17:54 2016 +0800
+
+ Ethernet header must be kept in VxLAN-gpe + eth + NSH for new ovs lwtunnel implementation
+
+ Signed-off-by: Yi Yang
+
+diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c
+index 888d431..3c05141 100644
+--- a/datapath/linux/compat/vxlan.c
++++ b/datapath/linux/compat/vxlan.c
+@@ -821,7 +821,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
+ struct vxlan_dev *vxlan;
+ struct pcpu_sw_netstats *stats;
+ union vxlan_addr saddr;
+- struct eth_nsh_hdr *eth_nsh_header = NULL;
++ //struct eth_nsh_hdr *eth_nsh_header = NULL;
+ int err = 0;
+
+ /* For flow based devices, map all packets to VNI 0 */
+@@ -891,12 +891,13 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
+ u64_stats_update_end(&stats->syncp);
+
+ /* Add a faked encap_eth_header for NSH */
+- if (md && ((md->gpe & VXLAN_GPE_NP_MASK) == VXLAN_GPE_NP_NSH)) {
++ /* ovs changes have ensured it is here, so needn't add it any more */
++ /*if (md && ((md->gpe & VXLAN_GPE_NP_MASK) == VXLAN_GPE_NP_NSH)) {
+ skb_push(skb, ENCAP_ETH_LEN);
+ eth_nsh_header = (struct eth_nsh_hdr *)skb->data;
+ memmove(eth_nsh_header->encap_eth_header.encap_eth_dst, skb_mac_header(skb), ENCAP_ETH_LEN);
+ eth_nsh_header->encap_eth_header.encap_eth_type = htons(ETH_P_NSH);
+- }
++ }*/
+
+ netdev_port_receive(skb, skb_tunnel_info(skb));
+ return;
+@@ -985,7 +986,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
+ struct vxlanhdr_gpe *gpe;
+
+ gpe = (struct vxlanhdr_gpe *)vxh;
+- md->gpe = ntohs(gpe->next_proto);
++ md->gpe = gpe->next_proto;
+
+ buf.dst.u.tun_info.key.tun_flags |= TUNNEL_VXLAN_OPT;
+
+@@ -1189,9 +1190,10 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *sk
+ }
+
+ /* Skip encap_eth_header on sending Eth+NSH to vxlan-gpe port */
+- if (md && ((md->gpe & VXLAN_GPE_NP_MASK) == VXLAN_GPE_NP_NSH)) {
++ /* ovs needs to keep eth header here */
++ /*if (md && ((md->gpe & VXLAN_GPE_NP_MASK) == VXLAN_GPE_NP_NSH)) {
+ skb_pull(skb, ENCAP_ETH_LEN);
+- }
++ }*/
+
+ min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
+ + VXLAN_HLEN + sizeof(struct iphdr)
+@@ -1236,6 +1238,8 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *sk
+ }
+ if (vxflags & VXLAN_F_GBP)
+ vxlan_build_gbp_hdr(vxh, vxflags, md);
++ else if (vxflags & VXLAN_F_GPE)
++ vxlan_build_gpe_hdr(vxh, vxflags, md);
+
+ ovs_skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+
diff --git a/ovs-nsh/patches/bf1e7ff.patch b/ovs-nsh/patches/bf1e7ff.patch
new file mode 100644
index 0000000..db9be43
--- /dev/null
+++ b/ovs-nsh/patches/bf1e7ff.patch
@@ -0,0 +1,2714 @@
+commit bf1e7ffdef027bb83d7b8a42418e51988490c0c4
+Author: Yi Yang
+Date: Wed Apr 13 11:15:42 2016 +0800
+
+ ovs-nsh: support push and pop actions for vxlan-gpe and Ethernet nsh
+
+ Signed-off-by: Mengke Liu
+ Signed-off-by: Ricky Li
+ Signed-off-by: Johnson Li
+ Signed-off-by: Yi Yang
+
+diff --git a/datapath/actions.c b/datapath/actions.c
+index dcf8591..16fc58f 100644
+--- a/datapath/actions.c
++++ b/datapath/actions.c
+@@ -252,6 +252,63 @@ static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
+ ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
+ }
+
++static int pop_nsh(struct sk_buff *skb, struct sw_flow_key *key)
++{
++ if (!pskb_may_pull(skb, NSH_M_TYPE1_LEN))
++ return -ENOMEM;
++ else
++ __skb_pull(skb, NSH_M_TYPE1_LEN);
++
++ return 0;
++}
++
++static int push_nsh(struct sk_buff *skb, struct sw_flow_key *key,
++ const struct ovs_action_push_nsh *nsh)
++{
++
++ if (nsh->nsh_mdtype == NSH_M_TYPE1) {
++ if (skb_cow_head(skb, NSH_M_TYPE1_LEN) < 0)
++ return -ENOMEM;
++
++ skb_push(skb, NSH_M_TYPE1_LEN);
++ OVS_CB(skb)->nsh_header = skb->data;
++ memcpy(OVS_CB(skb)->nsh_header, nsh->header, NSH_M_TYPE1_LEN);
++ }
++ else
++ return -EINVAL;
++
++ return 0;
++}
++
++static int pop_eth(struct sk_buff *skb, struct sw_flow_key *key)
++{
++ if (!pskb_may_pull(skb, ENCAP_ETH_PUSH_HEADER_SIZE))
++ return -ENOMEM;
++ else
++ __skb_pull(skb, ENCAP_ETH_PUSH_HEADER_SIZE);
++
++ return 0;
++}
++
++static int push_eth(struct sk_buff *skb, struct sw_flow_key *key,
++ const struct ovs_action_push_eth *encap_eth)
++{
++ if (encap_eth->encap_eth_type == htons(ETH_P_NSH)) {
++ if (skb_cow_head(skb, ENCAP_ETH_PUSH_HEADER_SIZE) < 0)
++ return -ENOMEM;
++
++ skb_push(skb, ENCAP_ETH_PUSH_HEADER_SIZE);
++ OVS_CB(skb)->encap_eth_header = skb->data;
++ memcpy(OVS_CB(skb)->encap_eth_header, encap_eth->header, ENCAP_ETH_PUSH_HEADER_SIZE);
++ }
++ else {
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++
+ /* 'src' is already properly masked. */
+ static void ether_addr_copy_masked(u8 *dst_, const u8 *src_, const u8 *mask_)
+ {
+@@ -1079,6 +1136,22 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
+ err = pop_vlan(skb, key);
+ break;
+
++ case OVS_ACTION_ATTR_PUSH_NSH:
++ err = push_nsh(skb, key, nla_data(a));
++ break;
++
++ case OVS_ACTION_ATTR_POP_NSH:
++ err = pop_nsh(skb, key);
++ break;
++
++ case OVS_ACTION_ATTR_PUSH_ETH:
++ err = push_eth(skb, key, nla_data(a));
++ break;
++
++ case OVS_ACTION_ATTR_POP_ETH:
++ err = pop_eth(skb, key);
++ break;
++
+ case OVS_ACTION_ATTR_RECIRC:
+ err = execute_recirc(dp, skb, key, a, rem);
+ if (nla_is_last(a, rem)) {
+diff --git a/datapath/datapath.h b/datapath/datapath.h
+index ceb3372..b2d5fcd 100644
+--- a/datapath/datapath.h
++++ b/datapath/datapath.h
+@@ -102,6 +102,8 @@ struct datapath {
+ struct ovs_skb_cb {
+ struct vport *input_vport;
+ u16 mru;
++ struct nsh_hdr *nsh_header;
++ struct encap_eth_hdr *encap_eth_header;
+ };
+ #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
+
+diff --git a/datapath/flow.c b/datapath/flow.c
+index c97c9c9..67b2f1d 100644
+--- a/datapath/flow.c
++++ b/datapath/flow.c
+@@ -44,6 +44,7 @@
+ #include
+ #include
+ #include
++#include
+
+ #include "datapath.h"
+ #include "conntrack.h"
+@@ -318,6 +319,32 @@ static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
+ return 0;
+ }
+
++static int parse_nsh(struct sk_buff *skb, struct sw_flow_key *key){
++ struct nsh_hdr *nsh_hdr = (struct nsh_hdr *)skb->data;
++ OVS_CB(skb)->nsh_header = nsh_hdr;
++ key->nsh.nsh_mdtype = nsh_hdr->base.mdtype;
++ if (key->nsh.nsh_mdtype != NSH_M_TYPE1)
++ return -EPERM;
++ key->nsh.nsh_np = nsh_hdr->base.proto;
++ key->nsh.nsi = nsh_hdr->base.svc_idx;
++ key->nsh.nsp = nsh_hdr->base.path_hdr << 8;
++ key->nsh.nshc1 = nsh_hdr->ctx.nshc1;
++ key->nsh.nshc2 = nsh_hdr->ctx.nshc2;
++ key->nsh.nshc3 = nsh_hdr->ctx.nshc3;
++ key->nsh.nshc4 = nsh_hdr->ctx.nshc4;
++ return 0;
++}
++
++static void parse_encap_eth(struct sk_buff *skb, struct sw_flow_key *key){
++ struct encap_eth_hdr *encap_eth_header = (struct encap_eth_hdr *)skb->data;
++ OVS_CB(skb)->encap_eth_header = encap_eth_header;
++ key->encap_eth.encap_eth_type = encap_eth_header->encap_eth_type;
++ ether_addr_copy(key->encap_eth.encap_eth_src, encap_eth_header->encap_eth_src);
++ ether_addr_copy(key->encap_eth.encap_eth_dst, encap_eth_header->encap_eth_dst);
++
++ return;
++}
++
+ static __be16 parse_ethertype(struct sk_buff *skb)
+ {
+ struct llc_snap_hdr {
+@@ -457,6 +484,24 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
+ int error;
+ struct ethhdr *eth;
+
++ /* Extract ethernet+nsh if ethernet type is 0x894F */
++ eth = (struct ethhdr *)skb->data;
++ if (eth->h_proto == htons(ETH_P_NSH)) {
++ parse_encap_eth(skb, key);
++ __skb_pull(skb, ENCAP_ETH_LEN);
++
++ if (unlikely(parse_nsh(skb, key)))
++ return -EINVAL;
++ if (key->nsh.nsh_mdtype == NSH_M_TYPE1 && key->nsh.nsh_np == NSH_P_ETHERNET) {
++ __skb_pull(skb, NSH_M_TYPE1_LEN);
++ } else {
++ return -EINVAL;
++ }
++ } else {
++ void *encap_eth_hdr = &(key->encap_eth);
++ memset(encap_eth_hdr, 0, ENCAP_ETH_LEN);
++ }
++
+ /* Flags are always used as part of stats */
+ key->tp.flags = 0;
+
+@@ -676,11 +721,54 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
+ }
+ }
+ }
++
++ if (key->encap_eth.encap_eth_type == htons(ETH_P_NSH))
++ __skb_push(skb, ENCAP_ETH_LEN + NSH_M_TYPE1_LEN);
++
++ return 0;
++}
++
++static void ovs_key_nsh_init(struct sw_flow_key *key, __u16 len) {
++ void *nsh_hdr = &(key->nsh);
++ memset(nsh_hdr, 0, len);
++}
++
++static int nsh_extract(struct sk_buff *skb, struct sw_flow_key *key) {
++ int ret;
++
++ /* only support NSH MD tpye1 */
++ if (unlikely(parse_nsh(skb, key)))
++ return -EINVAL;
++ if (key->nsh.nsh_mdtype == NSH_M_TYPE1) {
++ __skb_pull(skb, NSH_M_TYPE1_LEN);
++ if(key->nsh.nsh_np == NSH_P_ETHERNET)
++ ret = key_extract(skb, key);
++ else
++ return -EINVAL;
++ __skb_push(skb, NSH_M_TYPE1_LEN);
++
++ return ret;
++ } else {
++ ovs_key_nsh_init(key, NSH_M_TYPE1_LEN);
++ }
++
+ return 0;
+ }
+
++static bool is_nsh_header(const void *tun_opts, __be16 tun_flags) {
++ struct vxlan_metadata *md = NULL;
++ if (tun_opts && (tun_flags & TUNNEL_VXLAN_OPT))
++ md = (struct vxlan_metadata *)tun_opts;
++
++ if (md && (md->gpe & VXLAN_GPE_NP_MASK) == VXLAN_GPE_NP_NSH)
++ return true;
++ else
++ return false;
++}
++
+ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key)
+ {
++ ovs_key_nsh_init(key, NSH_M_TYPE1_LEN);
+ return key_extract(skb, key);
+ }
+
+@@ -715,6 +803,15 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
+ key->ovs_flow_hash = 0;
+ key->recirc_id = 0;
+
++ /* Extract NSH and inner Ethernet if NSH header exists */
++ if (tun_info && is_nsh_header(ip_tunnel_info_opts(tun_info), key->tun_key.tun_flags)) {
++ int ret ;
++ ret = nsh_extract(skb, key);
++ if (key->nsh.nsh_mdtype)
++ return ret;
++ } else {
++ ovs_key_nsh_init(key, NSH_M_TYPE1_LEN);
++ }
+ return key_extract(skb, key);
+ }
+
+@@ -729,5 +826,15 @@ int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr,
+ if (err)
+ return err;
+
++ /* Extract NSH and inner Ethernet if NSH header exists */
++ if (key && is_nsh_header(key->tun_opts, key->tun_key.tun_flags)) {
++ int ret ;
++ ret = nsh_extract(skb, key);
++ if (key->nsh.nsh_mdtype)
++ return ret;
++ } else {
++ ovs_key_nsh_init(key, NSH_M_TYPE1_LEN);
++ }
++
+ return key_extract(skb, key);
+ }
+diff --git a/datapath/flow.h b/datapath/flow.h
+index c0b628a..c8ccd5f 100644
+--- a/datapath/flow.h
++++ b/datapath/flow.h
+@@ -63,6 +63,21 @@ struct sw_flow_key {
+ u32 skb_mark; /* SKB mark. */
+ u16 in_port; /* Input switch port (or DP_MAX_PORTS). */
+ } __packed phy; /* Safe when right after 'tun_key'. */
++ struct {
++ u32 nshc1; /* NSH context C1-C4 */
++ u32 nshc2;
++ u32 nshc3;
++ u32 nshc4;
++ u32 nsp; /* NSH path id */
++ u8 nsi; /* NSH index */
++ u8 nsh_mdtype; /* NSH metadata type */
++ u8 nsh_np; /* NSH next protocol */
++ }__packed nsh; /* network service header */
++ struct {
++ u8 encap_eth_src[ETH_ALEN]; /* ENCAP ethernet source address. */
++ u8 encap_eth_dst[ETH_ALEN]; /* ENCAP ethernet destination address. */
++ u16 encap_eth_type; /* ENCAP ethernet type. */
++ } encap_eth;
+ u32 ovs_flow_hash; /* Datapath computed hash value. */
+ u32 recirc_id; /* Recirculation ID. */
+ struct {
+diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
+index 351a504..ebfae37 100644
+--- a/datapath/flow_netlink.c
++++ b/datapath/flow_netlink.c
+@@ -284,7 +284,7 @@ size_t ovs_key_attr_size(void)
+ /* Whenever adding new OVS_KEY_ FIELDS, we should consider
+ * updating this function.
+ */
+- BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 26);
++ BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 28);
+
+ return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */
+ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */
+@@ -297,6 +297,8 @@ size_t ovs_key_attr_size(void)
+ + nla_total_size(2) /* OVS_KEY_ATTR_CT_ZONE */
+ + nla_total_size(4) /* OVS_KEY_ATTR_CT_MARK */
+ + nla_total_size(16) /* OVS_KEY_ATTR_CT_LABELS */
++ + nla_total_size(24) /* OVS_KEY_ATTR_NSH */
++ + nla_total_size(14) /* OVS_KEY_ATTR_ENCAP_ETH */
+ + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */
+ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
+ + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */
+@@ -334,6 +336,8 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
+ [OVS_KEY_ATTR_PRIORITY] = { .len = sizeof(u32) },
+ [OVS_KEY_ATTR_IN_PORT] = { .len = sizeof(u32) },
+ [OVS_KEY_ATTR_SKB_MARK] = { .len = sizeof(u32) },
++ [OVS_KEY_ATTR_NSH] = { .len = sizeof(struct ovs_key_nsh) },
++ [OVS_KEY_ATTR_ENCAP_ETH] = { .len = sizeof(struct ovs_key_encap_eth) },
+ [OVS_KEY_ATTR_ETHERNET] = { .len = sizeof(struct ovs_key_ethernet) },
+ [OVS_KEY_ATTR_VLAN] = { .len = sizeof(__be16) },
+ [OVS_KEY_ATTR_ETHERTYPE] = { .len = sizeof(__be16) },
+@@ -869,6 +873,42 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
+ if (err)
+ return err;
+
++ if (attrs & (1ULL << OVS_KEY_ATTR_NSH)) {
++ const struct ovs_key_nsh *nsh_key;
++
++ nsh_key = nla_data(a[OVS_KEY_ATTR_NSH]);
++ SW_FLOW_KEY_PUT(match, nsh.nshc1,
++ nsh_key->nshc1, is_mask);
++ SW_FLOW_KEY_PUT(match, nsh.nshc2,
++ nsh_key->nshc2, is_mask);
++ SW_FLOW_KEY_PUT(match, nsh.nshc3,
++ nsh_key->nshc3, is_mask);
++ SW_FLOW_KEY_PUT(match, nsh.nshc4,
++ nsh_key->nshc4, is_mask);
++ SW_FLOW_KEY_PUT(match, nsh.nsh_mdtype,
++ nsh_key->nsh_mdtype, is_mask);
++ SW_FLOW_KEY_PUT(match, nsh.nsh_np,
++ nsh_key->nsh_np, is_mask);
++ SW_FLOW_KEY_PUT(match, nsh.nsp,
++ nsh_key->nsp, is_mask);
++ SW_FLOW_KEY_PUT(match, nsh.nsi,
++ nsh_key->nsi, is_mask);
++ attrs &= ~(1ULL << OVS_KEY_ATTR_NSH);
++ }
++
++ if (attrs & (1ULL << OVS_KEY_ATTR_ENCAP_ETH)) {
++ const struct ovs_key_encap_eth *encap_eth_key;
++
++ encap_eth_key = nla_data(a[OVS_KEY_ATTR_ENCAP_ETH]);
++ SW_FLOW_KEY_MEMCPY(match, encap_eth.encap_eth_src,
++ encap_eth_key->encap_eth_src, ETH_ALEN, is_mask);
++ SW_FLOW_KEY_MEMCPY(match, encap_eth.encap_eth_dst,
++ encap_eth_key->encap_eth_dst, ETH_ALEN, is_mask);
++ SW_FLOW_KEY_PUT(match, encap_eth.encap_eth_type,
++ encap_eth_key->encap_eth_type, is_mask);
++ attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP_ETH);
++ }
++
+ if (attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) {
+ const struct ovs_key_ethernet *eth_key;
+
+@@ -1386,6 +1426,35 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
+ if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
+ goto nla_put_failure;
+
++ if ((swkey->nsh.nsh_mdtype || is_mask)) {
++ struct ovs_key_nsh *nsh_key;
++
++ nla = nla_reserve(skb, OVS_KEY_ATTR_NSH, sizeof(*nsh_key));
++ if (!nla)
++ goto nla_put_failure;
++ nsh_key = nla_data(nla);
++ nsh_key->nsi = output->nsh.nsi;
++ nsh_key->nsp = output->nsh.nsp;
++ nsh_key->nsh_mdtype= output->nsh.nsh_mdtype;
++ nsh_key->nsh_np= output->nsh.nsh_np;
++ nsh_key->nshc1= output->nsh.nshc1;
++ nsh_key->nshc2 = output->nsh.nshc2;
++ nsh_key->nshc3 = output->nsh.nshc3;
++ nsh_key->nshc4 = output->nsh.nshc4;
++ }
++
++ if ((swkey->encap_eth.encap_eth_type || is_mask)) {
++ struct ovs_key_encap_eth *encap_eth_key;
++
++ nla = nla_reserve(skb, OVS_KEY_ATTR_ENCAP_ETH, sizeof(*encap_eth_key));
++ if (!nla)
++ goto nla_put_failure;
++ encap_eth_key = nla_data(nla);
++ memcpy(encap_eth_key->encap_eth_src, output->encap_eth.encap_eth_src, ETH_ALEN);
++ memcpy(encap_eth_key->encap_eth_dst, output->encap_eth.encap_eth_dst, ETH_ALEN);
++ encap_eth_key->encap_eth_type= output->encap_eth.encap_eth_type;
++ }
++
+ if ((swkey->tun_key.u.ipv4.dst || is_mask)) {
+ const void *opts = NULL;
+
+@@ -2182,6 +2251,10 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
+ [OVS_ACTION_ATTR_POP_MPLS] = sizeof(__be16),
+ [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
+ [OVS_ACTION_ATTR_POP_VLAN] = 0,
++ [OVS_ACTION_ATTR_PUSH_NSH] = sizeof(struct ovs_action_push_nsh),
++ [OVS_ACTION_ATTR_POP_NSH] = 0,
++ [OVS_ACTION_ATTR_PUSH_ETH] = sizeof(struct ovs_action_push_eth),
++ [OVS_ACTION_ATTR_POP_ETH] = 0,
+ [OVS_ACTION_ATTR_SET] = (u32)-1,
+ [OVS_ACTION_ATTR_SET_MASKED] = (u32)-1,
+ [OVS_ACTION_ATTR_SAMPLE] = (u32)-1,
+@@ -2226,6 +2299,12 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
+ break;
+ }
+
++ case OVS_ACTION_ATTR_PUSH_NSH:
++ case OVS_ACTION_ATTR_POP_NSH:
++ case OVS_ACTION_ATTR_PUSH_ETH:
++ case OVS_ACTION_ATTR_POP_ETH:
++ break;
++
+ case OVS_ACTION_ATTR_POP_VLAN:
+ vlan_tci = htons(0);
+ break;
+diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
+index 44adb81..187bb9b 100644
+--- a/datapath/linux/compat/include/linux/openvswitch.h
++++ b/datapath/linux/compat/include/linux/openvswitch.h
+@@ -42,6 +42,7 @@
+
+ #include
+ #include
++#include
+
+ /**
+ * struct ovs_header - header for OVS Generic Netlink messages.
+@@ -328,6 +329,9 @@ enum ovs_key_attr {
+ OVS_KEY_ATTR_ENCAP, /* Nested set of encapsulated attributes. */
+ OVS_KEY_ATTR_PRIORITY, /* u32 skb->priority */
+ OVS_KEY_ATTR_IN_PORT, /* u32 OVS dp port number */
++ OVS_KEY_ATTR_NSH, /* struct ovs_key_nsh. Only support NSH MD
++ type 1 now */
++ OVS_KEY_ATTR_ENCAP_ETH, /* struct ovs_key_encap_eth */
+ OVS_KEY_ATTR_ETHERNET, /* struct ovs_key_ethernet */
+ OVS_KEY_ATTR_VLAN, /* be16 VLAN TCI */
+ OVS_KEY_ATTR_ETHERTYPE, /* be16 Ethernet type */
+@@ -401,6 +405,24 @@ enum ovs_frag_type {
+
+ #define OVS_FRAG_TYPE_MAX (__OVS_FRAG_TYPE_MAX - 1)
+
++struct ovs_key_nsh {
++ __u32 nshc1;
++ __u32 nshc2;
++ __u32 nshc3;
++ __u32 nshc4;
++ __u32 nsp;
++ __u8 nsi;
++ __u8 nsh_mdtype;
++ __u8 nsh_np;
++ __u8 reserved;
++};
++
++struct ovs_key_encap_eth {
++ __u8 encap_eth_src[ETH_ALEN];
++ __u8 encap_eth_dst[ETH_ALEN];
++ __u16 encap_eth_type;
++};
++
+ struct ovs_key_ethernet {
+ __u8 eth_src[ETH_ALEN];
+ __u8 eth_dst[ETH_ALEN];
+@@ -630,6 +652,154 @@ struct ovs_action_push_vlan {
+ __be16 vlan_tci; /* 802.1Q TCI (VLAN ID and priority). */
+ };
+
++/*
++ * Network Service Header:
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * |Ver|O|C|R|R|R|R|R|R| Length | MD Type | Next Proto |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | Service Path ID | Service Index |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * Ver = The version field is used to ensure backward compatibility
++ * going forward with future NSH updates. It MUST be set to 0x0
++ * by the sender, in this first revision of NSH.
++ *
++ * O = OAM. when set to 0x1 indicates that this packet is an operations
++ * and management (OAM) packet. The receiving SFF and SFs nodes
++ * MUST examine the payload and take appropriate action.
++ *
++ * C = context. Indicates that a critical metadata TLV is present.
++ *
++ * Length : total length, in 4-byte words, of NSH including the Base
++ * Header, the Service Path Header and the optional variable
++ * TLVs.
++ * MD Type: indicates the format of NSH beyond the mandatory Base Header
++ * and the Service Path Header.
++ *
++ * Next Protocol: indicates the protocol type of the original packet. A
++ * new IANA registry will be created for protocol type.
++ *
++ * Service Path Identifier (SPI): identifies a service path.
++ * Participating nodes MUST use this identifier for Service
++ * Function Path selection.
++ *
++ * Service Index (SI): provides location within the SFP.
++ *
++ * [0] https://tools.ietf.org/html/draft-ietf-sfc-nsh-01
++ */
++
++struct nsh_base {
++#if defined(__LITTLE_ENDIAN_BITFIELD)
++
++ __u8 reserved_flags1:4;
++ __u8 context_flag:1;
++ __u8 oam_flag:1;
++ __u8 version:2;
++
++ __u8 length:6;
++ __u8 reserved_flags2:2;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++
++ __u8 version:2;
++ __u8 oam_flag:1;
++ __u8 context_flag:1;
++ __u8 reserved_flags1:4;
++
++ __u8 reserved_flags2:2;
++ __u8 length:6;
++#else
++#error "Bitfield Endianess not defined."
++#endif
++ __u8 mdtype;
++ __u8 proto;
++ union {
++ struct {
++ __u8 svc_path[3];
++ __u8 svc_idx;
++ };
++ __be32 path_hdr;
++ };
++};
++
++
++/**
++ * struct nsh_ctx - Keeps track of NSH context data
++ * @c<1-4>: NSH Contexts.
++ */
++struct nsh_ctx {
++ __be32 nshc1;
++ __be32 nshc2;
++ __be32 nshc3;
++ __be32 nshc4;
++};
++
++/**
++ * struct nshdr - Network Service header
++ * @nsh_base: Network Service Base Header.
++ * @nsh_ctx: Network Service Context Header.
++ */
++struct nsh_hdr {
++ struct nsh_base base;
++ struct nsh_ctx ctx;
++};
++
++
++#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_EXP1 0xFE
++#define NSH_M_EXP2 0xFF
++
++#define NSH_DST_PORT 4790 /* UDP Port for NSH on VXLAN */
++
++#define NSH_M_TYPE1_LEN 24
++
++/* Used for masking nsp and nsi values in field nsp below */
++#define NSH_M_NSP 0x00FFFFFF
++#define NSH_M_NSI 0xFF000000
++
++/* NSH header for MD type 1. Unit is bytes */
++#define NSH_PUSH_TYPE1_HEADER_SIZE 24
++#define NSH_PUSH_HEADER_SIZE 256
++
++#define ENCAP_ETH_LEN 14
++
++/**
++ * struct encap_eth - encap ethernet header for ethernet NSH
++ * @encap_eth_src: encap ethernet source address.
++ * @encap_eth_src: encap ethernet destination address.
++ * @encap_eth_type: encap ethernet type.
++ */
++struct encap_eth_hdr {
++ __u8 encap_eth_dst[ETH_ALEN];
++ __u8 encap_eth_src[ETH_ALEN];
++ __u16 encap_eth_type;
++};
++
++#define ENCAP_ETH_PUSH_HEADER_SIZE 14
++/**
++ * struct ovs_action_push_nsh - %OVS_ACTION_ATTR_PUSH_NSH action argument.
++ * @header
++ */
++struct ovs_action_push_nsh {
++ uint8_t nsh_mdtype;
++ uint8_t header[NSH_PUSH_HEADER_SIZE];
++};
++
++/**
++ * struct ovs_action_push_nsh - %OVS_ACTION_ATTR_PUSH_NSH action argument.
++ * @header
++ */
++struct ovs_action_push_eth {
++ uint16_t encap_eth_type;
++ uint8_t header[ENCAP_ETH_PUSH_HEADER_SIZE];
++};
++
+ /* Data path hash algorithm for computing Datapath hash.
+ *
+ * The algorithm type only specifies the fields in a flow
+@@ -793,6 +963,10 @@ enum ovs_action_attr {
+ OVS_ACTION_ATTR_SET, /* One nested OVS_KEY_ATTR_*. */
+ OVS_ACTION_ATTR_PUSH_VLAN, /* struct ovs_action_push_vlan. */
+ OVS_ACTION_ATTR_POP_VLAN, /* No argument. */
++ OVS_ACTION_ATTR_PUSH_NSH, /* struct ovs_action_push_nsh. */
++ OVS_ACTION_ATTR_POP_NSH, /* No argument. */
++ OVS_ACTION_ATTR_PUSH_ETH, /* struct ovs_action_push_eth. */
++ OVS_ACTION_ATTR_POP_ETH, /* No argument. */
+ OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */
+ OVS_ACTION_ATTR_RECIRC, /* u32 recirc_id. */
+ OVS_ACTION_ATTR_HASH, /* struct ovs_action_hash. */
+diff --git a/datapath/linux/compat/include/net/vxlan.h b/datapath/linux/compat/include/net/vxlan.h
+index 2bfc3f8..3aeac88 100644
+--- a/datapath/linux/compat/include/net/vxlan.h
++++ b/datapath/linux/compat/include/net/vxlan.h
+@@ -85,7 +85,7 @@ struct vxlanhdr_gbp {
+ #define VXLAN_GBP_ID_MASK (0xFFFF)
+
+ /*
+- * VXLAN Generic Protocol Extension Extension:
++ * VXLAN Generic Protocol Extension:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|R|Ver|I|P|R|O|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R| Next Proto |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+@@ -103,6 +103,9 @@ struct vxlanhdr_gbp {
+ * O = OAM Flag Bit. The O bit is set to indicate that the packet
+ * is an OAM packet.
+ *
++ * Next Protocol = This 8 bit field indicates the protocol header
++ * immediately following the VXLAN GPE header.
++ *
+ * [1] https://www.ietf.org/id/draft-ietf-nvo3-vxlan-gpe-01.txt
+ */
+
+diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
+index 119dc2d..8e67bfd 100644
+--- a/lib/dpif-netdev.c
++++ b/lib/dpif-netdev.c
+@@ -3874,6 +3874,10 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
+ VLOG_WARN("Cannot execute conntrack action in userspace.");
+ break;
+
++ case OVS_ACTION_ATTR_PUSH_NSH:
++ case OVS_ACTION_ATTR_POP_NSH:
++ case OVS_ACTION_ATTR_PUSH_ETH:
++ case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_PUSH_MPLS:
+diff --git a/lib/dpif.c b/lib/dpif.c
+index a784de7..b5265c4 100644
+--- a/lib/dpif.c
++++ b/lib/dpif.c
+@@ -1139,6 +1139,10 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt,
+ }
+
+ case OVS_ACTION_ATTR_HASH:
++ case OVS_ACTION_ATTR_PUSH_NSH:
++ case OVS_ACTION_ATTR_POP_NSH:
++ case OVS_ACTION_ATTR_PUSH_ETH:
++ case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_PUSH_MPLS:
+diff --git a/lib/flow.c b/lib/flow.c
+index d24bdc9..fc8b98c 100644
+--- a/lib/flow.c
++++ b/lib/flow.c
+@@ -1289,6 +1289,25 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
+ WC_MASK_FIELD(wc, tunnel.tun_id);
+ }
+
++ /* NSH fields wildcarded */
++ if (flow->nsh_mdtype) {
++ WC_MASK_FIELD(wc, nshc1);
++ WC_MASK_FIELD(wc, nshc2);
++ WC_MASK_FIELD(wc, nshc3);
++ WC_MASK_FIELD(wc, nshc4);
++ WC_MASK_FIELD(wc, nsh_mdtype);
++ WC_MASK_FIELD(wc, nsh_np);
++ WC_MASK_FIELD(wc, nsp);
++ WC_MASK_FIELD(wc, nsi);
++ }
++
++ /* ENCAP Eth wildcarded */
++ if (flow->encap_eth_type) {
++ WC_MASK_FIELD(wc, encap_eth_src);
++ WC_MASK_FIELD(wc, encap_eth_dst);
++ WC_MASK_FIELD(wc, encap_eth_type);
++ }
++
+ /* metadata, regs, and conj_id wildcarded. */
+
+ WC_MASK_FIELD(wc, skb_priority);
+diff --git a/lib/flow.h b/lib/flow.h
+index dc7130d..a8677b1 100644
+--- a/lib/flow.h
++++ b/lib/flow.h
+@@ -111,6 +111,23 @@ struct flow {
+ ofp_port_t actset_output; /* Output port in action set. */
+ uint8_t pad2[2]; /* Pad to 64 bits. */
+
++ /* NSH (64-bit aligned) */
++ ovs_be32 nshc1;
++ ovs_be32 nshc2;
++ ovs_be32 nshc3;
++ ovs_be32 nshc4;
++ ovs_be32 nsp;
++ uint8_t nsi;
++ uint8_t nsh_mdtype;
++ uint8_t nsh_np;
++ uint8_t pad3;
++
++ /* ENCAP_ETH (64-bit aligned) */
++ struct eth_addr encap_eth_src; /* Encap ethernet source address. */
++ struct eth_addr encap_eth_dst; /* Encap ethernet destination address. */
++ ovs_be16 encap_eth_type; /* Encap ethernet frame type. */
++ uint8_t pad4[2];
++
+ /* L2, Order the same as in the Ethernet header! (64-bit aligned) */
+ struct eth_addr dl_dst; /* Ethernet destination address. */
+ struct eth_addr dl_src; /* Ethernet source address. */
+@@ -132,7 +149,7 @@ struct flow {
+ struct eth_addr arp_sha; /* ARP/ND source hardware address. */
+ struct eth_addr arp_tha; /* ARP/ND target hardware address. */
+ ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */
+- ovs_be16 pad3; /* Pad to 64 bits. */
++ ovs_be16 pad5; /* Pad to 64 bits. */
+
+ /* L4 (64-bit aligned) */
+ ovs_be16 tp_src; /* TCP/UDP/SCTP source port/ICMP type. */
+@@ -158,7 +175,7 @@ BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0);
+
+ /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
+ BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
+- == sizeof(struct flow_tnl) + 216
++ == sizeof(struct flow_tnl) + 256
+ && FLOW_WC_SEQ == 35);
+
+ /* Incremental points at which flow classification may be performed in
+diff --git a/lib/match.c b/lib/match.c
+index 52437c9..3db2a2b 100644
+--- a/lib/match.c
++++ b/lib/match.c
+@@ -862,6 +862,152 @@ 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.nsp = mask;
++ match->flow.nsp = nsp & mask;
++}
++
++void
++match_set_nsi_masked(struct match *match, uint8_t nsi, uint8_t mask)
++{
++ match->wc.masks.nsi = mask;
++ match->flow.nsi = nsi & mask;
++}
++
++void
++match_set_nsh_mdtype_masked(struct match *match, uint8_t nsh_mdtype, uint8_t mask)
++{
++ match->wc.masks.nsh_mdtype = mask;
++ match->flow.nsh_mdtype = nsh_mdtype & mask;
++}
++
++void
++match_set_nsh_np_masked(struct match *match, uint8_t nsh_np, uint8_t mask)
++{
++ match->wc.masks.nsh_np = mask;
++ match->flow.nsh_np = nsh_np & mask;
++}
++
++void
++match_set_encap_eth_src_masked(struct match *match,
++ const struct eth_addr encap_eth_src,
++ const struct eth_addr mask)
++{
++ set_eth_masked(encap_eth_src, mask, &match->flow.encap_eth_src, &match->wc.masks.encap_eth_src);
++}
++
++void
++match_set_encap_eth_dst_masked(struct match *match,
++ const struct eth_addr encap_eth_dst,
++ const struct eth_addr mask)
++{
++ set_eth_masked(encap_eth_dst, mask, &match->flow.encap_eth_dst, &match->wc.masks.encap_eth_dst);
++}
++
++void
++match_set_encap_eth_type_masked(struct match *match, ovs_be16 encap_eth_type, ovs_be16 mask)
++{
++ match->wc.masks.encap_eth_type = mask;
++ match->flow.encap_eth_type = encap_eth_type & mask;
++}
++
++void
++match_set_nshc1_masked(struct match *match, ovs_be32 nshc1, ovs_be32 mask)
++{
++ match->wc.masks.nshc1 = mask;
++ match->flow.nshc1 = nshc1 & mask;
++}
++
++void
++match_set_nshc2_masked(struct match *match, ovs_be32 nshc2, ovs_be32 mask)
++{
++ match->wc.masks.nshc2 = mask;
++ match->flow.nshc2 = nshc2 & mask;
++}
++
++void
++match_set_nshc3_masked(struct match *match, ovs_be32 nshc3, ovs_be32 mask)
++{
++ match->wc.masks.nshc3 = mask;
++ match->flow.nshc3 = nshc3 & mask;
++}
++
++void
++match_set_nshc4_masked(struct match *match, ovs_be32 nshc4, ovs_be32 mask)
++{
++ match->wc.masks.nshc4 = mask;
++ match->flow.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_nsh_mdtype(struct match *match, uint8_t nsh_mdtype)
++{
++ match_set_nsh_mdtype_masked(match, nsh_mdtype, UINT8_MAX);
++}
++
++void
++match_set_nsh_np(struct match *match, uint8_t nsh_np)
++{
++ match_set_nsh_np_masked(match, nsh_np, 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);
++}
++
++void
++match_set_encap_eth_src(struct match *match, const struct eth_addr encap_eth_src)
++{
++ set_eth(encap_eth_src, &match->flow.encap_eth_src, &match->wc.masks.encap_eth_src);
++}
++
++void
++match_set_encap_eth_dst(struct match *match, const struct eth_addr encap_eth_dst)
++{
++ set_eth(encap_eth_dst, &match->flow.encap_eth_dst, &match->wc.masks.encap_eth_dst);
++}
++
++void
++match_set_encap_eth_type(struct match *match, uint16_t encap_eth_type)
++{
++ match_set_encap_eth_type_masked(match, encap_eth_type, UINT16_MAX);
++}
++
++
+ /* Returns true if 'a' and 'b' wildcard the same fields and have the same
+ * values for fixed fields, otherwise false. */
+ bool
+@@ -1155,6 +1301,50 @@ match_format(const struct match *match, struct ds *s, int priority)
+ format_ct_label_masked(s, &f->ct_label, &wc->masks.ct_label);
+ }
+
++ if (wc->masks.nsi) {
++ ds_put_format(s, "nsi=%"PRIu8",", f->nsi);
++ }
++
++ if (wc->masks.nsh_mdtype) {
++ ds_put_format(s, "nsh_mdtype=%"PRIu8",", f->nsh_mdtype);
++ }
++
++ if (wc->masks.nsh_np) {
++ ds_put_format(s, "nsh_np=%"PRIu8",", f->nsh_np);
++ }
++
++ if (wc->masks.nsp) {
++ format_be32_masked(s, "nsp", f->nsp,
++ wc->masks.nsp);
++ }
++
++ if (wc->masks.nshc1) {
++ format_be32_masked(s, "nshc1", f->nshc1,
++ wc->masks.nshc1);
++ }
++
++ if (wc->masks.nshc2) {
++ format_be32_masked(s, "nshc2", f->nshc2,
++ wc->masks.nshc2);
++ }
++
++ if (wc->masks.nshc3) {
++ format_be32_masked(s, "nshc3", f->nshc3,
++ wc->masks.nshc3);
++ }
++
++ if (wc->masks.nshc4) {
++ format_be32_masked(s, "nshc4", f->nshc4,
++ wc->masks.nshc4);
++ }
++
++ if (wc->masks.encap_eth_type) {
++ ds_put_format(s, "encap_eth_type=%"PRIu16",", f->encap_eth_type);
++ }
++
++ format_eth_masked(s, "encap_eth_src", f->encap_eth_src, wc->masks.encap_eth_src);
++ format_eth_masked(s, "encap_eth_dst", f->encap_eth_dst, wc->masks.encap_eth_dst);
++
+ if (wc->masks.dl_type) {
+ skip_type = true;
+ if (f->dl_type == htons(ETH_TYPE_IP)) {
+diff --git a/lib/match.h b/lib/match.h
+index 48aa0b1..529350e 100644
+--- a/lib/match.h
++++ b/lib/match.h
+@@ -165,6 +165,30 @@ 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_nsh_mdtype_masked(struct match *match, uint8_t nsh_mdtype, uint8_t mask);
++void match_set_nsh_np_masked(struct match *match, uint8_t nsh_np, 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_encap_eth_src_masked(struct match *match, const struct eth_addr encap_eth_src, const struct eth_addr mask);
++void match_set_encap_eth_dst_masked(struct match *match, const struct eth_addr encap_eth_dst, const struct eth_addr mask);
++void match_set_encap_eth_type_masked(struct match *match, ovs_be16 encap_eth_type, ovs_be16 mask);
++
++void match_set_nsp(struct match *, ovs_be32 nsp);
++void match_set_nsi(struct match *match, uint8_t nsi);
++void match_set_nsh_mdtype(struct match *match, uint8_t nsh_mdtype);
++void match_set_nsh_np(struct match *match, uint8_t nsh_np);
++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);
++void match_set_encap_eth_src(struct match *match, const struct eth_addr encap_eth_src);
++void match_set_encap_eth_dst(struct match *match, const struct eth_addr encap_eth_dst);
++void match_set_encap_eth_type(struct match *match, uint16_t encap_eth_type);
++
+ 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 ab77fca..648a753 100644
+--- a/lib/meta-flow.c
++++ b/lib/meta-flow.c
+@@ -243,7 +243,28 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
+ return !flow_get_xreg(&wc->masks, mf->id - MFF_XREG0);
+ case MFF_ACTSET_OUTPUT:
+ return !wc->masks.actset_output;
+-
++ case MFF_NSP:
++ return !wc->masks.nsp;
++ case MFF_NSI:
++ return !wc->masks.nsi;
++ case MFF_NSH_C1:
++ return !wc->masks.nshc1;
++ case MFF_NSH_C2:
++ return !wc->masks.nshc2;
++ case MFF_NSH_C3:
++ return !wc->masks.nshc3;
++ case MFF_NSH_C4:
++ return !wc->masks.nshc4;
++ case MFF_NSH_MDTYPE:
++ return !wc->masks.nsh_mdtype;
++ case MFF_NSH_NP:
++ return !wc->masks.nsh_np;
++ case MFF_ENCAP_ETH_SRC:
++ return eth_addr_is_zero(wc->masks.encap_eth_src);
++ case MFF_ENCAP_ETH_DST:
++ return eth_addr_is_zero(wc->masks.encap_eth_dst);
++ case MFF_ENCAP_ETH_TYPE:
++ return !wc->masks.encap_eth_type;
+ case MFF_ETH_SRC:
+ return eth_addr_is_zero(wc->masks.dl_src);
+ case MFF_ETH_DST:
+@@ -557,6 +578,17 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
+ case MFF_ICMPV6_TYPE:
+ case MFF_ICMPV6_CODE:
+ case MFF_ND_TARGET:
++ case MFF_NSP:
++ case MFF_NSI:
++ case MFF_NSH_MDTYPE:
++ case MFF_NSH_NP:
++ case MFF_NSH_C1:
++ case MFF_NSH_C2:
++ case MFF_NSH_C3:
++ case MFF_NSH_C4:
++ case MFF_ENCAP_ETH_SRC:
++ case MFF_ENCAP_ETH_DST:
++ case MFF_ENCAP_ETH_TYPE:
+ case MFF_ND_SLL:
+ case MFF_ND_TLL:
+ return true;
+@@ -716,6 +748,50 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
+ value->be64 = htonll(flow_get_xreg(flow, mf->id - MFF_XREG0));
+ break;
+
++ case MFF_NSP:
++ value->be32 = flow->nsp;
++ break;
++
++ case MFF_NSI:
++ value->u8 = flow->nsi;
++ break;
++
++ case MFF_NSH_C1:
++ value->be32 = flow->nshc1;
++ break;
++
++ case MFF_NSH_C2:
++ value->be32 = flow->nshc2;
++ break;
++
++ case MFF_NSH_C3:
++ value->be32 = flow->nshc3;
++ break;
++
++ case MFF_NSH_C4:
++ value->be32 = flow->nshc4;
++ break;
++
++ case MFF_NSH_MDTYPE:
++ value->u8 = flow->nsh_mdtype;
++ break;
++
++ case MFF_NSH_NP:
++ value->u8 = flow->nsh_np;
++ break;
++
++ case MFF_ENCAP_ETH_SRC:
++ value->mac = flow->encap_eth_src;
++ break;
++
++ case MFF_ENCAP_ETH_DST:
++ value->mac = flow->encap_eth_dst;
++ break;
++
++ case MFF_ENCAP_ETH_TYPE:
++ value->be16 = flow->encap_eth_type;
++ break;
++
+ case MFF_ETH_SRC:
+ value->mac = flow->dl_src;
+ break;
+@@ -1085,6 +1161,50 @@ mf_set_value(const struct mf_field *mf,
+ match_set_arp_sha(match, value->mac);
+ break;
+
++ case MFF_NSP:
++ match_set_nsp(match, value->be32);
++ break;
++
++ case MFF_NSI:
++ match_set_nsi(match, value->u8);
++ break;
++
++ case MFF_NSH_MDTYPE:
++ match_set_nsh_mdtype(match, value->u8);
++ break;
++
++ case MFF_NSH_NP:
++ match_set_nsh_np(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_ENCAP_ETH_SRC:
++ match_set_encap_eth_src(match, value->mac);
++ break;
++
++ case MFF_ENCAP_ETH_DST:
++ match_set_encap_eth_dst(match, value->mac);
++ break;
++
++ case MFF_ENCAP_ETH_TYPE:
++ match_set_encap_eth_type(match, value->be16);
++ break;
++
+ case MFF_ARP_THA:
+ case MFF_ND_TLL:
+ match_set_arp_tha(match, value->mac);
+@@ -1296,6 +1416,50 @@ mf_set_flow_value(const struct mf_field *mf,
+ flow_set_xreg(flow, mf->id - MFF_XREG0, ntohll(value->be64));
+ break;
+
++ case MFF_NSP:
++ flow->nsp = value->be32;
++ break;
++
++ case MFF_NSI:
++ flow->nsi = value->u8;
++ break;
++
++ case MFF_NSH_C1:
++ flow->nshc1 = value->be32;
++ break;
++
++ case MFF_NSH_C2:
++ flow->nshc2 = value->be32;
++ break;
++
++ case MFF_NSH_C3:
++ flow->nshc3 = value->be32;
++ break;
++
++ case MFF_NSH_C4:
++ flow->nshc4 = value->be32;
++ break;
++
++ case MFF_NSH_MDTYPE:
++ flow->nsh_mdtype = value->u8;
++ break;
++
++ case MFF_NSH_NP:
++ flow->nsh_np = value->u8;
++ break;
++
++ case MFF_ENCAP_ETH_SRC:
++ flow->encap_eth_src = value->mac;
++ break;
++
++ case MFF_ENCAP_ETH_DST:
++ flow->encap_eth_dst = value->mac;
++ break;
++
++ case MFF_ENCAP_ETH_TYPE:
++ flow->encap_eth_type = value->be16;
++ break;
++
+ case MFF_ETH_SRC:
+ flow->dl_src = value->mac;
+ break;
+@@ -1734,6 +1898,52 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
+ match->wc.masks.arp_sha = eth_addr_zero;
+ 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_MDTYPE:
++ match_set_nsh_mdtype_masked(match, 0, 0);
++ break;
++
++ case MFF_NSH_NP:
++ match_set_nsh_np_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_ENCAP_ETH_SRC:
++ match->flow.encap_eth_src= eth_addr_zero;
++ match->wc.masks.encap_eth_src = eth_addr_zero;
++ break;
++
++ case MFF_ENCAP_ETH_DST:
++ match->flow.encap_eth_dst= eth_addr_zero;
++ match->wc.masks.encap_eth_dst = eth_addr_zero;
++ break;
++
++ case MFF_ENCAP_ETH_TYPE:
++ match_set_encap_eth_type_masked(match, htons(0), htons(0));
++ break;
++
+ case MFF_ARP_THA:
+ case MFF_ND_TLL:
+ match->flow.arp_tha = eth_addr_zero;
+@@ -1929,6 +2139,50 @@ mf_set(const struct mf_field *mf,
+ match_set_arp_sha_masked(match, value->mac, mask->mac);
+ 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_MDTYPE:
++ match_set_nsh_mdtype_masked(match, value->u8, mask->u8);
++ break;
++
++ case MFF_NSH_NP:
++ match_set_nsh_np_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_ENCAP_ETH_SRC:
++ match_set_encap_eth_src_masked(match, value->mac, mask->mac);
++ break;
++
++ case MFF_ENCAP_ETH_DST:
++ match_set_encap_eth_dst_masked(match, value->mac, mask->mac);
++ break;
++
++ case MFF_ENCAP_ETH_TYPE:
++ match_set_encap_eth_type_masked(match, value->be16, mask->be16);
++ break;
++
+ case MFF_ARP_THA:
+ case MFF_ND_TLL:
+ match_set_arp_tha_masked(match, value->mac, mask->mac);
+diff --git a/lib/meta-flow.h b/lib/meta-flow.h
+index 4bd9ff6..a226b79 100644
+--- a/lib/meta-flow.h
++++ b/lib/meta-flow.h
+@@ -1747,6 +1747,180 @@ enum OVS_PACKED_ENUM mf_field_id {
+ */
+ MFF_ND_TLL,
+
++ /* "nsp".
++ *
++ * For a packet received 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(113) since v1.1.
++ * OXM: none.
++ * Prefix lookup member: nsp.
++ */
++ MFF_NSP,
++
++ /* "nsi".
++ *
++ * For a packet received, 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(114) since v1.1.
++ * OXM: none.
++ * Prefix lookup member: nsi.
++ */
++ MFF_NSI,
++
++ /* "nshc1".
++ *
++ * For a packet received 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(115) since v1.1.
++ * OXM: none.
++ * Prefix lookup member: nshc1.
++ */
++ MFF_NSH_C1,
++
++ /* "nshc2".
++ *
++ * For a packet received 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(116) since v1.1.
++ * OXM: none.
++ * Prefix lookup member: nshc2.
++ */
++ MFF_NSH_C2,
++
++ /* "nshc3".
++ *
++ * For a packet received via 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(117) since v1.1.
++ * OXM: none.
++ * Prefix lookup member: nshc3.
++ */
++ MFF_NSH_C3,
++
++ /* "nshc4".
++ *
++ * For a packet received 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(118) since v1.1.
++ * OXM: none.
++ * Prefix lookup member: nshc4.
++ */
++ MFF_NSH_C4,
++
++ /* "nsh_mdtype".
++ *
++ * For a packet received, it includes a (8-bit)
++ * nsh md-type field (md-type).
++ *
++ * Type: u8.
++ * Maskable: bitwise.
++ * Formatting: decimal.
++ * Prerequisites: none.
++ * Access: read/write.
++ * NXM: NXM_NX_NSH_MDTYPE(119) since v1.1.
++ * OXM: none.
++ */
++ MFF_NSH_MDTYPE,
++
++ /* "nsh_np".
++ *
++ * For a packet received, it includes a (8-bit)
++ * nsh next protocol field (np).
++ *
++ * Type: u8.
++ * Maskable: bitwise.
++ * Formatting: decimal.
++ * Prerequisites: none.
++ * Access: read/write.
++ * NXM: NXM_NX_NSH_NP(120) since v1.1.
++ * OXM: none.
++ */
++ MFF_NSH_NP,
++
++ /* "encap_eth_src".
++ *
++ * encap eth source address for Ethernet+NSH
++ *
++ * Type: MAC.
++ * Maskable: bitwise.
++ * Formatting: Ethernet.
++ * Prerequisites: none.
++ * Access: read/write.
++ * NXM: NXM_NX_ENCAP_ETH_SRC(121) since v1.1.
++ * OXM: none.
++ */
++ MFF_ENCAP_ETH_SRC,
++
++ /* "encap_eth_dst".
++ *
++ * encap eth destination address for Ethernet+NSH
++ *
++ * Type: MAC.
++ * Maskable: bitwise.
++ * Formatting: Ethernet.
++ * Prerequisites: none.
++ * Access: read/write.
++ * NXM: NXM_NX_ENCAP_ETH_DST(122) since v1.1.
++ * OXM: none.
++ */
++ MFF_ENCAP_ETH_DST,
++
++ /* "encap_eth_type".
++ *
++ * Encap Ethernet type.
++ *
++ * Type: be16.
++ * Maskable: no.
++ * Formatting: hexadecimal.
++ * Prerequisites: none.
++ * Access: read/write.
++ * NXM: NXM_NX_ENCAP_ETH_TYPE(123) since v1.1.
++ * OXM: none.
++ */
++ MFF_ENCAP_ETH_TYPE,
++
+ MFF_N_IDS
+ };
+
+diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
+index 92ceec1..406a492 100644
+--- a/lib/netdev-vport.c
++++ b/lib/netdev-vport.c
+@@ -582,9 +582,15 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
+ ext = strtok_r(str, ",", &save_ptr);
+ while (ext) {
+ if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) {
+- tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP);
++ if (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GPE))
++ VLOG_WARN("VXLAN_GPE extension exists, VxLAN_GBP extension can't be added.");
++ else
++ tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP);
+ } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) {
+- tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE);
++ if (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GBP))
++ VLOG_WARN("VXLAN_GBP extension exists, VxLAN_GPE extension can't be added.");
++ else
++ tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE);
+ } else {
+ VLOG_WARN("%s: unknown extension '%s'", name, ext);
+ }
+diff --git a/lib/nx-match.c b/lib/nx-match.c
+index 0eecac7..8d2bc4b 100644
+--- a/lib/nx-match.c
++++ b/lib/nx-match.c
+@@ -949,6 +949,25 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
+ ofputil_port_to_ofp11(flow->actset_output));
+ }
+
++ /* NSH */
++ nxm_put_32m(b, MFF_NSP, oxm, flow->nsp, match->wc.masks.nsp);
++ nxm_put_8m(b, MFF_NSI, oxm, flow->nsi, match->wc.masks.nsi);
++ nxm_put_8m(b, MFF_NSH_MDTYPE, oxm, flow->nsh_mdtype, match->wc.masks.nsh_mdtype);
++ nxm_put_8m(b, MFF_NSH_NP, oxm, flow->nsh_np, match->wc.masks.nsh_np);
++ nxm_put_32m(b, MFF_NSH_C1, oxm, flow->nshc1, match->wc.masks.nshc1);
++ nxm_put_32m(b, MFF_NSH_C2, oxm, flow->nshc2, match->wc.masks.nshc2);
++ nxm_put_32m(b, MFF_NSH_C3, oxm, flow->nshc3, match->wc.masks.nshc3);
++ nxm_put_32m(b, MFF_NSH_C4, oxm, flow->nshc4, match->wc.masks.nshc4);
++
++ /* ENCAP Eth */
++ nxm_put_eth_masked(b, MFF_ENCAP_ETH_SRC, oxm,
++ flow->encap_eth_src, match->wc.masks.encap_eth_src);
++ nxm_put_eth_masked(b, MFF_ENCAP_ETH_DST, oxm,
++ flow->encap_eth_dst, match->wc.masks.encap_eth_dst);
++ nxm_put_16m(b, MFF_ENCAP_ETH_TYPE, oxm,
++ ofputil_dl_type_to_openflow(flow->encap_eth_type),
++ match->wc.masks.encap_eth_type); //uncertain
++
+ /* Ethernet. */
+ nxm_put_eth_masked(b, MFF_ETH_SRC, oxm,
+ flow->dl_src, match->wc.masks.dl_src);
+diff --git a/lib/odp-execute.c b/lib/odp-execute.c
+index b5204b2..b6dcd98 100644
+--- a/lib/odp-execute.c
++++ b/lib/odp-execute.c
+@@ -434,6 +434,8 @@ odp_execute_masked_set_action(struct dp_packet *packet,
+ case OVS_KEY_ATTR_ETHERTYPE:
+ case OVS_KEY_ATTR_IN_PORT:
+ case OVS_KEY_ATTR_VLAN:
++ case OVS_KEY_ATTR_NSH:
++ case OVS_KEY_ATTR_ENCAP_ETH:
+ case OVS_KEY_ATTR_ICMP:
+ case OVS_KEY_ATTR_ICMPV6:
+ case OVS_KEY_ATTR_TCP_FLAGS:
+@@ -497,6 +499,10 @@ requires_datapath_assistance(const struct nlattr *a)
+
+ case OVS_ACTION_ATTR_SET:
+ case OVS_ACTION_ATTR_SET_MASKED:
++ case OVS_ACTION_ATTR_PUSH_NSH:
++ case OVS_ACTION_ATTR_POP_NSH:
++ case OVS_ACTION_ATTR_PUSH_ETH:
++ case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_SAMPLE:
+@@ -623,6 +629,10 @@ odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal,
+ }
+ break;
+
++ case OVS_ACTION_ATTR_PUSH_NSH:
++ case OVS_ACTION_ATTR_POP_NSH:
++ case OVS_ACTION_ATTR_PUSH_ETH:
++ case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_OUTPUT:
+ case OVS_ACTION_ATTR_TUNNEL_PUSH:
+ case OVS_ACTION_ATTR_TUNNEL_POP:
+diff --git a/lib/odp-util.c b/lib/odp-util.c
+index 7983720..102dfd7 100644
+--- a/lib/odp-util.c
++++ b/lib/odp-util.c
+@@ -70,7 +70,8 @@ static void format_odp_key_attr(const struct nlattr *a,
+ const struct nlattr *ma,
+ const struct hmap *portno_names, struct ds *ds,
+ bool verbose);
+-
++static void format_eth(struct ds *ds, const char *name, const struct eth_addr key,
++ const struct eth_addr *mask, bool verbose);
+ struct geneve_scan {
+ struct geneve_opt d[63];
+ int len;
+@@ -112,6 +113,10 @@ odp_action_len(uint16_t type)
+ case OVS_ACTION_ATTR_USERSPACE: return ATTR_LEN_VARIABLE;
+ case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
+ case OVS_ACTION_ATTR_POP_VLAN: return 0;
++ case OVS_ACTION_ATTR_PUSH_NSH: return sizeof(struct ovs_action_push_nsh);
++ case OVS_ACTION_ATTR_POP_NSH: return 0;
++ case OVS_ACTION_ATTR_PUSH_ETH: return sizeof(struct ovs_action_push_eth);
++ case OVS_ACTION_ATTR_POP_ETH: return 0;
+ case OVS_ACTION_ATTR_PUSH_MPLS: return sizeof(struct ovs_action_push_mpls);
+ case OVS_ACTION_ATTR_POP_MPLS: return sizeof(ovs_be16);
+ case OVS_ACTION_ATTR_RECIRC: return sizeof(uint32_t);
+@@ -147,6 +152,8 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
+ case OVS_KEY_ATTR_CT_LABELS: return "ct_label";
+ case OVS_KEY_ATTR_TUNNEL: return "tunnel";
+ case OVS_KEY_ATTR_IN_PORT: return "in_port";
++ case OVS_KEY_ATTR_ENCAP_ETH: return "encap_eth";
++ case OVS_KEY_ATTR_NSH: return "nsh";
+ case OVS_KEY_ATTR_ETHERNET: return "eth";
+ case OVS_KEY_ATTR_VLAN: return "vlan";
+ case OVS_KEY_ATTR_ETHERTYPE: return "eth_type";
+@@ -373,6 +380,36 @@ format_vlan_tci(struct ds *ds, ovs_be16 tci, ovs_be16 mask, bool verbose)
+ }
+
+ static void
++format_nsh(struct ds *ds, const struct ovs_action_push_nsh * nsh)
++{
++ const struct nsh_hdr *nsh_hdr =(struct nsh_hdr *)nsh->header;
++ ds_put_format(ds, "nsh_mdtype=%"PRIu8",nsh_np=%"PRIu8",nsp=%"PRIu32
++ ",nsi=%"PRIu8",nshc1=%"PRIu32",nshc2=%"PRIu32
++ ",nshc3=%"PRIu32",nshc4=%"PRIu32")",
++ nsh_hdr->base.mdtype,
++ nsh_hdr->base.proto,
++ ntohl(nsh_hdr->base.path_hdr << 8),
++ ntohl(nsh_hdr->base.path_hdr >> 24),
++ ntohl(nsh_hdr->ctx.nshc1),
++ ntohl(nsh_hdr->ctx.nshc2),
++ ntohl(nsh_hdr->ctx.nshc3),
++ ntohl(nsh_hdr->ctx.nshc4));
++}
++
++static void
++format_encap_eth(struct ds *ds, const struct ovs_action_push_eth *encap_eth)
++{
++ const struct encap_eth_hdr *encap_eth_hdr = (struct encap_eth_hdr *)encap_eth->header;
++ ds_put_format(ds, "encap_eth_type=%"PRIu16",",
++ ntohs(encap_eth_hdr->encap_eth_type));
++ format_eth(ds, "encap_eth_src", encap_eth_hdr->encap_eth_src,
++ NULL, true);
++ format_eth(ds, "encap_eth_dst", encap_eth_hdr->encap_eth_dst,
++ NULL, true);
++ ds_put_format(ds, ")");
++}
++
++static void
+ format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse)
+ {
+ ds_put_format(ds, "label=%"PRIu32",tc=%d,ttl=%d,bos=%d",
+@@ -500,7 +537,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
+ gnh->oam ? "oam," : "",
+ gnh->critical ? "crit," : "",
+ ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
+-
++
+ if (gnh->opt_len) {
+ ds_put_cstr(ds, ",options(");
+ format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
+@@ -760,6 +797,8 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
+ {
+ int expected_len;
+ enum ovs_action_attr type = nl_attr_type(a);
++ const struct ovs_action_push_nsh *nsh;
++ const struct ovs_action_push_eth *encap_eth;
+ size_t size;
+
+ expected_len = odp_action_len(nl_attr_type(a));
+@@ -830,6 +869,23 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
+ case OVS_ACTION_ATTR_POP_VLAN:
+ ds_put_cstr(ds, "pop_vlan");
+ break;
++ case OVS_ACTION_ATTR_PUSH_NSH:
++ nsh = nl_attr_get(a);
++ ds_put_cstr(ds, "push_nsh(");
++ format_nsh(ds, nsh);
++ break;
++ case OVS_ACTION_ATTR_POP_NSH:
++ ds_put_cstr(ds, "pop_nsh");
++ break;
++
++ case OVS_ACTION_ATTR_PUSH_ETH:
++ encap_eth = nl_attr_get(a);
++ ds_put_cstr(ds, "push_eth(");
++ format_encap_eth(ds, encap_eth);
++ break;
++ case OVS_ACTION_ATTR_POP_ETH:
++ ds_put_cstr(ds, "pop_eth");
++ break;
+ case OVS_ACTION_ATTR_PUSH_MPLS: {
+ const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
+ ds_put_cstr(ds, "push_mpls(");
+@@ -1618,6 +1674,72 @@ parse_odp_action(const char *s, const struct simap *port_names,
+ }
+
+ {
++ struct ovs_action_push_nsh push;
++ struct nsh_hdr *nsh = (struct nsh_hdr *)push.header;
++ ovs_be32 nsp, nshc1,nshc2,nshc3,nshc4;
++ uint8_t nsi, nsh_mdtype, nsh_np;
++ int n = -1;
++
++ if (ovs_scan_len(s, &n, "push_nsh(nsh_mdtype=%"SCNi8",nsh_np=%"SCNi8",nsp=0x%"SCNx32
++ ",nsi=%"SCNi8",nshc1=0x%"SCNx32",nshc2=0x%"SCNx32
++ ",nshc3=0x%"SCNx32",nshc4=0x%"SCNx32"))",
++ &nsh_mdtype, &nsh_np,
++ &nsp, &nsi,
++ &nshc1, &nshc2,
++ &nshc3, &nshc4)) {
++ if (nsh_mdtype == NSH_M_TYPE1) {
++ nsh->base.mdtype = NSH_M_TYPE1;
++ nsh->base.version = 0x01;
++ nsh->base.length = 6;
++ nsh->base.proto = nsh_np;
++ nsh->base.path_hdr= nsp;
++ nsh->base.svc_idx = nsi;
++ nsh->ctx.nshc1=nshc1;
++ nsh->ctx.nshc2=nshc2;
++ nsh->ctx.nshc3=nshc3;
++ nsh->ctx.nshc4=nshc4;
++ push.nsh_mdtype = NSH_M_TYPE1;
++ nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_NSH,
++ &push, sizeof push);
++ }
++
++ return n;
++ }
++ }
++
++ if (!strncmp(s, "pop_nsh", 7)) {
++ nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_NSH);
++ return 7;
++ }
++
++ {
++ struct ovs_action_push_eth push;
++ struct encap_eth_hdr *encap_eth = (struct encap_eth_hdr *)push.header;
++ uint16_t encap_eth_type;
++ int n = -1;
++
++ if (ovs_scan_len(s, &n, "push_eth(encap_eth_type=0x%"SCNx16",encap_eth_dst="ETH_ADDR_SCAN_FMT",encap_eth_src="ETH_ADDR_SCAN_FMT")",
++ &encap_eth_type,
++ ETH_ADDR_SCAN_ARGS(encap_eth->encap_eth_dst),
++ ETH_ADDR_SCAN_ARGS(encap_eth->encap_eth_src))) {
++ if (encap_eth->encap_eth_type == ETH_P_NSH) {
++ push.encap_eth_type = ETH_P_NSH;
++ encap_eth->encap_eth_type = htons(ETH_P_NSH);
++ nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_ETH,
++ &push, sizeof push);
++ }
++
++ return n;
++ }
++
++ }
++
++ if (!strncmp(s, "pop_eth", 7)) {
++ nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_ETH);
++ return 7;
++ }
++
++ {
+ double percentage;
+ int n = -1;
+
+@@ -1759,6 +1881,8 @@ static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] =
+ .next = ovs_tun_key_attr_lens,
+ .next_max = OVS_TUNNEL_KEY_ATTR_MAX },
+ [OVS_KEY_ATTR_IN_PORT] = { .len = 4 },
++ [OVS_KEY_ATTR_ENCAP_ETH] = { .len = 14 },
++ [OVS_KEY_ATTR_NSH] = { .len = 24 },
+ [OVS_KEY_ATTR_ETHERNET] = { .len = sizeof(struct ovs_key_ethernet) },
+ [OVS_KEY_ATTR_VLAN] = { .len = 2 },
+ [OVS_KEY_ATTR_ETHERTYPE] = { .len = 2 },
+@@ -2146,6 +2270,23 @@ format_be64(struct ds *ds, const char *name, ovs_be64 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_ipv4(struct ds *ds, const char *name, ovs_be32 key,
+ const ovs_be32 *mask, bool verbose)
+ {
+@@ -2798,6 +2939,34 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
+ }
+ break;
+
++ case OVS_KEY_ATTR_NSH: {
++ const struct ovs_key_nsh *mask = ma ? nl_attr_get(ma) : NULL;
++ const struct ovs_key_nsh *key = nl_attr_get(a);
++
++ format_u8u(ds, "nsi", key->nsi, MASK(mask, nsi), verbose);
++ format_be32(ds, "nsp", key->nsp, MASK(mask, nsp), verbose);
++ format_u8u(ds, "nsh_mdtype", key->nsh_mdtype, MASK(mask, nsh_mdtype), verbose);
++ format_u8u(ds, "nsh_np", key->nsh_np, MASK(mask, nsh_np), verbose);
++ format_be32(ds, "nshc1", key->nshc1, MASK(mask, nshc1), verbose);
++ format_be32(ds, "nshc2", key->nshc2, MASK(mask, nshc2), verbose);
++ format_be32(ds, "nshc3", key->nshc3, MASK(mask, nshc3), verbose);
++ format_be32(ds, "nshc4", key->nshc4, MASK(mask, nshc4), verbose);
++ ds_chomp(ds, ',');
++ break;
++ }
++
++ case OVS_KEY_ATTR_ENCAP_ETH: {
++ const struct ovs_key_encap_eth *mask = ma ? nl_attr_get(ma) : NULL;
++ const struct ovs_key_encap_eth *key = nl_attr_get(a);
++
++ format_be16(ds, "encap_eth_type", key->encap_eth_type, MASK(mask, encap_eth_type), verbose);
++ format_eth(ds, "encap_eth_src", key->encap_eth_src, MASK(mask, encap_eth_src), verbose);
++ format_eth(ds, "encap_eth_dst", key->encap_eth_dst, MASK(mask, encap_eth_dst), verbose);
++ ds_chomp(ds, ',');
++
++ break;
++ }
++
+ case OVS_KEY_ATTR_ETHERNET: {
+ const struct ovs_key_ethernet *mask = ma ? nl_attr_get(ma) : NULL;
+ const struct ovs_key_ethernet *key = nl_attr_get(a);
+@@ -3184,6 +3353,48 @@ scan_eth(const char *s, struct eth_addr *key, struct eth_addr *mask)
+ }
+
+ static int
++scan_nsp(const char *s, uint32_t *key, uint32_t *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 = UINT32_MAX;
++ }
++ }
++ return len;
++ }
++ return 0;
++}
++
++static int
++scan_encap_eth_type(const char *s, uint16_t *key, uint16_t *mask)
++{
++ int n;
++
++ if (ovs_scan(s, "%"SCNi16"%n", key, &n)) {
++ int len = n;
++ *key = htons(*key);
++ if (mask) {
++ if (ovs_scan(s + len, "/%"SCNi16"%n", mask, &n)) {
++ len += n;
++ *mask = htons(*mask);
++ } else {
++ *mask = UINT16_MAX;
++ }
++ }
++ return len;
++ }
++ return 0;
++}
++
++static int
+ scan_ipv4(const char *s, ovs_be32 *key, ovs_be32 *mask)
+ {
+ int n;
+@@ -4123,6 +4334,23 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
+ SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
+ } SCAN_END_NESTED();
+
++ SCAN_BEGIN("nsh(", struct ovs_key_nsh) {
++ SCAN_FIELD("nsh_mdtype=", u8, nsh_mdtype);
++ SCAN_FIELD("nsh_np=", u8, nsh_np);
++ SCAN_FIELD("nsp=", nsp, nsp);
++ SCAN_FIELD("nsi=", u8, nsi);
++ SCAN_FIELD("nshc1=", u32, nshc1);
++ SCAN_FIELD("nshc2=", u32, nshc2);
++ SCAN_FIELD("nshc3=", u32, nshc3);
++ SCAN_FIELD("nshc4=", u32, nshc4);
++ } SCAN_END(OVS_KEY_ATTR_NSH);
++
++ SCAN_BEGIN("encap_eth(", struct ovs_key_encap_eth) {
++ SCAN_FIELD("encap_eth_src=", eth, encap_eth_src);
++ SCAN_FIELD("encap_eth_dst=", eth, encap_eth_dst);
++ SCAN_FIELD("encap_eth_type=", encap_eth_type, encap_eth_type);
++ } SCAN_END(OVS_KEY_ATTR_ENCAP_ETH);
++
+ SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT);
+
+ SCAN_BEGIN("eth(", struct ovs_key_ethernet) {
+@@ -4325,11 +4553,35 @@ union ovs_key_tp {
+ static void get_tp_key(const struct flow *, union ovs_key_tp *);
+ static void put_tp_key(const union ovs_key_tp *, struct flow *);
+
++void
++get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh)
++{
++ nsh->nsi = flow->nsi;
++ nsh->nsp = flow->nsp;
++ nsh->nsh_mdtype = flow->nsh_mdtype;
++ nsh->nsh_np = flow->nsh_np;
++ nsh->reserved = 0;
++ nsh->nshc1 = flow->nshc1;
++ nsh->nshc2 = flow->nshc2;
++ nsh->nshc3 = flow->nshc3;
++ nsh->nshc4 = flow->nshc4;
++}
++
++void
++get_encap_eth_key(const struct flow *flow, struct ovs_key_encap_eth *encap_eth)
++{
++ encap_eth->encap_eth_type = flow->encap_eth_type;
++ encap_eth->encap_eth_src = flow->encap_eth_src;
++ encap_eth->encap_eth_dst = flow->encap_eth_dst;
++}
++
+ static void
+ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
+ bool export_mask, struct ofpbuf *buf)
+ {
+ struct ovs_key_ethernet *eth_key;
++ struct ovs_key_nsh *nsh_key;
++ struct ovs_key_encap_eth *encap_eth_key;
+ size_t encap;
+ const struct flow *flow = parms->flow;
+ const struct flow *data = export_mask ? parms->mask : parms->flow;
+@@ -4368,6 +4620,18 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
+ nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, parms->odp_in_port);
+ }
+
++ if (flow->nsh_mdtype) {
++ nsh_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_NSH,
++ sizeof *nsh_key);
++ get_nsh_key(data, nsh_key);
++ }
++
++ if (flow->encap_eth_type) {
++ encap_eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ENCAP_ETH,
++ sizeof *encap_eth_key);
++ get_encap_eth_key(data, encap_eth_key);
++ }
++
+ eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
+ sizeof *eth_key);
+ get_ethernet_key(data, eth_key);
+@@ -5225,6 +5489,28 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
+ flow->in_port.odp_port = ODPP_NONE;
+ }
+
++ /* NSH header. */
++ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_NSH)) {
++ const struct ovs_key_nsh *nsh_key;
++
++ nsh_key = nl_attr_get(attrs[OVS_KEY_ATTR_NSH]);
++ put_nsh_key(nsh_key, flow);
++ if (is_mask) {
++ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_NSH;
++ }
++ }
++
++ /* ENCAP Eth header. */
++ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP_ETH)) {
++ const struct ovs_key_encap_eth *encap_eth_key;
++
++ encap_eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ENCAP_ETH]);
++ put_encap_eth_key(encap_eth_key, flow);
++ if (is_mask) {
++ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ENCAP_ETH;
++ }
++ }
++
+ /* Ethernet header. */
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) {
+ const struct ovs_key_ethernet *eth_key;
+@@ -5526,6 +5812,41 @@ put_ethernet_key(const struct ovs_key_ethernet *eth, struct flow *flow)
+ flow->dl_dst = eth->eth_dst;
+ }
+
++void
++put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow)
++{
++ flow->nsh_mdtype = nsh->nsh_mdtype;
++ flow->nsh_np = nsh->nsh_np;
++ flow->nsi = nsh->nsi;
++ flow->nsp = nsh->nsp;
++ flow->nshc1 = nsh->nshc1;
++ flow->nshc2 = nsh->nshc2;
++ flow->nshc3 = nsh->nshc3;
++ flow->nshc4 = nsh->nshc4;
++}
++
++void
++flow_zero_nsh(struct flow *flow)
++{
++ void *dst_p = &(flow->nshc1);
++ memset(dst_p, 0, 24);
++}
++
++void
++flow_zero_encap_eth(struct flow *flow)
++{
++ void *dst_p = &flow->encap_eth_src;
++ memset(dst_p, 0, 16);
++}
++
++void
++put_encap_eth_key(const struct ovs_key_encap_eth *encap_eth, struct flow *flow)
++{
++ flow->encap_eth_type = encap_eth->encap_eth_type;
++ flow->encap_eth_src = encap_eth->encap_eth_src;
++ flow->encap_eth_dst = encap_eth->encap_eth_dst;
++}
++
+ static void
+ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
+ struct ofpbuf *odp_actions,
+@@ -5577,6 +5898,94 @@ commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
+ base->vlan_tci = vlan_tci;
+ }
+
++static void
++commit_nsh_pop_action(const struct flow *flow, struct flow *base,
++ struct ofpbuf *odp_actions, struct flow_wildcards *wc)
++{
++ struct ovs_key_nsh flow_key, base_key;
++ get_nsh_key(flow, &flow_key);
++ get_nsh_key(base, &base_key);
++
++ if (memcmp(&flow_key, &base_key, sizeof flow_key)) {
++ memset(&wc->masks.nsh_mdtype, 0xff, sizeof wc->masks.nsh_mdtype);
++
++ if (base->nsh_mdtype) {
++ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_NSH);
++ }
++ }
++}
++
++static void
++commit_nsh_push_action(const struct flow *flow, struct flow *base,
++ struct ofpbuf *odp_actions)
++{
++ struct ovs_key_nsh flow_key, base_key;
++ get_nsh_key(flow, &flow_key);
++ get_nsh_key(base, &base_key);
++
++ if (memcmp(&flow_key, &base_key, sizeof flow_key)) {
++ if (flow->nsh_mdtype) {
++ struct ovs_action_push_nsh nsh;
++ nsh.nsh_mdtype = flow->nsh_mdtype;
++ struct nsh_hdr *nsh_hdr = (struct nsh_hdr *)nsh.header;
++ memset(nsh_hdr, 0, sizeof *nsh_hdr);
++ nsh_hdr->base.length = 6;
++ nsh_hdr->base.proto = flow->nsh_np;
++ nsh_hdr->base.mdtype = flow->nsh_mdtype;
++ nsh_hdr->base.proto = flow->nsh_np;
++ nsh_hdr->base.path_hdr = flow->nsp >> 8 | flow->nsi << 24;
++ nsh_hdr->ctx.nshc1 = flow->nshc1;
++ nsh_hdr->ctx.nshc2 = flow->nshc2;
++ nsh_hdr->ctx.nshc3 = flow->nshc3;
++ nsh_hdr->ctx.nshc4 = flow->nshc4;
++ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_NSH,
++ &nsh, sizeof nsh);
++ }
++ }
++}
++
++static void
++commit_encap_eth_pop_action(const struct flow *flow, struct flow *base,
++ struct ofpbuf *odp_actions, struct flow_wildcards *wc)
++{
++ struct ovs_key_encap_eth flow_key, base_key;
++ get_encap_eth_key(flow, &flow_key);
++ get_encap_eth_key(base, &base_key);
++
++ if (memcmp(&flow_key, &base_key, sizeof flow_key)) {
++ memset(&wc->masks.encap_eth_type, 0xff, sizeof wc->masks.encap_eth_type);
++
++ if (base->encap_eth_type) {
++ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_ETH);
++ }
++ }
++}
++
++static void
++commit_encap_eth_push_action(const struct flow *flow, struct flow *base,
++ struct ofpbuf *odp_actions)
++{
++ struct ovs_key_encap_eth flow_key, base_key;
++ get_encap_eth_key(flow, &flow_key);
++ get_encap_eth_key(base, &base_key);
++
++ if (memcmp(&flow_key, &base_key, sizeof flow_key)) {
++ if (flow->encap_eth_type) {
++ struct ovs_action_push_eth encap_eth;
++ encap_eth.encap_eth_type = flow->encap_eth_type;
++ struct encap_eth_hdr *encap_eth_header =
++ (struct encap_eth_hdr *)encap_eth.header;
++ void *dst_p = encap_eth_header->encap_eth_dst.ea;
++ void *src_p = encap_eth_header->encap_eth_src.ea;
++ memcpy(dst_p, flow->encap_eth_dst.ea, sizeof flow->encap_eth_dst);
++ memcpy(src_p, flow->encap_eth_src.ea, sizeof flow->encap_eth_src);
++ encap_eth_header->encap_eth_type = htons(ETH_P_NSH);
++ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_ETH,
++ &encap_eth, sizeof encap_eth);
++ }
++ }
++}
++
+ /* Wildcarding already done at action translation time. */
+ static void
+ commit_mpls_action(const struct flow *flow, struct flow *base,
+@@ -6008,6 +6417,10 @@ commit_odp_actions(const struct flow *flow, struct flow *base,
+ slow2 = commit_set_icmp_action(flow, base, odp_actions, wc);
+ commit_mpls_action(flow, base, odp_actions);
+ commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
++ commit_encap_eth_pop_action(flow, base, odp_actions, wc);
++ commit_nsh_pop_action(flow, base, odp_actions, wc);
++ commit_nsh_push_action(flow, base, odp_actions);
++ commit_encap_eth_push_action(flow, base, odp_actions);
+ commit_set_priority_action(flow, base, odp_actions, wc, use_masked);
+ commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked);
+
+diff --git a/lib/odp-util.h b/lib/odp-util.h
+index 51cf5c3..2cb04f1 100644
+--- a/lib/odp-util.h
++++ b/lib/odp-util.h
+@@ -66,7 +66,7 @@ enum slow_path_reason {
+ /* Mask of all slow_path_reasons. */
+ enum {
+ SLOW_PATH_REASON_MASK = 0
+-#define SPR(ENUM, STRING, EXPLANATION) | 1 << ENUM##_INDEX
++#define SPR(ENUM, STRING, EXPLANATION) | 1 << ENUM##_INDEX
+ SLOW_PATH_REASONS
+ #undef SPR
+ };
+@@ -126,6 +126,8 @@ void odp_portno_names_destroy(struct hmap *portno_names);
+ * OVS_KEY_ATTR_CT_ZONE 2 2 4 8
+ * OVS_KEY_ATTR_CT_MARK 4 -- 4 8
+ * OVS_KEY_ATTR_CT_LABEL 16 -- 4 20
++ * OVS_KEY_ATTR_NSH 24 -- 4 28
++ * OVS_KEY_ATTR_ENCAP 14 -- 4 18
+ * OVS_KEY_ATTR_ETHERNET 12 -- 4 16
+ * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype)
+ * OVS_KEY_ATTR_VLAN 2 2 4 8
+@@ -150,6 +152,12 @@ struct odputil_keybuf {
+ uint32_t keybuf[DIV_ROUND_UP(ODPUTIL_FLOW_KEY_BYTES, 4)];
+ };
+
++void put_nsh_key(const struct ovs_key_nsh *, struct flow *);
++void get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh);
++void put_encap_eth_key(const struct ovs_key_encap_eth *encap_eth, struct flow *flow);
++void get_encap_eth_key(const struct flow *flow, struct ovs_key_encap_eth *encap_eth);
++void flow_zero_nsh(struct flow *);
++void flow_zero_encap_eth(struct flow *);
+ enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *, bool udpif,
+ struct flow_tnl *);
+
+diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
+index aac4ff0..f4062b2 100644
+--- a/lib/ofp-actions.c
++++ b/lib/ofp-actions.c
+@@ -299,6 +299,18 @@ enum ofp_raw_action_type {
+ /* NX1.0+(36): struct nx_action_nat, ... */
+ NXAST_RAW_NAT,
+
++ /* NX1.0+(38): void. */
++ NXAST_RAW_PUSH_NSH,
++
++ /* NX1.0+(39): void. */
++ NXAST_RAW_POP_NSH,
++
++ /* NX1.0+(40): void. */
++ NXAST_RAW_PUSH_ETH,
++
++ /* NX1.0+(41): void. */
++ NXAST_RAW_POP_ETH,
++
+ /* ## ------------------ ## */
+ /* ## Debugging actions. ## */
+ /* ## ------------------ ## */
+@@ -1624,7 +1636,127 @@ format_PUSH_VLAN(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+ ds_put_format(s, "%spush_vlan:%s%#"PRIx16,
+ colors.param, colors.end, ETH_TYPE_VLAN_8021Q);
+ }
+-
++
++/* Push NSH header actions. */
++static enum ofperr
++decode_NXAST_RAW_PUSH_NSH(struct ofpbuf * out)
++{
++ ofpact_put_PUSH_NSH(out)->ofpact.raw = NXAST_RAW_PUSH_NSH;
++
++ return 0;
++}
++
++static void
++encode_PUSH_NSH(const struct ofpact_null *null OVS_UNUSED,
++ enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
++{
++ put_NXAST_PUSH_NSH(out);
++}
++
++static char * OVS_WARN_UNUSED_RESULT
++parse_PUSH_NSH(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
++ enum ofputil_protocol *usable_protocols OVS_UNUSED)
++{
++ ofpact_put_PUSH_NSH(ofpacts)->ofpact.raw = NXAST_RAW_PUSH_NSH;;
++ return NULL;
++}
++
++static void
++format_PUSH_NSH(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
++{
++ ds_put_format(s, "push_nsh");
++}
++
++/* Pop NSH header actions. */
++static enum ofperr
++decode_NXAST_RAW_POP_NSH(struct ofpbuf * out)
++{
++ ofpact_put_POP_NSH(out)->ofpact.raw = NXAST_RAW_POP_NSH;
++
++ return 0;
++}
++
++static void
++encode_POP_NSH(const struct ofpact_null *null OVS_UNUSED,
++ enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
++{
++ put_NXAST_POP_NSH(out);
++}
++
++static char * OVS_WARN_UNUSED_RESULT
++parse_POP_NSH(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
++ enum ofputil_protocol *usable_protocols OVS_UNUSED)
++{
++ ofpact_put_POP_NSH(ofpacts)->ofpact.raw = NXAST_RAW_POP_NSH;
++ return NULL;
++}
++
++static void
++format_POP_NSH(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
++{
++ ds_put_format(s, "pop_nsh");
++}
++
++/* Push ENCAP Eth header actions. */
++static enum ofperr
++decode_NXAST_RAW_PUSH_ETH(struct ofpbuf * out)
++{
++ ofpact_put_PUSH_ETH(out)->ofpact.raw = NXAST_RAW_PUSH_ETH;
++
++ return 0;
++}
++
++static void
++encode_PUSH_ETH(const struct ofpact_null *null OVS_UNUSED,
++ enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
++{
++ put_NXAST_PUSH_ETH(out);
++}
++
++static char * OVS_WARN_UNUSED_RESULT
++parse_PUSH_ETH(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
++ enum ofputil_protocol *usable_protocols OVS_UNUSED)
++{
++ ofpact_put_PUSH_ETH(ofpacts)->ofpact.raw = NXAST_RAW_PUSH_ETH;;
++ return NULL;
++}
++
++static void
++format_PUSH_ETH(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
++{
++ ds_put_format(s, "push_eth");
++}
++
++/* Pop ENCAP ETH header actions. */
++static enum ofperr
++decode_NXAST_RAW_POP_ETH(struct ofpbuf * out)
++{
++ ofpact_put_POP_ETH(out)->ofpact.raw = NXAST_RAW_POP_ETH;
++
++ return 0;
++}
++
++static void
++encode_POP_ETH(const struct ofpact_null *null OVS_UNUSED,
++ enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
++{
++ put_NXAST_POP_ETH(out);
++}
++
++static char * OVS_WARN_UNUSED_RESULT
++parse_POP_ETH(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
++ enum ofputil_protocol *usable_protocols OVS_UNUSED)
++{
++ ofpact_put_POP_ETH(ofpacts)->ofpact.raw = NXAST_RAW_POP_ETH;
++ return NULL;
++}
++
++static void
++format_POP_ETH(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
++{
++ ds_put_format(s, "pop_eth");
++}
++
+ /* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */
+ struct ofp_action_dl_addr {
+ ovs_be16 type; /* Type. */
+@@ -5910,6 +6042,10 @@ ofpact_is_set_or_move_action(const struct ofpact *a)
+ case OFPACT_POP_QUEUE:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_PUSH_VLAN:
++ case OFPACT_PUSH_NSH:
++ case OFPACT_POP_NSH:
++ case OFPACT_PUSH_ETH:
++ case OFPACT_POP_ETH:
+ case OFPACT_RESUBMIT:
+ case OFPACT_SAMPLE:
+ case OFPACT_STACK_POP:
+@@ -5937,6 +6073,10 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
+ case OFPACT_POP_MPLS:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_PUSH_VLAN:
++ case OFPACT_PUSH_NSH:
++ case OFPACT_POP_NSH:
++ case OFPACT_PUSH_ETH:
++ case OFPACT_POP_ETH:
+ case OFPACT_REG_MOVE:
+ case OFPACT_SET_FIELD:
+ case OFPACT_SET_ETH_DST:
+@@ -6162,6 +6302,10 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_PUSH_VLAN:
++ case OFPACT_PUSH_NSH:
++ case OFPACT_POP_NSH:
++ case OFPACT_PUSH_ETH:
++ case OFPACT_POP_ETH:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+@@ -6709,6 +6853,10 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_POP_QUEUE:
++ case OFPACT_PUSH_NSH:
++ case OFPACT_POP_NSH:
++ case OFPACT_PUSH_ETH:
++ case OFPACT_POP_ETH:
+ case OFPACT_RESUBMIT:
+ return 0;
+
+@@ -7266,6 +7414,10 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_PUSH_VLAN:
++ case OFPACT_PUSH_NSH:
++ case OFPACT_POP_NSH:
++ case OFPACT_PUSH_ETH:
++ case OFPACT_POP_ETH:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
+index 4bd8854..3897c0b 100644
+--- a/lib/ofp-actions.h
++++ b/lib/ofp-actions.h
+@@ -93,6 +93,12 @@
+ OFPACT(POP_QUEUE, ofpact_null, ofpact, "pop_queue") \
+ OFPACT(FIN_TIMEOUT, ofpact_fin_timeout, ofpact, "fin_timeout") \
+ \
++ /* NSH */ \
++ OFPACT(PUSH_NSH, ofpact_null, ofpact, "push_nsh") \
++ OFPACT(POP_NSH, ofpact_null, ofpact, "pop_nsh") \
++ OFPACT(PUSH_ETH, ofpact_null, ofpact, "push_eth") \
++ OFPACT(POP_ETH, ofpact_null, ofpact, "pop_eth") \
++ \
+ /* Flow table interaction. */ \
+ OFPACT(RESUBMIT, ofpact_resubmit, ofpact, "resubmit") \
+ OFPACT(LEARN, ofpact_learn, specs, "learn") \
+diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
+index fbc82b7..e15e8d2 100644
+--- a/ofproto/ofproto-dpif-sflow.c
++++ b/ofproto/ofproto-dpif-sflow.c
+@@ -976,6 +976,8 @@ sflow_read_set_action(const struct nlattr *attr,
+ case OVS_KEY_ATTR_IN_PORT:
+ case OVS_KEY_ATTR_ETHERNET:
+ case OVS_KEY_ATTR_VLAN:
++ case OVS_KEY_ATTR_NSH:
++ case OVS_KEY_ATTR_ENCAP_ETH:
+ break;
+
+ case OVS_KEY_ATTR_MPLS: {
+diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
+index 2612b7d..f59eed8 100644
+--- a/ofproto/ofproto-dpif-upcall.c
++++ b/ofproto/ofproto-dpif-upcall.c
+@@ -39,6 +39,7 @@
+ #include "seq.h"
+ #include "unixctl.h"
+ #include "openvswitch/vlog.h"
++#include "odp-util.h"
+
+ #define MAX_QUEUE_LENGTH 512
+ #define UPCALL_MAX_BATCH 64
+@@ -746,6 +747,9 @@ recv_upcalls(struct handler *handler)
+ struct upcall *upcall = &upcalls[n_upcalls];
+ struct flow *flow = &flows[n_upcalls];
+ unsigned int mru;
++ struct ovs_key_nsh reserve_nsh;
++ struct ovs_key_encap_eth reserve_encap_eth;
++ bool nsh_mdtype_flag, encap_eth_type_flag;
+ int error;
+
+ ofpbuf_use_stub(recv_buf, recv_stubs[n_upcalls],
+@@ -796,8 +800,23 @@ recv_upcalls(struct handler *handler)
+ }
+
+ pkt_metadata_from_flow(&dupcall->packet.md, flow);
++
++ nsh_mdtype_flag = !!(flow->nsh_mdtype);
++ encap_eth_type_flag = !!(flow->encap_eth_type);
++ if(nsh_mdtype_flag)
++ get_nsh_key(flow, &reserve_nsh);
++
++ if(encap_eth_type_flag)
++ get_encap_eth_key(flow, &reserve_encap_eth);
++
+ flow_extract(&dupcall->packet, flow);
+
++ if(nsh_mdtype_flag)
++ put_nsh_key(&reserve_nsh, flow);
++
++ if(encap_eth_type_flag)
++ put_encap_eth_key(&reserve_encap_eth, flow);
++
+ error = process_upcall(udpif, upcall,
+ &upcall->odp_actions, &upcall->wc);
+ if (error) {
+diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
+index a02dc24..4f91d0a 100644
+--- a/ofproto/ofproto-dpif-xlate.c
++++ b/ofproto/ofproto-dpif-xlate.c
+@@ -4240,6 +4240,10 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end,
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_PUSH_VLAN:
++ case OFPACT_PUSH_NSH:
++ case OFPACT_POP_NSH:
++ case OFPACT_PUSH_ETH:
++ case OFPACT_POP_ETH:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+@@ -4524,6 +4528,24 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+ }
+ break;
+
++ case OFPACT_PUSH_NSH:
++ memset(&wc->masks.nsh_mdtype, 0xff, sizeof wc->masks.nsh_mdtype);
++ break;
++
++ case OFPACT_POP_NSH:
++ memset(&wc->masks.nsh_mdtype, 0xff, sizeof wc->masks.nsh_mdtype);
++ flow_zero_nsh(flow);
++ break;
++
++ case OFPACT_PUSH_ETH:
++ memset(&wc->masks.encap_eth_type, 0xff, sizeof wc->masks.encap_eth_type);
++ break;
++
++ case OFPACT_POP_ETH:
++ memset(&wc->masks.encap_eth_type, 0xff, sizeof wc->masks.encap_eth_type);
++ flow_zero_encap_eth(flow);
++ break;
++
+ case OFPACT_STRIP_VLAN:
+ memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
+ flow->vlan_tci = htons(0);
+diff --git a/tests/ofproto.at b/tests/ofproto.at
+index 6c7217d..7141e39 100644
+--- a/tests/ofproto.at
++++ b/tests/ofproto.at
+@@ -1777,7 +1777,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_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_gpe_np tun_gpe_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 ct_mark ct_label 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 mpls_ttl 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 icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll
++metadata in_port in_port_oxm pkt_mark ct_mark ct_label 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 mpls_ttl 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 icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nsp nsi nshc1 nshc2 nshc3 nshc4 nsh_mdtype nsh_np encap_eth_src encap_eth_dst encap_eth_type
+ matching:
+ dp_hash: arbitrary mask
+ recirc_id: exact match or wildcard
+@@ -1917,6 +1917,17 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
+ 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
++ nsh_mdtype: arbitrary mask
++ nsh_np: arbitrary mask
++ encap_eth_src: arbitrary mask
++ encap_eth_dst: arbitrary mask
++ encap_eth_type: exact match or wildcard
+
+ ' $1
+ }
+diff --git a/tests/tunnel.at b/tests/tunnel.at
+index 0c033da..d957574 100644
+--- a/tests/tunnel.at
++++ b/tests/tunnel.at
+@@ -412,6 +412,163 @@ AT_CHECK([tail -1 stdout], [0],
+ OVS_VSWITCHD_STOP
+ AT_CLEANUP
+
++AT_SETUP([tunnel - VXLAN-GPE and NSH - Encapsulation - kernel space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
++ options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=2 \
++ options:exts=gpe])
++
++OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
++ADD_OF_PORTS([br0], [90])
++AT_DATA([flows.txt], [dnl
++in_port=90 actions=resubmit:1,resubmit:2
++in_port=1 actions=push_nsh,set_field:1->nsh_mdtype,set_field:0x3->nsh_np,set_field:0x112233->nsp,set_field:0x44->nsi,set_field:0x11223344->nshc1,set_field:0x55667788->nshc2,set_field:0x99aabbcc->nshc3,set_field:0xddeeff00->nshc4,output:1
++in_port=2 actions=push_nsh,set_field:1->nsh_mdtype,set_field:0x3->nsh_np,set_field:0x112233->nsp,set_field:0x44->nsi,set_field:0x11223344->nshc1,set_field:0x55667788->nshc2,set_field:0x99aabbcc->nshc3,set_field:0xddeeff00->nshc4,set_field:4->tun_gpe_np,output:2
++])
++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: push_nsh(nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=287454020,nshc2=1432778632,nshc3=2578103244,nshc4=3723427584),1,set(tunnel(tun_id=0x0,dst=1.1.1.1,ttl=64,vxlan(gpe(np=0x4,flags=0)),flags(df|key))),push_nsh(nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=287454020,nshc2=1432778632,nshc3=2578103244,nshc4=3723427584),4790
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([tunnel - VXLAN-GPE and NSH - Decapsulation - kernel space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
++ options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=2 \
++ options:exts=gpe])
++OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
++
++AT_DATA([flows.txt], [dnl
++priority=200,in_port=2,tun_gpe_np=4,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,actions=pop_nsh,output=1
++])
++
++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/1: (dummy)
++ p2 2/4790: (vxlan: dst_port=4790, key=flow, remote_ip=1.1.1.1)
++])
++
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=1.2.3.4,ttl=64,flags(),vxlan(gpe(np=4,flags=0x0c))),in_port(4790),nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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: pop_nsh,1
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([Eth and NSH - Encapsulation - kernel space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
++
++OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
++ADD_OF_PORTS([br0], [90])
++AT_DATA([flows.txt], [dnl
++in_port=90 actions=resubmit:1,resubmit:2
++in_port=1 actions=push_nsh,set_field:1->nsh_mdtype,set_field:0x3->nsh_np,set_field:0x112233->nsp,set_field:0x44->nsi,set_field:0x11223344->nshc1,set_field:0x55667788->nshc2,set_field:0x99aabbcc->nshc3,push_eth,set_field:0x894f->encap_eth_type,set_field:00:11:22:33:44:55->encap_eth_dst,set_field:00:66:77:88:99:aa->encap_eth_src,output:1
++in_port=2 actions=push_nsh,set_field:1->nsh_mdtype,set_field:0x3->nsh_np,set_field:0x112233->nsp,set_field:0x44->nsi,set_field:0x11223344->nshc1,set_field:0x55667788->nshc2,set_field:0x99aabbcc->nshc3,set_field:0xddeeff00->nshc4,output:2
++])
++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: push_nsh(nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=287454020,nshc2=1432778632,nshc3=2578103244,nshc4=0),push_eth(encap_eth_type=35151,encap_eth_src=00:66:77:88:99:aa,encap_eth_dst=00:11:22:33:44:55,),1,push_nsh(nsh_mdtype=1,nsh_np=3,nsp=1122867,nsi=1140850688,nshc1=287454020,nshc2=1432778632,nshc3=2578103244,nshc4=3723427584),push_eth(encap_eth_type=35151,encap_eth_src=00:66:77:88:99:aa,encap_eth_dst=00:11:22:33:44:55,),2
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([Eth and NSH - Decapsulation - kernel space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
++OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
++
++AT_DATA([flows.txt], [dnl
++priority=200,in_port=2,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,encap_eth_type=0x894f,encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa,actions=pop_nsh,pop_eth,output=1
++])
++
++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/1: (dummy)
++ p2 2/2: (dummy)
++])
++
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy ',in_port(2),encap_eth(encap_eth_type=0x894f,encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa),nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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: pop_eth,pop_nsh,1
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([VXLANGPE+NSH to Eth+NSH - kernel space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
++ options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=2 \
++ options:exts=gpe])
++OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
++
++AT_DATA([flows.txt], [dnl
++priority=200,in_port=2,tun_gpe_np=4,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,actions=push_eth,set_field:0x894f->encap_eth_type,set_field:00:11:22:33:44:55->encap_eth_dst,set_field:00:66:77:88:99:aa->encap_eth_src,output:1
++
++])
++
++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/1: (dummy)
++ p2 2/4790: (vxlan: dst_port=4790, key=flow, remote_ip=1.1.1.1)
++])
++
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=1.2.3.4,ttl=64,flags(),vxlan(gpe(np=4,flags=0x0c))),in_port(4790),nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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: push_eth(encap_eth_type=35151,encap_eth_src=00:66:77:88:99:aa,encap_eth_dst=00:11:22:33:44:55,),1
++])
++
++OVS_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([Eth+NSH to VXLANGPE+NSH - kernel space])
++OVS_VSWITCHD_START([dnl
++ add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
++ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
++ options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=2 \
++ options:exts=gpe])
++OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
++
++AT_DATA([flows.txt], [dnl
++priority=200,in_port=1,nsh_mdtype=1,nsh_np=3,nsi=0x44,nsp=0x112233,encap_eth_type=0x894f,encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa,actions=pop_eth,set_field:0x4->tun_gpe_np,output=2
++])
++
++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/1: (dummy)
++ p2 2/4790: (vxlan: dst_port=4790, key=flow, remote_ip=1.1.1.1)
++])
++
++AT_CHECK([ovs-appctl ofproto/trace ovs-dummy ',in_port(1),encap_eth(encap_eth_type=0x894f,encap_eth_dst=00:11:22:33:44:55,encap_eth_src=00:66:77:88:99:aa),,nsh(nsh_mdtype=1,nsh_np=3,nsp=0x112233,nsi=0x44,nshc1=0x11223344,nshc2=0x55667788,nshc3=0x99aabbcc,nshc4=0xddeeff00),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,vxlan(gpe(np=0x4,flags=0)),flags(df|key))),pop_eth,4790
++])
++
++OVS_VSWITCHD_STOP
++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 \
diff --git a/ovs-nsh/patches/eeaf57e.patch b/ovs-nsh/patches/eeaf57e.patch
new file mode 100644
index 0000000..4cbb19b
--- /dev/null
+++ b/ovs-nsh/patches/eeaf57e.patch
@@ -0,0 +1,711 @@
+commit eeaf57e283ef90066fb72663bcc802e19d075d3b
+Author: Yi Yang
+Date: Mon Apr 11 15:58:14 2016 +0800
+
+ ovs-vxlan-gpe: vxlan extension to support vxlan-gpe tunnel port
+
+ Signed-off-by: Mengke Liu
+ Signed-off-by: Ricky Li
+ Signed-off-by: Johnson Li
+ Signed-off-by: Yi Yang
+
+diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
+index 6ffcc53..351a504 100644
+--- a/datapath/flow_netlink.c
++++ b/datapath/flow_netlink.c
+@@ -309,6 +309,7 @@ size_t ovs_key_attr_size(void)
+
+ static const struct ovs_len_tbl ovs_vxlan_ext_key_lens[OVS_VXLAN_EXT_MAX + 1] = {
+ [OVS_VXLAN_EXT_GBP] = { .len = sizeof(u32) },
++ [OVS_VXLAN_EXT_GPE] = { .len = sizeof(u32) },
+ };
+
+ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
+@@ -521,6 +522,9 @@ static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr,
+ case OVS_VXLAN_EXT_GBP:
+ opts.gbp = nla_get_u32(a);
+ break;
++ case OVS_VXLAN_EXT_GPE:
++ opts.gpe = nla_get_u32(a);
++ break;
+ default:
+ OVS_NLERR(log, "Unknown VXLAN extension attribute %d",
+ type);
+@@ -677,7 +681,9 @@ static int vxlan_opt_to_nlattr(struct sk_buff *skb,
+ if (!nla)
+ return -EMSGSIZE;
+
+- if (nla_put_u32(skb, OVS_VXLAN_EXT_GBP, opts->gbp) < 0)
++ if (opts->gbp && nla_put_u32(skb, OVS_VXLAN_EXT_GBP, opts->gbp) < 0)
++ return -EMSGSIZE;
++ else if (opts->gpe && nla_put_u32(skb, OVS_VXLAN_EXT_GPE, opts->gpe) < 0)
+ return -EMSGSIZE;
+
+ nla_nest_end(skb, nla);
+diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
+index 3b39ebb..44adb81 100644
+--- a/datapath/linux/compat/include/linux/openvswitch.h
++++ b/datapath/linux/compat/include/linux/openvswitch.h
+@@ -287,6 +287,7 @@ enum ovs_vport_attr {
+ enum {
+ OVS_VXLAN_EXT_UNSPEC,
+ OVS_VXLAN_EXT_GBP, /* Flag or __u32 */
++ OVS_VXLAN_EXT_GPE,
+ __OVS_VXLAN_EXT_MAX,
+ };
+
+diff --git a/datapath/linux/compat/include/net/vxlan.h b/datapath/linux/compat/include/net/vxlan.h
+index 75a5a7a..2bfc3f8 100644
+--- a/datapath/linux/compat/include/net/vxlan.h
++++ b/datapath/linux/compat/include/net/vxlan.h
+@@ -84,6 +84,75 @@ struct vxlanhdr_gbp {
+ #define VXLAN_GBP_POLICY_APPLIED (BIT(3) << 16)
+ #define VXLAN_GBP_ID_MASK (0xFFFF)
+
++/*
++ * VXLAN Generic Protocol Extension Extension:
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * |R|R|Ver|I|P|R|O|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R| Next Proto |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * | VXLAN Network Identifier (VNI) | Reserved |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * Ver = Version. Indicates VXLAN GPE protocol version. The initial
++ * version is 0. If a receiver does not support the version
++ * indicated it MUST drop the packet.
++ *
++ * I = Instance Bit. The I bit MUST be set to indicate a valid VNI.
++ *
++ * P = Next Protocol Bit. The P bit is set to indicate that the
++ * Next Protocol field is present.
++ *
++ * O = OAM Flag Bit. The O bit is set to indicate that the packet
++ * is an OAM packet.
++ *
++ * [1] https://www.ietf.org/id/draft-ietf-nvo3-vxlan-gpe-01.txt
++ */
++
++struct vxlanhdr_gpe {
++#ifdef __LITTLE_ENDIAN_BITFIELD
++ uint8_t oam_flag:1;
++ uint8_t reserved_flags1:1;
++ uint8_t np_applied:1;
++ uint8_t instance_applied:1;
++ uint8_t gpe_version:2;
++ uint8_t reserved_flags2:2;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++ uint8_t reserved_flags2:2;
++ uint8_t gpe_version:2;
++ uint8_t instance_applied:1;
++ uint8_t np_applied:1;
++ uint8_t reserved_flags1:1;
++ uint8_t oam_flag:1;
++#else
++#error "Please fix "
++#endif
++ uint8_t reserved_flags3;
++ uint8_t reserved_flags4;
++ uint8_t next_proto;
++ __be32 vx_vni;
++};
++
++/* VxLAN-GPE Header Next Protocol */
++#define VXLAN_GPE_NP_IPV4 0x01
++#define VXLAN_GPE_NP_IPV6 0x02
++#define VXLAN_GPE_NP_ETHERNET 0x03
++#define VXLAN_GPE_NP_NSH 0x04
++
++/* skb->mark mapping
++ *
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ * |R|R|Ver|I|P|R|O|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R|R| Next Proto |
++ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
++ */
++
++#define VXLAN_GPE_OAM_FLAG (BIT(0) << 24)
++#define VXLAN_GPE_NP_APPLIED (BIT(0) << 26)
++#define VXLAN_GPE_INSTANCE_APPLIED (BIT(0) << 27)
++#define VXLAN_GPE_VERSION ((BIT(0) << 28) | (BIT(0) << 29))
++
++#define VXLAN_GPE_NP_MASK (0xFF)
++
++#define VXLAN_GPE_USED_BITS (VXLAN_GPE_OAM_FLAG | VXLAN_GPE_NP_APPLIED \
++ | VXLAN_GPE_INSTANCE_APPLIED | VXLAN_GPE_VERSION | 0xFF)
++
+ /* VXLAN protocol header:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |G|R|R|R|I|R|R|C| Reserved |
+@@ -104,6 +173,7 @@ struct vxlanhdr {
+ #define VXLAN_HF_RCO BIT(21)
+ #define VXLAN_HF_VNI BIT(27)
+ #define VXLAN_HF_GBP BIT(31)
++#define VXLAN_HF_GPE BIT(26)
+
+ /* Remote checksum offload header option */
+ #define VXLAN_RCO_MASK 0x7f /* Last byte of vni field */
+@@ -120,6 +190,7 @@ struct vxlanhdr {
+ struct vxlan_metadata {
+ __be32 vni;
+ u32 gbp;
++ u32 gpe;
+ };
+
+ #define VNI_HASH_BITS 10
+@@ -205,11 +276,13 @@ struct vxlan_dev {
+ #define VXLAN_F_GBP 0x800
+ #define VXLAN_F_REMCSUM_NOPARTIAL 0x1000
+ #define VXLAN_F_COLLECT_METADATA 0x2000
++#define VXLAN_F_GPE 0x4000
+
+ /* Flags that are used in the receive path. These flags must match in
+ * order for a socket to be shareable
+ */
+ #define VXLAN_F_RCV_FLAGS (VXLAN_F_GBP | \
++ VXLAN_F_GPE | \
+ VXLAN_F_UDP_ZERO_CSUM6_RX | \
+ VXLAN_F_REMCSUM_RX | \
+ VXLAN_F_REMCSUM_NOPARTIAL | \
+diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c
+index 4faa18f..7ef051c 100644
+--- a/datapath/linux/compat/vxlan.c
++++ b/datapath/linux/compat/vxlan.c
+@@ -971,6 +971,18 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
+ md->gbp |= VXLAN_GBP_POLICY_APPLIED;
+
+ flags &= ~VXLAN_GBP_USED_BITS;
++ } else if ((flags & VXLAN_HF_GPE) && (vs->flags & VXLAN_F_GPE)) {
++ struct vxlanhdr_gpe *gpe;
++
++ gpe = (struct vxlanhdr_gpe *)vxh;
++ md->gpe = ntohs(gpe->next_proto);
++
++ buf.dst.u.tun_info.key.tun_flags |= TUNNEL_VXLAN_OPT;
++
++ if (gpe->oam_flag)
++ md->gpe |= VXLAN_GPE_OAM_FLAG;
++
++ flags &= ~VXLAN_GPE_USED_BITS;
+ }
+
+ if (flags || vni & ~VXLAN_VNI_MASK) {
+@@ -1023,6 +1035,22 @@ static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags,
+ gbp->policy_id = htons(md->gbp & VXLAN_GBP_ID_MASK);
+ }
+
++static void vxlan_build_gpe_hdr(struct vxlanhdr *vxh, u32 vxflags,
++ struct vxlan_metadata *md)
++{
++ struct vxlanhdr_gpe *gpe;
++
++ if (!md->gpe)
++ return;
++
++ gpe = (struct vxlanhdr_gpe*)vxh;
++ vxh->vx_flags |= htonl(VXLAN_HF_GPE);
++
++ if (md->gpe & VXLAN_GPE_OAM_FLAG)
++ gpe->oam_flag = 1;
++ gpe->next_proto = md->gpe & VXLAN_GPE_NP_MASK;
++}
++
+ #if IS_ENABLED(CONFIG_IPV6)
+ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk,
+ struct sk_buff *skb,
+@@ -1106,6 +1134,8 @@ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk,
+
+ if (vxflags & VXLAN_F_GBP)
+ vxlan_build_gbp_hdr(vxh, vxflags, md);
++ else if (vxflags & VXLAN_F_GPE)
++ vxlan_build_gpe_hdr(vxh, vxflags, md);
+
+ ovs_skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+
+diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
+index c05f5d4..5d775cc 100644
+--- a/datapath/vport-vxlan.c
++++ b/datapath/vport-vxlan.c
+@@ -52,6 +52,18 @@ static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
+ return -EMSGSIZE;
+
+ nla_nest_end(skb, exts);
++ } else if (vxlan->flags & VXLAN_F_GPE) {
++ struct nlattr *exts;
++
++ exts = nla_nest_start(skb, OVS_TUNNEL_ATTR_EXTENSION);
++ if (!exts)
++ return -EMSGSIZE;
++
++ if (vxlan->flags & VXLAN_F_GPE &&
++ nla_put_flag(skb, OVS_VXLAN_EXT_GPE))
++ return -EMSGSIZE;
++
++ nla_nest_end(skb, exts);
+ }
+
+ return 0;
+@@ -59,6 +71,7 @@ static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
+
+ static const struct nla_policy exts_policy[OVS_VXLAN_EXT_MAX + 1] = {
+ [OVS_VXLAN_EXT_GBP] = { .type = NLA_FLAG, },
++ [OVS_VXLAN_EXT_GPE] = { .type = NLA_FLAG, },
+ };
+
+ static int vxlan_configure_exts(struct vport *vport, struct nlattr *attr,
+@@ -76,6 +89,8 @@ static int vxlan_configure_exts(struct vport *vport, struct nlattr *attr,
+
+ if (exts[OVS_VXLAN_EXT_GBP])
+ conf->flags |= VXLAN_F_GBP;
++ else if (exts[OVS_VXLAN_EXT_GPE])
++ conf->flags |= VXLAN_F_GPE;
+
+ return 0;
+ }
+diff --git a/lib/flow.c b/lib/flow.c
+index b9ce331..d24bdc9 100644
+--- a/lib/flow.c
++++ b/lib/flow.c
+@@ -870,6 +870,12 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata)
+ if (flow->tunnel.gbp_flags) {
+ match_set_tun_gbp_flags(flow_metadata, flow->tunnel.gbp_flags);
+ }
++ if (flow->tunnel.gpe_np != htons(0)) {
++ match_set_tun_gpe_np(flow_metadata, flow->tunnel.gpe_np);
++ }
++ if (flow->tunnel.gpe_flags) {
++ match_set_tun_gpe_flags(flow_metadata, flow->tunnel.gpe_flags);
++ }
+ tun_metadata_get_fmd(&flow->tunnel, flow_metadata);
+ if (flow->metadata != htonll(0)) {
+ match_set_metadata(flow_metadata, flow->metadata);
+@@ -1265,6 +1271,8 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
+ WC_MASK_FIELD(wc, tunnel.tp_dst);
+ WC_MASK_FIELD(wc, tunnel.gbp_id);
+ WC_MASK_FIELD(wc, tunnel.gbp_flags);
++ WC_MASK_FIELD(wc, tunnel.gpe_np);
++ WC_MASK_FIELD(wc, tunnel.gpe_flags);
+
+ if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) {
+ if (flow->tunnel.metadata.present.map) {
+diff --git a/lib/match.c b/lib/match.c
+index fd571d9..52437c9 100644
+--- a/lib/match.c
++++ b/lib/match.c
+@@ -289,6 +289,32 @@ match_set_tun_gbp_flags(struct match *match, uint8_t flags)
+ }
+
+ void
++match_set_tun_gpe_np_masked(struct match *match, uint8_t np, uint8_t mask)
++{
++ match->wc.masks.tunnel.gpe_np = mask;
++ match->flow.tunnel.gpe_np = np & mask;
++}
++
++void
++match_set_tun_gpe_np(struct match *match, uint8_t np)
++{
++ match_set_tun_gpe_np_masked(match, np, UINT8_MAX);
++}
++
++void
++match_set_tun_gpe_flags_masked(struct match *match, uint8_t flags, uint8_t mask)
++{
++ match->wc.masks.tunnel.gpe_flags = mask;
++ match->flow.tunnel.gpe_flags = flags & mask;
++}
++
++void
++match_set_tun_gpe_flags(struct match *match, uint8_t flags)
++{
++ match_set_tun_gpe_flags_masked(match, flags, UINT8_MAX);
++}
++
++void
+ match_set_in_port(struct match *match, ofp_port_t ofp_port)
+ {
+ match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
+@@ -1013,6 +1039,14 @@ format_flow_tunnel(struct ds *s, const struct match *match)
+ ds_put_format(s, "tun_gbp_flags=%#"PRIx8",", tnl->gbp_flags);
+ }
+
++ if (wc->masks.tunnel.gpe_np) {
++ ds_put_format(s, "tun_gpe_np=%#"PRIx8",", tnl->gpe_np);
++ }
++
++ if (wc->masks.tunnel.gpe_flags) {
++ ds_put_format(s, "tun_gpe_flags=%#"PRIx8",", tnl->gpe_flags);
++ }
++
+ if (wc->masks.tunnel.ip_tos) {
+ ds_put_format(s, "tun_tos=%"PRIx8",", tnl->ip_tos);
+ }
+diff --git a/lib/match.h b/lib/match.h
+index 0a6ac29..48aa0b1 100644
+--- a/lib/match.h
++++ b/lib/match.h
+@@ -86,6 +86,10 @@ void match_set_tun_gbp_id_masked(struct match *match, ovs_be16 gbp_id, ovs_be16
+ void match_set_tun_gbp_id(struct match *match, ovs_be16 gbp_id);
+ void match_set_tun_gbp_flags_masked(struct match *match, uint8_t flags, uint8_t mask);
+ void match_set_tun_gbp_flags(struct match *match, uint8_t flags);
++void match_set_tun_gpe_np_masked(struct match *match, uint8_t gpe_np, uint8_t mask);
++void match_set_tun_gpe_np(struct match *match, uint8_t gpe_np);
++void match_set_tun_gpe_flags_masked(struct match *match, uint8_t flags, uint8_t mask);
++void match_set_tun_gpe_flags(struct match *match, uint8_t flags);
+ void match_set_in_port(struct match *, ofp_port_t ofp_port);
+ void match_set_pkt_mark(struct match *, uint32_t pkt_mark);
+ void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t mask);
+diff --git a/lib/meta-flow.c b/lib/meta-flow.c
+index 721152c..ab77fca 100644
+--- a/lib/meta-flow.c
++++ b/lib/meta-flow.c
+@@ -213,6 +213,10 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
+ return !wc->masks.tunnel.gbp_id;
+ case MFF_TUN_GBP_FLAGS:
+ return !wc->masks.tunnel.gbp_flags;
++ case MFF_TUN_GPE_NP:
++ return !wc->masks.tunnel.gpe_np;
++ case MFF_TUN_GPE_FLAGS:
++ return !wc->masks.tunnel.gpe_flags;
+ CASE_MFF_TUN_METADATA:
+ return !ULLONG_GET(wc->masks.tunnel.metadata.present.map,
+ mf->id - MFF_TUN_METADATA0);
+@@ -515,6 +519,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
+ case MFF_TUN_TTL:
+ case MFF_TUN_GBP_ID:
+ case MFF_TUN_GBP_FLAGS:
++ case MFF_TUN_GPE_NP:
++ case MFF_TUN_GPE_FLAGS:
+ CASE_MFF_TUN_METADATA:
+ case MFF_METADATA:
+ case MFF_IN_PORT:
+@@ -648,6 +654,12 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
+ case MFF_TUN_GBP_FLAGS:
+ value->u8 = flow->tunnel.gbp_flags;
+ break;
++ case MFF_TUN_GPE_NP:
++ value->u8 = flow->tunnel.gpe_np;
++ break;
++ case MFF_TUN_GPE_FLAGS:
++ value->u8 = flow->tunnel.gpe_flags;
++ break;
+ case MFF_TUN_TTL:
+ value->u8 = flow->tunnel.ip_ttl;
+ break;
+@@ -899,6 +911,12 @@ mf_set_value(const struct mf_field *mf,
+ case MFF_TUN_GBP_FLAGS:
+ match_set_tun_gbp_flags(match, value->u8);
+ break;
++ case MFF_TUN_GPE_NP:
++ match_set_tun_gpe_np(match, value->u8);
++ break;
++ case MFF_TUN_GPE_FLAGS:
++ match_set_tun_gpe_flags(match, value->u8);
++ break;
+ case MFF_TUN_TOS:
+ match_set_tun_tos(match, value->u8);
+ break;
+@@ -1216,6 +1234,12 @@ mf_set_flow_value(const struct mf_field *mf,
+ case MFF_TUN_GBP_FLAGS:
+ flow->tunnel.gbp_flags = value->u8;
+ break;
++ case MFF_TUN_GPE_NP:
++ flow->tunnel.gpe_np= value->u8;
++ break;
++ case MFF_TUN_GPE_FLAGS:
++ flow->tunnel.gpe_flags= value->u8;
++ break;
+ case MFF_TUN_TOS:
+ flow->tunnel.ip_tos = value->u8;
+ break;
+@@ -1535,6 +1559,12 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
+ case MFF_TUN_GBP_FLAGS:
+ match_set_tun_gbp_flags_masked(match, 0, 0);
+ break;
++ case MFF_TUN_GPE_NP:
++ match_set_tun_gpe_np_masked(match, 0, 0);
++ break;
++ case MFF_TUN_GPE_FLAGS:
++ match_set_tun_gpe_flags_masked(match, 0, 0);
++ break;
+ case MFF_TUN_TOS:
+ match_set_tun_tos_masked(match, 0, 0);
+ break;
+@@ -1838,6 +1868,12 @@ mf_set(const struct mf_field *mf,
+ case MFF_TUN_GBP_FLAGS:
+ match_set_tun_gbp_flags_masked(match, value->u8, mask->u8);
+ break;
++ case MFF_TUN_GPE_NP:
++ match_set_tun_gpe_np_masked(match, value->u8, mask->u8);
++ break;
++ case MFF_TUN_GPE_FLAGS:
++ match_set_tun_gpe_flags_masked(match, value->u8, mask->u8);
++ break;
+ case MFF_TUN_TTL:
+ match_set_tun_ttl_masked(match, value->u8, mask->u8);
+ break;
+diff --git a/lib/meta-flow.h b/lib/meta-flow.h
+index c73a1af..4bd9ff6 100644
+--- a/lib/meta-flow.h
++++ b/lib/meta-flow.h
+@@ -491,6 +491,34 @@ enum OVS_PACKED_ENUM mf_field_id {
+ */
+ MFF_TUN_GBP_FLAGS,
+
++ /* "tun_gpe_np".
++ *
++ * VXLAN Generic Protocol Extension next_proto
++ *
++ * Type: u8.
++ * Maskable: bitwise.
++ * Formatting: hexadecimal.
++ * Prerequisites: none.
++ * Access: read/write.
++ * NXM: NXM_NX_TUN_GPE_NP(111) since v2.4.
++ * OXM: none.
++ */
++ MFF_TUN_GPE_NP,
++
++ /* "tun_gpe_flags".
++ *
++ * VXLAN Generic Protocol Extension flag
++ *
++ * Type: u8.
++ * Maskable: bitwise.
++ * Formatting: hexadecimal.
++ * Prerequisites: none.
++ * Access: read/write.
++ * NXM: NXM_NX_TUN_GPE_FLAGS(112) since v2.4.
++ * OXM: none.
++ */
++ MFF_TUN_GPE_FLAGS,
++
+ #if TUN_METADATA_NUM_OPTS == 64
+ /* "tun_metadata".
+ *
+diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
+index e398562..92ceec1 100644
+--- a/lib/netdev-vport.c
++++ b/lib/netdev-vport.c
+@@ -583,6 +583,8 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
+ while (ext) {
+ if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) {
+ tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP);
++ } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) {
++ tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE);
+ } else {
+ VLOG_WARN("%s: unknown extension '%s'", name, ext);
+ }
+diff --git a/lib/nx-match.c b/lib/nx-match.c
+index 9f0f452..0eecac7 100644
+--- a/lib/nx-match.c
++++ b/lib/nx-match.c
+@@ -1037,6 +1037,10 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
+ flow->tunnel.gbp_id, match->wc.masks.tunnel.gbp_id);
+ nxm_put_8m(b, MFF_TUN_GBP_FLAGS, oxm,
+ flow->tunnel.gbp_flags, match->wc.masks.tunnel.gbp_flags);
++ nxm_put_8m(b, MFF_TUN_GPE_NP, oxm,
++ flow->tunnel.gpe_np, match->wc.masks.tunnel.gpe_np);
++ nxm_put_8m(b, MFF_TUN_GPE_FLAGS, oxm,
++ flow->tunnel.gpe_flags, match->wc.masks.tunnel.gpe_flags);
+ tun_metadata_to_nx_match(b, oxm, match);
+
+ /* Registers. */
+diff --git a/lib/odp-util.c b/lib/odp-util.c
+index b4689cc..7983720 100644
+--- a/lib/odp-util.c
++++ b/lib/odp-util.c
+@@ -1727,6 +1727,7 @@ odp_actions_from_string(const char *s, const struct simap *port_names,
+
+ static const struct attr_len_tbl ovs_vxlan_ext_attr_lens[OVS_VXLAN_EXT_MAX + 1] = {
+ [OVS_VXLAN_EXT_GBP] = { .len = 4 },
++ [OVS_VXLAN_EXT_GPE] = { .len = 4 },
+ };
+
+ static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
+@@ -1888,7 +1889,10 @@ odp_tun_key_from_attr__(const struct nlattr *attr,
+ break;
+ case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS: {
+ static const struct nl_policy vxlan_opts_policy[] = {
+- [OVS_VXLAN_EXT_GBP] = { .type = NL_A_U32 },
++ [OVS_VXLAN_EXT_GBP] = { .type = NL_A_U32 ,
++ .optional = true },
++ [OVS_VXLAN_EXT_GPE] = { .type = NL_A_U32 ,
++ .optional = true },
+ };
+ struct nlattr *ext[ARRAY_SIZE(vxlan_opts_policy)];
+
+@@ -1902,6 +1906,12 @@ odp_tun_key_from_attr__(const struct nlattr *attr,
+ tun->gbp_id = htons(gbp & 0xFFFF);
+ tun->gbp_flags = (gbp >> 16) & 0xFF;
+ }
++ if (ext[OVS_VXLAN_EXT_GPE]) {
++ uint32_t gpe = nl_attr_get_u32(ext[OVS_VXLAN_EXT_GPE]);
++
++ tun->gpe_np = gpe & 0xFF;
++ tun->gpe_flags = gpe >> 24;
++ }
+
+ break;
+ }
+@@ -1988,6 +1998,13 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key,
+ nl_msg_put_u32(a, OVS_VXLAN_EXT_GBP,
+ (tun_key->gbp_flags << 16) | ntohs(tun_key->gbp_id));
+ nl_msg_end_nested(a, vxlan_opts_ofs);
++ } else if (tun_key->gpe_flags || tun_key->gpe_np) {
++ size_t vxlan_opts_ofs;
++
++ vxlan_opts_ofs = nl_msg_start_nested(a, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS);
++ nl_msg_put_u32(a, OVS_VXLAN_EXT_GPE,
++ (tun_key->gpe_flags << 24) | (tun_key->gpe_np));
++ nl_msg_end_nested(a, vxlan_opts_ofs);
+ }
+ tun_metadata_to_geneve_nlattr(tun_key, tun_flow_key, key_buf, a);
+
+@@ -2383,6 +2400,26 @@ format_odp_tun_vxlan_opt(const struct nlattr *attr,
+ ds_put_cstr(ds, "),");
+ break;
+ }
++ case OVS_VXLAN_EXT_GPE: {
++ uint32_t key = nl_attr_get_u32(a);
++ uint8_t np, np_mask;
++ uint8_t flags, flags_mask;
++
++ np = key & 0xFF;
++ flags = (key >> 24) & 0xFF;
++ if (ma) {
++ uint32_t mask = nl_attr_get_u32(ma);
++ np_mask = mask & 0xFF;
++ flags_mask = (mask >> 24) & 0xFF;
++ }
++
++ ds_put_cstr(ds, "gpe(");
++ format_u8x(ds, "np", np, ma ? &np_mask : NULL, verbose);
++ format_u8x(ds, "flags", flags, ma ? &flags_mask : NULL, verbose);
++ ds_chomp(ds, ',');
++ ds_put_cstr(ds, "),");
++ break;
++ }
+
+ default:
+ format_unknown_key(ds, a, ma);
+@@ -3670,6 +3707,40 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
+ }
+
+ static int
++scan_vxlan_gpe(const char *s, uint32_t *key, uint32_t *mask)
++{
++ const char *s_base = s;
++ uint8_t np = 0, np_mask = 0;
++ uint8_t flags = 0, flags_mask = 0;
++
++ if (!strncmp(s, "np=", 3)) {
++ s += 3;
++ s += scan_u8(s, &np, mask ? &np_mask : NULL);
++ }
++
++ if (s[0] == ',') {
++ s++;
++ }
++ if (!strncmp(s, "flags=", 6)) {
++ s += 6;
++ s += scan_u8(s, &flags, mask ? &flags_mask : NULL);
++ }
++
++ if (!strncmp(s, "))", 2)) {
++ s += 2;
++
++ *key = (flags << 24) | np;
++ if (mask) {
++ *mask = (flags_mask << 24) | np_mask;
++ }
++
++ return s - s_base;
++ }
++
++ return 0;
++}
++
++static int
+ scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask)
+ {
+ const char *s_base = s;
+@@ -3796,6 +3867,21 @@ vxlan_gbp_to_attr(struct ofpbuf *a, const void *data_)
+ }
+
+ static void
++vxlan_gpe_to_attr(struct ofpbuf *a, const void *data_)
++{
++ const uint32_t *gpe = data_;
++
++ if (*gpe) {
++ size_t vxlan_opts_ofs;
++
++ vxlan_opts_ofs = nl_msg_start_nested(a, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS);
++ nl_msg_put_u32(a, OVS_VXLAN_EXT_GPE, *gpe);
++ nl_msg_end_nested(a, vxlan_opts_ofs);
++ }
++}
++
++
++static void
+ geneve_to_attr(struct ofpbuf *a, const void *data_)
+ {
+ const struct geneve_scan *geneve = data_;
+@@ -4031,6 +4117,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
+ 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_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp, vxlan_gbp_to_attr);
++ SCAN_FIELD_NESTED_FUNC("vxlan(gpe(", uint32_t, vxlan_gpe, vxlan_gpe_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);
+diff --git a/lib/packets.h b/lib/packets.h
+index a8ea24b..dc97333 100644
+--- a/lib/packets.h
++++ b/lib/packets.h
+@@ -49,7 +49,9 @@ 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 gpe_np;
++ uint8_t gpe_flags;
++ uint8_t pad1[3]; /* Pad to 64 bits. */
+ struct tun_metadata metadata;
+ };
+
+diff --git a/tests/ofproto.at b/tests/ofproto.at
+index fbb6d71..6c7217d 100644
+--- a/tests/ofproto.at
++++ b/tests/ofproto.at
+@@ -1775,7 +1775,7 @@ head_table () {
+ instructions: meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table
+ Write-Actions and Apply-Actions features:
+ 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_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 dnl
++ supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_gpe_np tun_gpe_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 ct_mark ct_label 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 mpls_ttl 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 icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll
+ matching:
+@@ -1790,6 +1790,8 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
+ tun_flags: arbitrary mask
+ tun_gbp_id: arbitrary mask
+ tun_gbp_flags: arbitrary mask
++ tun_gpe_np: arbitrary mask
++ tun_gpe_flags: arbitrary mask
+ tun_metadata0: arbitrary mask
+ tun_metadata1: arbitrary mask
+ tun_metadata2: arbitrary mask
+diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
+index f26f622..dde603d 100644
+--- a/tests/ovs-ofctl.at
++++ b/tests/ovs-ofctl.at
+@@ -17,6 +17,10 @@ for test_case in \
+ 'tun_gbp_id=0/0x1 NXM,OXM' \
+ 'tun_gbp_flags=0 NXM,OXM' \
+ 'tun_gbp_flags=0/0x1 NXM,OXM' \
++ 'tun_gpe_np=0 NXM,OXM' \
++ 'tun_gpe_np=0/0x1 NXM,OXM' \
++ 'tun_gpe_flags=0 NXM,OXM' \
++ 'tun_gpe_flags=0/0x1 NXM,OXM' \
+ 'tun_metadata0=0 NXM,OXM' \
+ 'tun_metadata0=0/0x1 NXM,OXM' \
+ 'tun_metadata0 NXM,OXM' \
diff --git a/pre_build_hook b/pre_build_hook
index 5804d83..d9d48d9 100755
--- a/pre_build_hook
+++ b/pre_build_hook
@@ -3,7 +3,7 @@
set -eux
BUILD_FOR=${BUILD_FOR:-ubuntu}
-DIR="$(dirname `readlink -f $0`)"
+DIR="$(dirname $(readlink -f "$0"))"
INCLUDE_DEPENDENCIES=${INCLUDE_DEPENDENCIES:-true}
@@ -23,15 +23,15 @@ function build_pkg {
cd ${DIR}/ovs-nsh
sudo docker build -t ovs-nsh .
container_id=`sudo docker run -d ovs-nsh`
- sudo docker cp $container_id:/openvswitch/openvswitch-common_2.4.90-1_amd64.deb ${DIR}/repositories/ubuntu/ovs/
- sudo docker cp $container_id:/openvswitch/openvswitch-datapath-dkms_2.4.90-1_all.deb ${DIR}/repositories/ubuntu/ovs/
- sudo docker cp $container_id:/openvswitch/openvswitch-switch_2.4.90-1_amd64.deb ${DIR}/repositories/ubuntu/ovs/
+ sudo docker cp $container_id:/root/openvswitch/openvswitch-common_2.5.90-1_amd64.deb ${DIR}/repositories/ubuntu/ovs/
+ sudo docker cp $container_id:/root/openvswitch/openvswitch-datapath-dkms_2.5.90-1_all.deb ${DIR}/repositories/ubuntu/ovs/
+ sudo docker cp $container_id:/root/openvswitch/openvswitch-switch_2.5.90-1_amd64.deb ${DIR}/repositories/ubuntu/ovs/
- sudo docker cp $container_id:/openvswitch-dpdk/openvswitch-common_2.4.90-1_amd64.deb ${DIR}/repositories/ubuntu/dpdk
- sudo docker cp $container_id:/openvswitch-dpdk/openvswitch-datapath-dkms_2.4.90-1_all.deb ${DIR}/repositories/ubuntu/dpdk
- sudo docker cp $container_id:/openvswitch-dpdk/openvswitch-switch_2.4.90-1_amd64.deb ${DIR}/repositories/ubuntu/dpdk
- sudo docker cp $container_id:/dpdk-2.1.0.bin.tar.gz ${DIR}/repositories/ubuntu/dpdk/
- sudo docker cp $container_id:/dppd-prox-v021.bin.tar.gz ${DIR}/repositories/ubuntu/dppd/
+ sudo docker cp $container_id:/root/openvswitch-dpdk/openvswitch-common_2.5.90-1_amd64.deb ${DIR}/repositories/ubuntu/dpdk
+ sudo docker cp $container_id:/root/openvswitch-dpdk/openvswitch-datapath-dkms_2.5.90-1_all.deb ${DIR}/repositories/ubuntu/dpdk
+ sudo docker cp $container_id:/root/openvswitch-dpdk/openvswitch-switch_2.5.90-1_amd64.deb ${DIR}/repositories/ubuntu/dpdk
+ sudo docker cp $container_id:/root/dpdk-2.2.0.bin.tar.gz ${DIR}/repositories/ubuntu/dpdk/
+ sudo docker cp $container_id:/root/dppd-prox-v021.bin.tar.gz ${DIR}/repositories/ubuntu/dppd/
;;
*) echo "Not supported system"; exit 1;;
esac
diff --git a/repositories/ubuntu/dpdk/dpdk-install.sh b/repositories/ubuntu/dpdk/dpdk-install.sh
index 99b406c..e45d605 100755
--- a/repositories/ubuntu/dpdk/dpdk-install.sh
+++ b/repositories/ubuntu/dpdk/dpdk-install.sh
@@ -2,11 +2,13 @@
NICS=$1
INSTALL_HOME=/usr/share/dpdk/
+OVS_VER='2.5.90'
+DPDK_VER='2.2.0'
rm -rf $INSTALL_HOME ; mkdir -p $INSTALL_HOME
cd $INSTALL_HOME
-wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/dpdk-2.1.0.bin.tar.gz
-tar xzvf dpdk-2.1.0.bin.tar.gz
-rm -rf dpdk-2.1.0.bin.tar.gz
+wget "http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/dpdk-${DPDK_VER}.bin.tar.gz"
+tar xzvf "dpdk-${DPDK_VER}.bin.tar.gz"
+rm -rf dpdk-${DPDK_VER}.bin.tar.gz
wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/dpdk.init -O /etc/init.d/dpdk
chmod +x /etc/init.d/dpdk
wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/dpdk.conf -O /etc/default/dpdk.conf
@@ -16,9 +18,9 @@ service dpdk start
INSTALL_HOME=/usr/share/ovs-dpdk/
rm -rf $INSTALL_HOME ; mkdir -p $INSTALL_HOME
cd $INSTALL_HOME
-wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/openvswitch-datapath-dkms_2.4.90-1_all.deb
-dpkg -i openvswitch-datapath-dkms_2.4.90-1_all.deb
-wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/openvswitch-common_2.4.90-1_amd64.deb
-dpkg -i openvswitch-common_2.4.90-1_amd64.deb
-wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/openvswitch-switch_2.4.90-1_amd64.deb
-dpkg -i openvswitch-switch_2.4.90-1_amd64.deb
+wget "http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/openvswitch-datapath-dkms_${OVS_VER}-1_all.deb"
+dpkg -i "openvswitch-datapath-dkms_${OVS_VER}_all.deb"
+wget "http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/openvswitch-common_${OVS_VER}-1_amd64.deb"
+dpkg -i "openvswitch-common_${OVS_VER}-1_amd64.deb"
+wget "http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk/openvswitch-switch_${OVS_VER}-1_amd64.deb"
+dpkg -i "openvswitch-switch_${OVS_VER}-1_amd64.deb"
diff --git a/repositories/ubuntu/dpdk/dpdk.init b/repositories/ubuntu/dpdk/dpdk.init
index 3a1ee5d..377bb7a 100644
--- a/repositories/ubuntu/dpdk/dpdk.init
+++ b/repositories/ubuntu/dpdk/dpdk.init
@@ -17,7 +17,7 @@
echo "sourcing config"
source /etc/default/dpdk.conf
-RTE_SDK=${RTE_SDK:-/usr/share/dpdk/dpdk-2.1.0}
+RTE_SDK=${RTE_SDK:-/usr/share/dpdk/dpdk-2.2.0}
RTE_TARGET=${RTE_TARGET:-x86_64-native-linuxapp-gcc}
DPDK_DIR=$RTE_SDK
diff --git a/repositories/ubuntu/ovs/ovs-install.sh b/repositories/ubuntu/ovs/ovs-install.sh
index a323817..b8d8af3 100755
--- a/repositories/ubuntu/ovs/ovs-install.sh
+++ b/repositories/ubuntu/ovs/ovs-install.sh
@@ -3,10 +3,11 @@ set -eux
INSTALL_HOME=/usr/share/ovs/
rm -rf $INSTALL_HOME ; mkdir -p $INSTALL_HOME
+OVS_VER='2.5.90'
cd $INSTALL_HOME
-wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/ovs/openvswitch-datapath-dkms_2.4.90-1_all.deb
-dpkg -i openvswitch-datapath-dkms_2.4.90-1_all.deb
-wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/ovs/openvswitch-common_2.4.90-1_amd64.deb
-dpkg -i openvswitch-common_2.4.90-1_amd64.deb
-wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/ovs/openvswitch-switch_2.4.90-1_amd64.deb
-dpkg -i openvswitch-switch_2.4.90-1_amd64.deb
+wget "http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/ovs/openvswitch-datapath-dkms_${OVS_VER}-1_all.deb"
+dpkg -i "openvswitch-datapath-dkms_${OVS_VER}-1_all.deb"
+wget "http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/ovs/openvswitch-common_${OVS_VER}-1_amd64.deb"
+dpkg -i "openvswitch-common_${OVS_VER}-1_amd64.deb"
+wget "http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/ovs/openvswitch-switch_${OVS_VER}-1_amd64.deb"
+dpkg -i "openvswitch-switch_${OVS_VER}-1_amd64.deb"