From 7590019023887d6f59c20248e6503cacfa491d8c Mon Sep 17 00:00:00 2001 From: lingyu1 Date: Fri, 27 Nov 2015 23:17:37 +0800 Subject: [PATCH] Implement: Fuel install OVS with NSH/DPDK Change-Id: I6933105a30fef4bc95e830ec86965c4417976454 Closes-Bug:#1507877 Signed-off-by: lingyu1 --- README.md | 11 +- .../puppet/manifests/ovs-install-compute.pp | 59 +- .../manifests/ovs-install-controller.pp | 64 +- .../ovs-install-primary-controller.pp | 64 +- deployment_tasks.yaml | 1 + metadata.yaml | 13 +- ovs-nsh/Dockerfile | 21 + ovs-nsh/build-ovs-nsh.sh | 4 + ovs-nsh/download-ovs-nsh.sh | 77 + ovs-nsh/patches/060679.patch | 3187 +++++++++++++++++ ovs-nsh/patches/060680.patch | 788 ++++ ovs-nsh/patches/060681.patch | 1025 ++++++ ovs-nsh/patches/060682.patch | 730 ++++ ovs-nsh/patches/060683.patch | 1334 +++++++ ovs-nsh/patches/060684.patch | 151 + ovs-nsh/patches/060685.patch | 675 ++++ ovs_package/centos/dependencies.txt | 8 - ovs_package/ubuntu/dependencies.txt | 3 - pre_build_hook | 27 +- repositories/centos/.gitkeep | 0 20 files changed, 8099 insertions(+), 143 deletions(-) create mode 100644 deployment_tasks.yaml create mode 100644 ovs-nsh/Dockerfile create mode 100755 ovs-nsh/build-ovs-nsh.sh create mode 100755 ovs-nsh/download-ovs-nsh.sh create mode 100644 ovs-nsh/patches/060679.patch create mode 100644 ovs-nsh/patches/060680.patch create mode 100644 ovs-nsh/patches/060681.patch create mode 100644 ovs-nsh/patches/060682.patch create mode 100644 ovs-nsh/patches/060683.patch create mode 100644 ovs-nsh/patches/060684.patch create mode 100644 ovs-nsh/patches/060685.patch delete mode 100644 ovs_package/centos/dependencies.txt delete mode 100644 ovs_package/ubuntu/dependencies.txt delete mode 100644 repositories/centos/.gitkeep diff --git a/README.md b/README.md index 243e349..e9be667 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Requirements | Requirement | Version/Comment | |----------------------------------|-----------------| -| Mirantis OpenStack compatibility | 6.1 | +| Mirantis OpenStack compatibility | 7.0 | Recommendations --------------- @@ -60,7 +60,7 @@ Openvswitch plugin installation id | name | version | package_version ---|-----------------|---------|---------------- - 1 | fuel-plugin-ovs | 0.5.0 | 2.0.0 + 1 | fuel-plugin-ovs | 0.5.1 | 3.0.0 8. Plugin is ready to use and can be enabled on the Settings tab of the Fuel web UI. @@ -102,13 +102,6 @@ Known issues None. -Release Notes -------------- - -**0.5.0** - -* Initial release of the plugin. This is a beta version. - Development =========== diff --git a/deployment_scripts/puppet/manifests/ovs-install-compute.pp b/deployment_scripts/puppet/manifests/ovs-install-compute.pp index 3ff0bf2..7f7a7ac 100644 --- a/deployment_scripts/puppet/manifests/ovs-install-compute.pp +++ b/deployment_scripts/puppet/manifests/ovs-install-compute.pp @@ -1,40 +1,39 @@ $fuel_settings = parseyaml(file('/etc/compute.yaml')) -$ovs_version = "2.4.90-1" if $operatingsystem == 'Ubuntu' { - package { 'openvswitch-datapath-dkms': - ensure => "${ovs_version}", - } - package { 'openvswitch-common': - ensure => "${ovs_version}", - } - package { 'openvswitch-switch': - ensure => "${ovs_version}", - require => Package['openvswitch-common','openvswitch-datapath-dkms'], - } -} elsif $operatingsystem == 'CentOS' { if $fuel_settings['fuel-plugin-ovs']['use_dpdk'] { - package { 'openvswitch': - ensure => "2.4.90-1.el6", + exec { "wget dpdk package": + command => "wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk-install.tar.gz", + path => "/usr/bin:/usr/sbin:/bin:/sbin", + } + exec { "unzip dpdk package": + command => "tar -xvzf /etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install.tar.gz", + path => "/usr/bin:/usr/sbin:/bin:/sbin", + } + exec { "install dpdk package": + command => "/etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install/dpdk-install.sh", + path => "/usr/bin:/usr/sbin:/bin:/sbin", + } + package { 'openvswitch-datapath-dkms': + ensure => "2.4.90-1", } - package { 'kmod-openvswitch': - ensure => "2.4.90-2.el6", + package { 'openvswitch-common': + ensure => "2.4.90-1", } - package { 'dpdk': - ensure => "2.1.0-6.el6", + package { 'openvswitch-switch': + ensure => "2.4.90-1", + require => Package['openvswitch-common','openvswitch-datapath-dkms'], } - package { 'dpdk-tools': - ensure => "2.1.0-6.el6", + } else { + package { 'openvswitch-datapath-dkms': + ensure => "2.4.90-1", } - package { 'dpdk-devel': - ensure => "2.1.0-6.el6", + package { 'openvswitch-common': + ensure => "2.4.90-1", } - - } else { - package { 'openvswitch': - ensure =>"2.4.90-1", + package { 'openvswitch-switch': + ensure => "2.4.90-1", + require => Package['openvswitch-common','openvswitch-datapath-dkms'], } - package { 'kmod-openvswitch': - ensure => "2.4.90-1.el6", - } - } + } +} elsif $operatingsystem == 'CentOS' { } diff --git a/deployment_scripts/puppet/manifests/ovs-install-controller.pp b/deployment_scripts/puppet/manifests/ovs-install-controller.pp index 9bf18a5..93323e0 100644 --- a/deployment_scripts/puppet/manifests/ovs-install-controller.pp +++ b/deployment_scripts/puppet/manifests/ovs-install-controller.pp @@ -1,39 +1,39 @@ $fuel_settings = parseyaml(file('/etc/controller.yaml')) -$ovs_version = "2.4.90-1" if $operatingsystem == 'Ubuntu' { - package { 'openvswitch-datapath-dkms': - ensure => "${ovs_version}", - } - package { 'openvswitch-common': - ensure => "${ovs_version}", - } - package { 'openvswitch-switch': - ensure => "${ovs_version}", - require => Package['openvswitch-common','openvswitch-datapath-dkms'], - } -} elsif $operatingsystem == 'CentOS' { if $fuel_settings['fuel-plugin-ovs']['use_dpdk'] { - package { 'openvswitch': - ensure => "2.4.90-1.el6", - } - package { 'kmod-openvswitch': - ensure => "2.4.90-2.el6", - } - package { 'dpdk': - ensure => "2.1.0-6.el6", - } - package { 'dpdk-tools': - ensure => "2.1.0-6.el6", - } - package { 'dpdk-devel': - ensure => "2.1.0-6.el6", - } - } else { - package { 'openvswitch': + exec { "wget dpdk package": + command => "wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk-install.tar.gz", + path => "/usr/bin:/usr/sbin:/bin:/sbin", + } + exec { "unzip dpdk package": + command => "tar -xvzf /etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install.tar.gz", + path => "/usr/bin:/usr/sbin:/bin:/sbin", + } + exec { "install dpdk package": + command => "/etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install/dpdk-install.sh", + path => "/usr/bin:/usr/sbin:/bin:/sbin", + } + package { 'openvswitch-datapath-dkms': ensure => "2.4.90-1", } - package { 'kmod-openvswitch': - ensure => "2.4.90-1.el6", + package { 'openvswitch-common': + ensure => "2.4.90-1", } - } + package { 'openvswitch-switch': + ensure => "2.4.90-1", + require => Package['openvswitch-common','openvswitch-datapath-dkms'], + } + } else { + package { 'openvswitch-datapath-dkms': + ensure => "2.4.90-1", + } + package { 'openvswitch-common': + ensure => "2.4.90-1", + } + package { 'openvswitch-switch': + ensure => "2.4.90-1", + require => Package['openvswitch-common','openvswitch-datapath-dkms'], + } + } +} elsif $operatingsystem == 'CentOS' { } diff --git a/deployment_scripts/puppet/manifests/ovs-install-primary-controller.pp b/deployment_scripts/puppet/manifests/ovs-install-primary-controller.pp index 07ac9f9..59a4721 100644 --- a/deployment_scripts/puppet/manifests/ovs-install-primary-controller.pp +++ b/deployment_scripts/puppet/manifests/ovs-install-primary-controller.pp @@ -1,39 +1,39 @@ $fuel_settings = parseyaml(file('/etc/primary-controller.yaml')) -$ovs_version = "2.4.90-1" if $operatingsystem == 'Ubuntu' { - package { 'openvswitch-datapath-dkms': - ensure => "${ovs_version}", - } - package { 'openvswitch-common': - ensure => "${ovs_version}", - } - package { 'openvswitch-switch': - ensure => "${ovs_version}", - require => Package['openvswitch-common','openvswitch-datapath-dkms'], - } -} elsif $operatingsystem == 'CentOS' { if $fuel_settings['fuel-plugin-ovs']['use_dpdk'] { - package { 'openvswitch': - ensure => "2.4.90-1.el6", - } - package { 'kmod-openvswitch': - ensure => "2.4.90-2.el6", - } - package { 'dpdk': - ensure => "2.1.0-6.el6", - } - package { 'dpdk-tools': - ensure => "2.1.0-6.el6", - } - package { 'dpdk-devel': - ensure => "2.1.0-6.el6", - } - } else { - package { 'openvswitch': + exec { "wget dpdk package": + command => "wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk-install.tar.gz", + path => "/usr/bin:/usr/sbin:/bin:/sbin", + } + exec { "unzip dpdk package": + command => "tar -xvzf /etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install.tar.gz", + path => "/usr/bin:/usr/sbin:/bin:/sbin", + } + exec { "install dpdk package": + command => "/etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install/dpdk-install.sh", + path => "/usr/bin:/usr/sbin:/bin:/sbin", + } + package { 'openvswitch-datapath-dkms': ensure => "2.4.90-1", } - package { 'kmod-openvswitch': - ensure => "2.4.90-1.el6", + package { 'openvswitch-common': + ensure => "2.4.90-1", } - } + package { 'openvswitch-switch': + ensure => "2.4.90-1", + require => Package['openvswitch-common','openvswitch-datapath-dkms'], + } + } else { + package { 'openvswitch-datapath-dkms': + ensure => "2.4.90-1", + } + package { 'openvswitch-common': + ensure => "2.4.90-1", + } + package { 'openvswitch-switch': + ensure => "2.4.90-1", + require => Package['openvswitch-common','openvswitch-datapath-dkms'], + } + } +} elsif $operatingsystem == 'CentOS' { } diff --git a/deployment_tasks.yaml b/deployment_tasks.yaml new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/deployment_tasks.yaml @@ -0,0 +1 @@ +[] diff --git a/metadata.yaml b/metadata.yaml index aad5f5b..aba3d3b 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -3,11 +3,11 @@ name: fuel-plugin-ovs # Human-readable name for your plugin title: Openvswitch with NSH support # Plugin version -version: '0.5.0' +version: '0.5.1' # Description description: 'This plugin provides to deploy openvswitch with nsh' # Required fuel version -fuel_version: ['6.1'] +fuel_version: ['7.0'] # Specify license of your plugin licenses: ['Apache License Version 2.0'] # Specify author or company name @@ -21,15 +21,10 @@ groups: ['network'] # The plugin is compatible with releases in the list releases: - os: ubuntu - version: 2014.2-6.1 + version: 2015.1.0-7.0 mode: ['ha', 'multinode'] deployment_scripts_path: deployment_scripts/ repository_path: repositories/ubuntu - - os: centos - version: 2014.2-6.1 - mode: ['ha', 'multinode'] - deployment_scripts_path: deployment_scripts/ - repository_path: repositories/centos # Version of plugin package -package_version: '2.0.0' +package_version: '3.0.0' diff --git a/ovs-nsh/Dockerfile b/ovs-nsh/Dockerfile new file mode 100644 index 0000000..e1cafb3 --- /dev/null +++ b/ovs-nsh/Dockerfile @@ -0,0 +1,21 @@ +#!/bin/bash + +FROM ubuntu:14.04.3 +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 + +RUN echo "ALL ALL=NOPASSWD: ALL" > /etc/sudoers.d/open-sudo +RUN chmod 0440 /etc/sudoers.d/open-sudo + +ADD ./patches /patches + +ADD ./download-ovs-nsh.sh /root/download-ovs-nsh.sh +RUN chmod +x /root/download-ovs-nsh.sh +RUN /root/download-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 diff --git a/ovs-nsh/build-ovs-nsh.sh b/ovs-nsh/build-ovs-nsh.sh new file mode 100755 index 0000000..779e9cb --- /dev/null +++ b/ovs-nsh/build-ovs-nsh.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cd /openvswitch;./boot.sh;./configure;make dist;tar -xzf openvswitch-2.4.90.tar.gz +cd /openvswitch/openvswitch-2.4.90;dpkg-checkbuilddeps;DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary diff --git a/ovs-nsh/download-ovs-nsh.sh b/ovs-nsh/download-ovs-nsh.sh new file mode 100755 index 0000000..bb50f72 --- /dev/null +++ b/ovs-nsh/download-ovs-nsh.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# Author: Johnson Li +# Change Log: +# 10-28-2015: Initial version +# +# This script helps to setup the NSH test environment. +# Since the NSH patches are not merged into OVS's mainline, +# users cannot test the feature easily. +# In order to get OVS installed for tests, this script helps +# to get patch files from the mail archivements and apply +# the patches to a special commit of OVS. +# +# This script is for free use, feel free to modify the script +# for your own use. Any questions about the installation script, +# please send an email to the author. + + +######################## Global Variables ##################### +WORK_DIR=`pwd` +PATCHES="060679 060680 060681 060682 060683 060684 060685" +URL_OVS=https://github.com/openvswitch/ovs.git +OVS_COMMIT=121daded51b9798fe3722824b27a05c16806cbd1 + +######################## Functions ############################ + +################################################################ +# Function Name: get_ovs_codes_from_github() +# Desription: Clone sources for OVS from github. +################################################################ +function get_ovs_codes_from_github() +{ + git clone ${URL_OVS} openvswitch 2>&1 | tee -a ${WORK_DIR}/install.log + return $? +} + +################################################################ +# Function Name: apply_patches_to_ovs() +# Desription: Apply patches to a specific commit of OVS. +################################################################ +function apply_patches_to_ovs() +{ + if [ ! -d openvswitch ] ;then + echo "No source found for Openvswitch, exit!" | tee -a ${WORK_DIR}/install.log + return -1 + fi + + if [ ! -d patches ] ;then + echo "No source found for Openvswitch, exit!" | tee -a ${WORK_DIR}/install.log + return -1 + fi + + cd openvswitch + git checkout ${OVS_COMMIT} -b development 2>&1 | tee -a ${WORK_DIR}/install.log + for patch in ${PATCHES} + do + patch -p1 < ${WORK_DIR}/patches/${patch}.patch 2>&1 | tee -a ${WORK_DIR}/install.log + done + + cd ${WORK_DIR} + return 0 +} + +################### MAIN ############################ +get_ovs_codes_from_github +if [ $? -ne 0 ] ;then + echo "Error occured when cloning OVS, exit." | tee -a ${WORK_DIR}/install.log + exit 1 +fi + +apply_patches_to_ovs +if [ $? -ne 0 ] ;then + echo "Error occured when applying patches, exit." | tee -a ${WORK_DIR}/install.log + exit 1 +fi + +exit 0 diff --git a/ovs-nsh/patches/060679.patch b/ovs-nsh/patches/060679.patch new file mode 100644 index 0000000..635cf1d --- /dev/null +++ b/ovs-nsh/patches/060679.patch @@ -0,0 +1,3187 @@ +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 new file mode 100644 index 0000000..538d2df --- /dev/null +++ b/ovs-nsh/patches/060680.patch @@ -0,0 +1,788 @@ +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 new file mode 100644 index 0000000..f5afed1 --- /dev/null +++ b/ovs-nsh/patches/060681.patch @@ -0,0 +1,1025 @@ +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 new file mode 100644 index 0000000..c6ccd25 --- /dev/null +++ b/ovs-nsh/patches/060682.patch @@ -0,0 +1,730 @@ +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 new file mode 100644 index 0000000..008f430 --- /dev/null +++ b/ovs-nsh/patches/060683.patch @@ -0,0 +1,1334 @@ +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 new file mode 100644 index 0000000..329ac5c --- /dev/null +++ b/ovs-nsh/patches/060684.patch @@ -0,0 +1,151 @@ +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 new file mode 100644 index 0000000..a9da911 --- /dev/null +++ b/ovs-nsh/patches/060685.patch @@ -0,0 +1,675 @@ +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_package/centos/dependencies.txt b/ovs_package/centos/dependencies.txt deleted file mode 100644 index bc830eb..0000000 --- a/ovs_package/centos/dependencies.txt +++ /dev/null @@ -1,8 +0,0 @@ -https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/dpdk-2.1.0-6.el6.x86_64.rpm?raw=true -https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/dpdk-devel-2.1.0-6.el6.x86_64.rpm?raw=true -https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/dpdk-tools-2.1.0-6.el6.x86_64.rpm?raw=true -https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/kmod-openvswitch-2.4.90-1.el6.x86_64.rpm?raw=true -https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/kmod-openvswitch-2.4.90-2.el6.x86_64.rpm?raw=true -https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/openvswitch-2.4.90-1.el6.x86_64.rpm?raw=true -https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/openvswitch-2.4.90-1.x86_64.rpm?raw=true - diff --git a/ovs_package/ubuntu/dependencies.txt b/ovs_package/ubuntu/dependencies.txt deleted file mode 100644 index db37074..0000000 --- a/ovs_package/ubuntu/dependencies.txt +++ /dev/null @@ -1,3 +0,0 @@ -https://github.com/lingyu1/fuel-rpm/blob/master/ubuntu/14.04.1/x86_64/Packages/openvswitch-common_2.4.90-1_amd64.deb?raw=true -https://github.com/lingyu1/fuel-rpm/blob/master/ubuntu/14.04.1/x86_64/Packages/openvswitch-datapath-dkms_2.4.90-1_all.deb?raw=true -https://github.com/lingyu1/fuel-rpm/blob/master/ubuntu/14.04.1/x86_64/Packages/openvswitch-switch_2.4.90-1_amd64.deb?raw=true diff --git a/pre_build_hook b/pre_build_hook index d088c41..8813ce3 100755 --- a/pre_build_hook +++ b/pre_build_hook @@ -5,36 +5,23 @@ # The script should return 0 if there were no errors. set -eux -BUILD_FOR=${BUILD_FOR:-centos ubuntu} +BUILD_FOR=${BUILD_FOR:-ubuntu} DIR="$(dirname `readlink -f $0`)" -INCLUDE_DEPENDENCIES=${INCLUDE_DEPENDENCIES:-true} - -function download_dependencies { - if [ "$INCLUDE_DEPENDENCIES" = true ] - then - wget --content-disposition -N -i "${DIR}/ovs_package/${1}/dependencies.txt" - fi -} - - function build_pkg { case $1 in - centos) - pushd "${DIR}/repositories/${1}/" - download_dependencies ${1} - popd - ;; ubuntu) - pushd "${DIR}/repositories/${1}/" - download_dependencies ${1} - popd + 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/ + sudo docker cp $container_id:/openvswitch/openvswitch-datapath-dkms_2.4.90-1_all.deb ${DIR}/repositories/ubuntu/ + sudo docker cp $container_id:/openvswitch/openvswitch-switch_2.4.90-1_amd64.deb ${DIR}/repositories/ubuntu/ ;; *) echo "Not supported system"; exit 1;; esac } - for system in $BUILD_FOR do build_pkg $system diff --git a/repositories/centos/.gitkeep b/repositories/centos/.gitkeep deleted file mode 100644 index e69de29..0000000