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"