Browse Source

Implement: Fuel install OVS with NSH/DPDK

Change-Id: I6933105a30fef4bc95e830ec86965c4417976454
Closes-Bug:#1507877
Signed-off-by: lingyu1 <ling.y.yu@intel.com>
lingyu1 3 years ago
parent
commit
7590019023

+ 2
- 9
README.md View File

@@ -14,7 +14,7 @@ Requirements
14 14
 
15 15
 | Requirement                      | Version/Comment |
16 16
 |----------------------------------|-----------------|
17
-| Mirantis OpenStack compatibility | 6.1             |
17
+| Mirantis OpenStack compatibility | 7.0             |
18 18
 
19 19
 Recommendations
20 20
 ---------------
@@ -60,7 +60,7 @@ Openvswitch plugin installation
60 60
 
61 61
         id | name            | version | package_version
62 62
         ---|-----------------|---------|----------------
63
-        1  | fuel-plugin-ovs | 0.5.0   | 2.0.0
63
+        1  | fuel-plugin-ovs | 0.5.1   | 3.0.0
64 64
 
65 65
 8. Plugin is ready to use and can be enabled on the Settings tab of the Fuel web UI.
66 66
 
@@ -102,13 +102,6 @@ Known issues
102 102
 
103 103
 None.
104 104
 
105
-Release Notes
106
--------------
107
-
108
-**0.5.0**
109
-
110
-* Initial release of the plugin. This is a beta version.
111
-
112 105
 
113 106
 Development
114 107
 ===========

+ 29
- 30
deployment_scripts/puppet/manifests/ovs-install-compute.pp View File

@@ -1,40 +1,39 @@
1 1
 $fuel_settings = parseyaml(file('/etc/compute.yaml'))
2
-$ovs_version = "2.4.90-1"
3 2
 if $operatingsystem == 'Ubuntu' {
4
-        package { 'openvswitch-datapath-dkms':
5
-                ensure => "${ovs_version}",
6
-        }
7
-        package { 'openvswitch-common':
8
-                ensure => "${ovs_version}",
9
-        }
10
-        package { 'openvswitch-switch':
11
-                ensure => "${ovs_version}",
12
-                require => Package['openvswitch-common','openvswitch-datapath-dkms'],
13
-        }
14
-} elsif $operatingsystem == 'CentOS' {
15 3
         if $fuel_settings['fuel-plugin-ovs']['use_dpdk'] {
16
-                package { 'openvswitch':
17
-                        ensure => "2.4.90-1.el6",
18
-                }
19
-                package { 'kmod-openvswitch':
20
-                        ensure => "2.4.90-2.el6",
4
+		exec { "wget dpdk package":
5
+		        command => "wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk-install.tar.gz",
6
+		        path   => "/usr/bin:/usr/sbin:/bin:/sbin",
7
+		}
8
+		exec { "unzip dpdk package":
9
+		        command => "tar -xvzf /etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install.tar.gz",
10
+		        path   => "/usr/bin:/usr/sbin:/bin:/sbin",
11
+		}
12
+		exec { "install dpdk package":
13
+		        command => "/etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install/dpdk-install.sh",
14
+		        path   => "/usr/bin:/usr/sbin:/bin:/sbin",
15
+		}
16
+                package { 'openvswitch-datapath-dkms':
17
+                        ensure => "2.4.90-1",
21 18
                 }
22
-                package { 'dpdk':
23
-                        ensure => "2.1.0-6.el6",
19
+                package { 'openvswitch-common':
20
+                        ensure => "2.4.90-1",
24 21
                 }
25
-                package { 'dpdk-tools':
26
-                        ensure => "2.1.0-6.el6",
22
+                package { 'openvswitch-switch':
23
+                        ensure => "2.4.90-1",
24
+                        require => Package['openvswitch-common','openvswitch-datapath-dkms'],
27 25
                 }
28
-                package { 'dpdk-devel':
29
-                        ensure => "2.1.0-6.el6",
26
+	} else {
27
+                package { 'openvswitch-datapath-dkms':
28
+                        ensure => "2.4.90-1",
30 29
                 }
31
-
32
-        } else {
33
-                package { 'openvswitch':
34
-                        ensure =>"2.4.90-1",
30
+                package { 'openvswitch-common':
31
+                        ensure => "2.4.90-1",
35 32
                 }
36
-                package { 'kmod-openvswitch':
37
-                        ensure => "2.4.90-1.el6",
33
+                package { 'openvswitch-switch':
34
+                        ensure => "2.4.90-1",
35
+                        require => Package['openvswitch-common','openvswitch-datapath-dkms'],
38 36
                 }
39
-        }
37
+	}
38
+} elsif $operatingsystem == 'CentOS' {
40 39
 }

+ 28
- 28
deployment_scripts/puppet/manifests/ovs-install-controller.pp View File

@@ -1,39 +1,39 @@
1 1
 $fuel_settings = parseyaml(file('/etc/controller.yaml'))
2
-$ovs_version = "2.4.90-1"
3 2
 if $operatingsystem == 'Ubuntu' {
4
-        package { 'openvswitch-datapath-dkms':
5
-                ensure => "${ovs_version}",
6
-        }
7
-        package { 'openvswitch-common':
8
-                ensure => "${ovs_version}",
9
-        }
10
-        package { 'openvswitch-switch':
11
-                ensure => "${ovs_version}",
12
-                require => Package['openvswitch-common','openvswitch-datapath-dkms'],
13
-        }
14
-} elsif $operatingsystem == 'CentOS' {
15 3
         if $fuel_settings['fuel-plugin-ovs']['use_dpdk'] {
16
-                package { 'openvswitch':
17
-                        ensure => "2.4.90-1.el6",
18
-                }
19
-                package { 'kmod-openvswitch':
20
-                        ensure => "2.4.90-2.el6",
4
+		exec { "wget dpdk package":
5
+		        command => "wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk-install.tar.gz",
6
+		        path   => "/usr/bin:/usr/sbin:/bin:/sbin",
7
+		}
8
+		exec { "unzip dpdk package":
9
+		        command => "tar -xvzf /etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install.tar.gz",
10
+		        path   => "/usr/bin:/usr/sbin:/bin:/sbin",
11
+		}
12
+		exec { "install dpdk package":
13
+		        command => "/etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install/dpdk-install.sh",
14
+		        path   => "/usr/bin:/usr/sbin:/bin:/sbin",
15
+		}
16
+                package { 'openvswitch-datapath-dkms':
17
+                        ensure => "2.4.90-1",
21 18
                 }
22
-                package { 'dpdk':
23
-                        ensure => "2.1.0-6.el6",
19
+                package { 'openvswitch-common':
20
+                        ensure => "2.4.90-1",
24 21
                 }
25
-                package { 'dpdk-tools':
26
-                        ensure => "2.1.0-6.el6",
22
+                package { 'openvswitch-switch':
23
+                        ensure => "2.4.90-1",
24
+                        require => Package['openvswitch-common','openvswitch-datapath-dkms'],
27 25
                 }
28
-                package { 'dpdk-devel':
29
-                        ensure => "2.1.0-6.el6",
26
+	} else {
27
+                package { 'openvswitch-datapath-dkms':
28
+                        ensure => "2.4.90-1",
30 29
                 }
31
-        } else {
32
-                package { 'openvswitch':
30
+                package { 'openvswitch-common':
33 31
                         ensure => "2.4.90-1",
34 32
                 }
35
-                package { 'kmod-openvswitch':
36
-                        ensure => "2.4.90-1.el6",
33
+                package { 'openvswitch-switch':
34
+                        ensure => "2.4.90-1",
35
+                        require => Package['openvswitch-common','openvswitch-datapath-dkms'],
37 36
                 }
38
-        }
37
+	}
38
+} elsif $operatingsystem == 'CentOS' {
39 39
 }

+ 28
- 28
deployment_scripts/puppet/manifests/ovs-install-primary-controller.pp View File

@@ -1,39 +1,39 @@
1 1
 $fuel_settings = parseyaml(file('/etc/primary-controller.yaml'))
2
-$ovs_version = "2.4.90-1"
3 2
 if $operatingsystem == 'Ubuntu' {
4
-        package { 'openvswitch-datapath-dkms':
5
-                ensure => "${ovs_version}",
6
-        }
7
-        package { 'openvswitch-common':
8
-                ensure => "${ovs_version}",
9
-        }
10
-        package { 'openvswitch-switch':
11
-                ensure => "${ovs_version}",
12
-                require => Package['openvswitch-common','openvswitch-datapath-dkms'],
13
-        }
14
-} elsif $operatingsystem == 'CentOS' {
15 3
         if $fuel_settings['fuel-plugin-ovs']['use_dpdk'] {
16
-                package { 'openvswitch':
17
-                        ensure => "2.4.90-1.el6",
18
-                }
19
-                package { 'kmod-openvswitch':
20
-                        ensure => "2.4.90-2.el6",
4
+		exec { "wget dpdk package":
5
+		        command => "wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk-install.tar.gz",
6
+		        path   => "/usr/bin:/usr/sbin:/bin:/sbin",
7
+		}
8
+		exec { "unzip dpdk package":
9
+		        command => "tar -xvzf /etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install.tar.gz",
10
+		        path   => "/usr/bin:/usr/sbin:/bin:/sbin",
11
+		}
12
+		exec { "install dpdk package":
13
+		        command => "/etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install/dpdk-install.sh",
14
+		        path   => "/usr/bin:/usr/sbin:/bin:/sbin",
15
+		}
16
+                package { 'openvswitch-datapath-dkms':
17
+                        ensure => "2.4.90-1",
21 18
                 }
22
-                package { 'dpdk':
23
-                        ensure => "2.1.0-6.el6",
19
+                package { 'openvswitch-common':
20
+                        ensure => "2.4.90-1",
24 21
                 }
25
-                package { 'dpdk-tools':
26
-                        ensure => "2.1.0-6.el6",
22
+                package { 'openvswitch-switch':
23
+                        ensure => "2.4.90-1",
24
+                        require => Package['openvswitch-common','openvswitch-datapath-dkms'],
27 25
                 }
28
-                package { 'dpdk-devel':
29
-                        ensure => "2.1.0-6.el6",
26
+	} else {
27
+                package { 'openvswitch-datapath-dkms':
28
+                        ensure => "2.4.90-1",
30 29
                 }
31
-        } else {
32
-                package { 'openvswitch':
30
+                package { 'openvswitch-common':
33 31
                         ensure => "2.4.90-1",
34 32
                 }
35
-                package { 'kmod-openvswitch':
36
-                        ensure => "2.4.90-1.el6",
33
+                package { 'openvswitch-switch':
34
+                        ensure => "2.4.90-1",
35
+                        require => Package['openvswitch-common','openvswitch-datapath-dkms'],
37 36
                 }
38
-        }
37
+	}
38
+} elsif $operatingsystem == 'CentOS' {
39 39
 }

+ 1
- 0
deployment_tasks.yaml View File

@@ -0,0 +1 @@
1
+[]

+ 4
- 9
metadata.yaml View File

@@ -3,11 +3,11 @@ name: fuel-plugin-ovs
3 3
 # Human-readable name for your plugin
4 4
 title: Openvswitch with NSH support
5 5
 # Plugin version
6
-version: '0.5.0'
6
+version: '0.5.1'
7 7
 # Description
8 8
 description: 'This plugin provides to deploy openvswitch with nsh'
9 9
 # Required fuel version
10
-fuel_version: ['6.1']
10
+fuel_version: ['7.0']
11 11
 # Specify license of your plugin
12 12
 licenses: ['Apache License Version 2.0']
13 13
 # Specify author or company name
@@ -21,15 +21,10 @@ groups: ['network']
21 21
 # The plugin is compatible with releases in the list
22 22
 releases:
23 23
   - os: ubuntu
24
-    version: 2014.2-6.1
24
+    version: 2015.1.0-7.0
25 25
     mode: ['ha', 'multinode']
26 26
     deployment_scripts_path: deployment_scripts/
27 27
     repository_path: repositories/ubuntu
28
-  - os: centos
29
-    version: 2014.2-6.1
30
-    mode: ['ha', 'multinode']
31
-    deployment_scripts_path: deployment_scripts/
32
-    repository_path: repositories/centos
33 28
 
34 29
 # Version of plugin package
35
-package_version: '2.0.0'
30
+package_version: '3.0.0'

+ 21
- 0
ovs-nsh/Dockerfile View File

@@ -0,0 +1,21 @@
1
+#!/bin/bash
2
+
3
+FROM ubuntu:14.04.3
4
+RUN apt-get update
5
+RUN apt-get install -y software-properties-common python-software-properties \
6
+    make python-setuptools python-all dpkg-dev debhelper \
7
+    fuseiso git genisoimage bind9-host wget curl lintian tmux lxc iptables \
8
+    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
9
+
10
+RUN echo "ALL ALL=NOPASSWD: ALL" > /etc/sudoers.d/open-sudo
11
+RUN chmod 0440 /etc/sudoers.d/open-sudo
12
+
13
+ADD ./patches /patches
14
+
15
+ADD ./download-ovs-nsh.sh /root/download-ovs-nsh.sh
16
+RUN chmod +x /root/download-ovs-nsh.sh
17
+RUN /root/download-ovs-nsh.sh
18
+
19
+ADD ./build-ovs-nsh.sh /root/build-ovs-nsh.sh
20
+RUN chmod +x /root/build-ovs-nsh.sh
21
+RUN /root/build-ovs-nsh.sh

+ 4
- 0
ovs-nsh/build-ovs-nsh.sh View File

@@ -0,0 +1,4 @@
1
+#!/bin/bash
2
+
3
+cd /openvswitch;./boot.sh;./configure;make dist;tar -xzf openvswitch-2.4.90.tar.gz
4
+cd /openvswitch/openvswitch-2.4.90;dpkg-checkbuilddeps;DEB_BUILD_OPTIONS='parallel=8 nocheck' fakeroot debian/rules binary

+ 77
- 0
ovs-nsh/download-ovs-nsh.sh View File

@@ -0,0 +1,77 @@
1
+#!/bin/bash
2
+
3
+# Author: Johnson Li <johnson.li@intel.com>
4
+# Change Log:
5
+#     10-28-2015: Initial version
6
+#
7
+# This script helps to setup the NSH test environment.
8
+# Since the NSH patches are not merged into OVS's mainline,
9
+# users cannot test the feature easily.
10
+# In order to get OVS installed for tests, this script helps
11
+# to get patch files from the mail archivements and apply
12
+# the patches to a special commit of OVS.
13
+#
14
+# This script is for free use, feel free to modify the script
15
+# for your own use. Any questions about the installation script,
16
+# please send an email to the author.
17
+
18
+
19
+######################## Global Variables #####################
20
+WORK_DIR=`pwd`
21
+PATCHES="060679 060680 060681 060682 060683 060684 060685"
22
+URL_OVS=https://github.com/openvswitch/ovs.git
23
+OVS_COMMIT=121daded51b9798fe3722824b27a05c16806cbd1
24
+
25
+######################## Functions  ############################
26
+
27
+################################################################
28
+# Function Name: get_ovs_codes_from_github()
29
+# Desription: Clone sources for OVS from github.
30
+################################################################
31
+function get_ovs_codes_from_github()
32
+{
33
+    git clone ${URL_OVS} openvswitch 2>&1 | tee -a ${WORK_DIR}/install.log
34
+    return $?
35
+}
36
+
37
+################################################################
38
+# Function Name: apply_patches_to_ovs()
39
+# Desription: Apply patches to a specific commit of OVS.
40
+################################################################
41
+function apply_patches_to_ovs()
42
+{
43
+    if [ ! -d openvswitch ] ;then
44
+        echo "No source found for Openvswitch, exit!"  | tee -a ${WORK_DIR}/install.log
45
+        return -1
46
+    fi
47
+
48
+    if [ ! -d patches ] ;then
49
+        echo "No source found for Openvswitch, exit!" | tee -a ${WORK_DIR}/install.log
50
+        return -1
51
+    fi
52
+
53
+    cd openvswitch
54
+    git checkout ${OVS_COMMIT} -b development 2>&1 | tee -a ${WORK_DIR}/install.log
55
+    for patch in ${PATCHES}
56
+    do
57
+        patch -p1 < ${WORK_DIR}/patches/${patch}.patch 2>&1 | tee -a ${WORK_DIR}/install.log
58
+    done
59
+
60
+    cd ${WORK_DIR}
61
+    return 0
62
+}
63
+
64
+###################        MAIN        ############################
65
+get_ovs_codes_from_github
66
+if [ $? -ne 0 ] ;then
67
+    echo "Error occured when cloning OVS, exit." | tee -a ${WORK_DIR}/install.log
68
+    exit 1
69
+fi
70
+
71
+apply_patches_to_ovs
72
+if [ $? -ne 0 ] ;then
73
+    echo "Error occured when applying patches, exit." | tee -a ${WORK_DIR}/install.log
74
+    exit 1
75
+fi
76
+
77
+exit 0

+ 3187
- 0
ovs-nsh/patches/060679.patch
File diff suppressed because it is too large
View File


+ 788
- 0
ovs-nsh/patches/060680.patch View File

@@ -0,0 +1,788 @@
1
+at data plane level in user space and modify the related codes at control plane
2
+level in user space.
3
+ 
4
+The design is based on basic VxLAN impletation. When packets are received at
5
+data plane level in user space, function 'dp_netdev_input' will be called for
6
+processing the packets at data plane level in user space.
7
+ 
8
+When VXLAN-GPE NSH packets are received, decapsulation will be implemented. For
9
+the first time, the packets are sent to control plane by function 'upcall_cb',
10
+and tunnel port will be lookuped by matching the UDP port which is 4790 for
11
+VxLAN-GPE NSH port, if VxLAN-GPE NSH tunnel port are matched
12
+successfully, the tunnel pop action will be appended and implemented at data
13
+plane level, and the NSH related field will be parsed, then packets will be
14
+reprocessed by function 'dp_netdev_input'.
15
+ 
16
+When original packets are sent to VxLAN-GPE NSH port, the encapsulation will
17
+be implemented. For the first time, in the control plane the tunnel
18
+tunnel_push_data are built according to VxLAN-GPE NSH port configuration and
19
+related rules, then the tunnel push actions are appended and implemented at
20
+data plane level. Finally packets will be reprocessed by function
21
+'dp_netdev_input'.
22
+ 
23
+Signed-off-by: Ricky Li <<A HREF="http://openvswitch.org/mailman/listinfo/dev">ricky.li at intel.com</A>>
24
+Signed-off-by: Mengke Liu <<A HREF="http://openvswitch.org/mailman/listinfo/dev">mengke.liu at intel.com</A>>
25
+---
26
+ lib/netdev-vport.c           | 166 +++++++++++++++++++++++++++++++++++-----
27
+ lib/odp-util.c               | 175 ++++++++++++++++++++++++++++++-------------
28
+ lib/packets.h                | 115 +++++++++++++++++++++++++++-
29
+ ofproto/ofproto-dpif-xlate.c |   1 -
30
+ tests/tunnel.at              | 118 +++++++++++++++++++++++++++++
31
+ 5 files changed, 500 insertions(+), 75 deletions(-)
32
+ 
33
+diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
34
+index 3f85386..a0a4da2 100644
35
+--- a/lib/netdev-vport.c
36
++++ b/lib/netdev-vport.c
37
+@@ -67,6 +67,12 @@ static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5);
38
+                             sizeof(struct udp_header) +         \
39
+                             sizeof(struct genevehdr))
40
+ 
41
++#define VXNSH_HLEN   (sizeof(struct eth_header) +         \
42
++                      sizeof(struct ip_header)  +         \
43
++                      sizeof(struct udp_header) +         \
44
++                      sizeof(struct vxgpehdr)   +         \
45
++                      sizeof(struct nshhdr))
46
++
47
+ #define DEFAULT_TTL 64
48
+ 
49
+ struct netdev_vport {
50
+@@ -1462,29 +1468,69 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
51
+ {
52
+     struct pkt_metadata *md = &packet->md;
53
+     struct flow_tnl *tnl = &md->tunnel;
54
+-    struct vxlanhdr *vxh;
55
++    struct udp_header *udp;
56
+ 
57
+     pkt_metadata_init_tnl(md);
58
+     if (VXLAN_HLEN > dp_packet_size(packet)) {
59
+         return EINVAL;
60
+     }
61
+ 
62
+-    vxh = udp_extract_tnl_md(packet, tnl);
63
+-    if (!vxh) {
64
+-        return EINVAL;
65
++    udp = ip_extract_tnl_md(packet, tnl);
66
++    if (!udp) {
67
++        return EINVAL;;
68
+     }
69
+ 
70
+-    if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
71
+-       (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
72
+-        VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
73
+-                     ntohl(get_16aligned_be32(&vxh->vx_flags)),
74
+-                     ntohl(get_16aligned_be32(&vxh->vx_vni)));
75
+-        return EINVAL;
76
+-    }
77
+-    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
78
+-    tnl->flags |= FLOW_TNL_F_KEY;
79
++    if (ntohs(udp->udp_dst) == VXGPE_DST_PORT) {
80
++
81
++        struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
82
++
83
++        if (get_16aligned_be32(&vxg->vx_vni) & htonl(0xff)) {
84
++            VLOG_WARN_RL(&err_rl, "invalid vxlan-gpe vni=%#x\n",
85
++                         ntohl(get_16aligned_be32(&vxg->vx_vni)));
86
++            return EINVAL;;
87
++        }
88
++
89
++        tnl->tp_src = udp->udp_src;
90
++        tnl->tp_dst = udp->udp_dst;
91
++        tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxg->vx_vni)) >> 8);
92
++
93
++        if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
94
++            struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
95
++
96
++            tnl->nsp = nsh->b.b2 << 8;
97
++            tnl->nsi = nsh->b.svc_idx;
98
++            tnl->nshc1 = nsh->c.nshc1;
99
++            tnl->nshc2 = nsh->c.nshc2;
100
++            tnl->nshc3 = nsh->c.nshc3;
101
++            tnl->nshc4 = nsh->c.nshc4;
102
++            tnl->flags |= FLOW_TNL_F_NSP;
103
++            tnl->flags |= FLOW_TNL_F_NSI;
104
++            tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
105
++                        FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
106
++
107
++            dp_packet_reset_packet(packet, VXNSH_HLEN);
108
++        } else {
109
++            VLOG_WARN("Unsupported vxlan GPE + NSH format!");
110
++            return EINVAL;;
111
++        }
112
++
113
++    } else {
114
++
115
++        struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
116
+ 
117
+-    dp_packet_reset_packet(packet, VXLAN_HLEN);
118
++        if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
119
++               (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
120
++            VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
121
++                         ntohl(get_16aligned_be32(&vxh->vx_flags)),
122
++                         ntohl(get_16aligned_be32(&vxh->vx_vni)));
123
++            return EINVAL;;
124
++        }
125
++
126
++        tnl->tp_src = udp->udp_src;
127
++        tnl->tp_dst = udp->udp_dst;
128
++        tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
129
++        dp_packet_reset_packet(packet, VXLAN_HLEN);
130
++    }
131
+ 
132
+     return 0;
133
+ }
134
+@@ -1496,23 +1542,103 @@ netdev_vxlan_build_header(const struct netdev *netdev,
135
+ {
136
+     struct netdev_vport *dev = netdev_vport_cast(netdev);
137
+     struct netdev_tunnel_config *tnl_cfg;
138
+-    struct vxlanhdr *vxh;
139
++    struct ip_header *ip;
140
++    struct udp_header *udp;
141
++    bool isnsh = false;
142
+ 
143
+     /* XXX: RCUfy tnl_cfg. */
144
+     ovs_mutex_lock(&dev->mutex);
145
+     tnl_cfg = &dev->tnl_cfg;
146
+ 
147
+-    vxh = udp_build_header(tnl_cfg, tnl_flow, data);
148
++    ip = ip_hdr(data->header);
149
++    ip->ip_proto = IPPROTO_UDP;
150
++
151
++    udp = (struct udp_header *) (ip + 1);
152
++    udp->udp_dst = tnl_cfg->dst_port;
153
++
154
++    if (tnl_flow->tunnel.flags & FLOW_TNL_F_CSUM) {
155
++            /* Write a value in now to mark that we should compute the checksum
156
++             * later. 0xffff is handy because it is transparent to the
157
++             * calculation. */
158
++            udp->udp_csum = htons(0xffff);
159
++    }
160
++
161
++    if (ntohs(udp->udp_dst) == VXGPE_DST_PORT){
162
++        struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
163
+ 
164
+-    put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
165
+-    put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
166
++        memset(vxg, 0, sizeof *vxg);
167
++        vxg->i = 0x01;
168
++        vxg->p = 0x01;
169
++        vxg->ver = 0x01;
170
++        vxg->proto = VXG_P_NSH;
171
++        put_16aligned_be32(&vxg->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
172
++
173
++        if (vxg->p && vxg->proto == VXG_P_NSH){
174
++            struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
175
++
176
++            memset(nsh, 0, sizeof *nsh);
177
++            nsh->b.ver = 0x01;
178
++            nsh->b.len = 6;
179
++            nsh->b.mdtype = NSH_M_TYPE1;
180
++            nsh->b.proto = NSH_P_ETHERNET;
181
++
182
++            nsh->b.b2 = tnl_flow->tunnel.nsp >> 8;
183
++            nsh->b.svc_idx = tnl_flow->tunnel.nsi;
184
++
185
++            nsh->c.nshc1 = tnl_flow->tunnel.nshc1;
186
++            nsh->c.nshc2 = tnl_flow->tunnel.nshc2;
187
++            nsh->c.nshc3 = tnl_flow->tunnel.nshc3;
188
++            nsh->c.nshc4 = tnl_flow->tunnel.nshc4;
189
++
190
++            isnsh = true;
191
++        }
192
++
193
++    } else {
194
++        struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
195
++        put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
196
++        put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
197
++    }
198
+ 
199
+     ovs_mutex_unlock(&dev->mutex);
200
+-    data->header_len = VXLAN_HLEN;
201
++
202
++    if(isnsh)
203
++        data->header_len = VXNSH_HLEN;
204
++    else
205
++        data->header_len = VXLAN_HLEN;
206
+     data->tnl_type = OVS_VPORT_TYPE_VXLAN;
207
++
208
+     return 0;
209
+ }
210
+ 
211
++static void
212
++netdev_vxlan_push_header(struct dp_packet *packet,
213
++                         const struct ovs_action_push_tnl *data)
214
++{
215
++    int ip_tot_size;
216
++    int size = data->header_len;
217
++    const void *header = data->header;
218
++    struct udp_header *udp;
219
++
220
++    udp = push_ip_header(packet, header, size, &ip_tot_size);
221
++
222
++    /* set udp src port */
223
++    udp->udp_src = get_src_port(packet);
224
++    udp->udp_len = htons(ip_tot_size - sizeof (struct ip_header));
225
++    /* udp_csum is zero */
226
++
227
++    if (udp->udp_csum) {
228
++        uint32_t csum = packet_csum_pseudoheader(ip_hdr(dp_packet_data(packet)));
229
++
230
++        csum = csum_continue(csum, udp,
231
++                             ip_tot_size - sizeof (struct ip_header));
232
++        udp->udp_csum = csum_finish(csum);
233
++
234
++        if (!udp->udp_csum) {
235
++            udp->udp_csum = htons(0xffff);
236
++        }
237
++    }
238
++}
239
++
240
+ static int
241
+ netdev_geneve_pop_header(struct dp_packet *packet)
242
+ {
243
+@@ -1736,7 +1862,7 @@ netdev_vport_tunnel_register(void)
244
+                                        netdev_gre_pop_header),
245
+         TUNNEL_CLASS("ipsec_gre", "gre_sys", NULL, NULL, NULL),
246
+         TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header,
247
+-                                           push_udp_header,
248
++                                           netdev_vxlan_push_header,
249
+                                            netdev_vxlan_pop_header),
250
+         TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL),
251
+         TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL),
252
+diff --git a/lib/odp-util.c b/lib/odp-util.c
253
+index e8bc86d..1696f77 100644
254
+--- a/lib/odp-util.c
255
++++ b/lib/odp-util.c
256
+@@ -468,12 +468,42 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
257
+ 
258
+     if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
259
+         const struct vxlanhdr *vxh;
260
+-
261
+-        vxh = format_udp_tnl_push_header(ds, ip);
262
+-
263
+-        ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
264
+-                      ntohl(get_16aligned_be32(&vxh->vx_flags)),
265
+-                      ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
266
++        const struct udp_header *udp;
267
++        const struct vxgpehdr *vxg;
268
++
269
++        /* UDP */
270
++        udp = (const struct udp_header *) (ip + 1);
271
++        ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16",csum=0x%"PRIx16"),",
272
++              ntohs(udp->udp_src), ntohs(udp->udp_dst),
273
++              ntohs(udp->udp_csum));
274
++
275
++        /* VxLan & VxLan GPE(UDP port: 4790) */
276
++        if (ntohs(udp->udp_dst) == 4790) {
277
++            vxg = (const struct vxgpehdr *)   (udp + 1);
278
++
279
++            ds_put_format(ds, "vxlangpe(vni=0x%"PRIx32",",
280
++                          ntohl(get_16aligned_be32(&vxg->vx_vni)));
281
++            ds_put_format(ds, "proto=%"PRIu8"),", vxg->proto);
282
++            if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
283
++                const struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
284
++
285
++                /* NSH */
286
++                ds_put_format(ds, "nsh(mdtype=%"PRIu8",proto=%"PRIu8",",
287
++                              nsh->b.mdtype, nsh->b.proto);
288
++                ds_put_format(ds, "nsp=%"PRIx32",nsi=%"PRIu8",",
289
++                              nsh->b.b2 & 0x00FFFFFF, nsh->b.svc_idx);
290
++                ds_put_format(ds, "nshc1=%"PRIx32",nshc2=%"PRIx32",",
291
++                              ntohl(nsh->c.nshc1), ntohl(nsh->c.nshc2));
292
++                ds_put_format(ds, "nshc3=%"PRIx32",nshc4=%"PRIx32",",
293
++                              ntohl(nsh->c.nshc3), ntohl(nsh->c.nshc4));
294
++                ds_put_format(ds, ")");
295
++            }
296
++        } else {
297
++            vxh = (const struct vxlanhdr *)   (udp + 1);
298
++            ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
299
++                          ntohl(get_16aligned_be32(&vxh->vx_flags)),
300
++                          ntohl(get_16aligned_be32(&vxh->vx_vni))>>8);
301
++        }
302
+     } else if (data->tnl_type == OVS_VPORT_TYPE_GENEVE) {
303
+         const struct genevehdr *gnh;
304
+ 
305
+@@ -490,8 +520,8 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
306
+                                ds, false);
307
+             ds_put_char(ds, ')');
308
+         }
309
+-
310
+         ds_put_char(ds, ')');
311
++
312
+     } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
313
+         const struct gre_base_hdr *greh;
314
+         ovs_16aligned_be32 *options;
315
+@@ -504,7 +534,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
316
+                            ntohs(greh->flags), ntohs(greh->protocol));
317
+         options = (ovs_16aligned_be32 *)(greh + 1);
318
+         if (greh->flags & htons(GRE_CSUM)) {
319
+-            ds_put_format(ds, ",csum=0x%"PRIx16, ntohs(*((ovs_be16 *)options)));
320
++            ds_put_format(ds, ",csum=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
321
+             options++;
322
+         }
323
+         if (greh->flags & htons(GRE_KEY)) {
324
+@@ -791,8 +821,10 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
325
+     struct ip_header *ip;
326
+     struct udp_header *udp;
327
+     struct gre_base_hdr *greh;
328
++    struct nshhdr *nsh;
329
+     uint16_t gre_proto, gre_flags, dl_type, udp_src, udp_dst, csum;
330
+-    ovs_be32 sip, dip;
331
++    ovs_be32 sip, dip, nsp, nshc1,nshc2,nshc3,nshc4;
332
++    uint8_t nsi;
333
+     uint32_t tnl_type = 0, header_len = 0;
334
+     void *l3, *l4;
335
+     int n = 0;
336
+@@ -837,71 +869,108 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
337
+     udp = (struct udp_header *) l4;
338
+     greh = (struct gre_base_hdr *) l4;
339
+     if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
340
+-                         &udp_src, &udp_dst, &csum)) {
341
+-        uint32_t vx_flags, vni;
342
++                     &udp_src, &udp_dst, &csum)) {
343
++        struct vxlanhdr *vxh;
344
++        struct vxgpehdr *vxg;
345
++        uint32_t vx_flags, vx_vni;
346
++        uint32_t geneve_vni;
347
+ 
348
+         udp->udp_src = htons(udp_src);
349
+         udp->udp_dst = htons(udp_dst);
350
+         udp->udp_len = 0;
351
+         udp->udp_csum = htons(csum);
352
+ 
353
++        vxh = (struct vxlanhdr *) (udp + 1);
354
++        vxg = (struct vxgpehdr *) (udp + 1);
355
++
356
+         if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
357
+-                            &vx_flags, &vni)) {
358
+-            struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
359
++                            &vx_flags, &vx_vni)) {
360
++            tnl_type = OVS_VPORT_TYPE_VXLAN;
361
+ 
362
+             put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
363
+-            put_16aligned_be32(&vxh->vx_vni, htonl(vni << 8));
364
+-            tnl_type = OVS_VPORT_TYPE_VXLAN;
365
++            put_16aligned_be32(&vxh->vx_vni, htonl(vx_vni<<8));
366
++
367
+             header_len = sizeof *eth + sizeof *ip +
368
+                          sizeof *udp + sizeof *vxh;
369
+-        } else if (ovs_scan_len(s, &n, "geneve(")) {
370
+-            struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
371
+ 
372
+-            memset(gnh, 0, sizeof *gnh);
373
+-            header_len = sizeof *eth + sizeof *ip +
374
+-                         sizeof *udp + sizeof *gnh;
375
++        } else if (ovs_scan_len(s, &n, "vxlangpe(vni=0x%"SCNx32",proto="SCNi8"),",
376
++                                   &vx_vni, &vxg->proto)) {
377
++            struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
378
+ 
379
+-            if (ovs_scan_len(s, &n, "oam,")) {
380
+-                gnh->oam = 1;
381
+-            }
382
+-            if (ovs_scan_len(s, &n, "crit,")) {
383
+-                gnh->critical = 1;
384
+-            }
385
+-            if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &vni)) {
386
++            tnl_type = OVS_VPORT_TYPE_VXLAN;
387
++            vxg->i = 0x01;
388
++            vxg->p = 0x01;
389
++            vxg->ver = 0x01;
390
++            put_16aligned_be32(&vxg->vx_vni, htonl(vx_vni));
391
++
392
++            if (ovs_scan_len(s, &n, "nsh(mdtype=%"SCNi8",proto=%"SCNi8",nsp=0x%"SCNx32
393
++                                ",nsi=%"SCNi8",nshc1=0x%"SCNx32",nshc2=0x%"SCNx32
394
++                                ",nshc3=0x%"SCNx32",nshc4=0x%"SCNx32"))",
395
++                                &nsh->b.mdtype, &nsh->b.proto,
396
++                                &nsp, &nsi,
397
++                                &nshc1, &nshc2,
398
++                                &nshc3, &nshc4)) {
399
++                nsh->b.ver = 0x01;
400
++                nsh->b.len = 6;
401
++                nsh->b.b2 = nsp;
402
++                nsh->b.svc_idx = nsi;
403
++                nsh->c.nshc1=nshc1;
404
++                nsh->c.nshc2=nshc2;
405
++                nsh->c.nshc3=nshc3;
406
++                nsh->c.nshc4=nshc4;
407
++                header_len = sizeof *eth + sizeof *ip +
408
++                             sizeof *udp + sizeof *vxh + sizeof *nsh;
409
++            } else {
410
+                 return -EINVAL;
411
+             }
412
+-            if (ovs_scan_len(s, &n, ",options(")) {
413
+-                struct geneve_scan options;
414
+-                int len;
415
+-
416
+-                memset(&options, 0, sizeof options);
417
+-                len = scan_geneve(s + n, &options, NULL);
418
+-                if (!len) {
419
+-                    return -EINVAL;
420
+-                }
421
++        } else if (ovs_scan_len(s, &n, "geneve(")) {
422
++            struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
423
+ 
424
+-                memcpy(gnh->options, options.d, options.len);
425
+-                gnh->opt_len = options.len / 4;
426
+-                header_len += options.len;
427
++        memset(gnh, 0, sizeof *gnh);
428
++        header_len = sizeof *eth + sizeof *ip +
429
++                     sizeof *udp + sizeof *gnh;
430
+ 
431
+-                n += len;
432
+-            }
433
+-            if (!ovs_scan_len(s, &n, "))")) {
434
++        if (ovs_scan_len(s, &n, "oam,")) {
435
++            gnh->oam = 1;
436
++        }
437
++        if (ovs_scan_len(s, &n, "crit,")) {
438
++            gnh->critical = 1;
439
++        }
440
++        if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &geneve_vni)) {
441
++            return -EINVAL;
442
++        }
443
++        if (ovs_scan_len(s, &n, ",options(")) {
444
++            struct geneve_scan options;
445
++            int len;
446
++
447
++            memset(&options, 0, sizeof options);
448
++            len = scan_geneve(s + n, &options, NULL);
449
++            if (!len) {
450
+                 return -EINVAL;
451
+             }
452
+ 
453
+-            gnh->proto_type = htons(ETH_TYPE_TEB);
454
+-            put_16aligned_be32(&gnh->vni, htonl(vni << 8));
455
+-            tnl_type = OVS_VPORT_TYPE_GENEVE;
456
+-        } else {
457
++            memcpy(gnh->options, options.d, options.len);
458
++            gnh->opt_len = options.len / 4;
459
++            header_len += options.len;
460
++
461
++            n += len;
462
++        }
463
++        if (!ovs_scan_len(s, &n, "))")) {
464
+             return -EINVAL;
465
+         }
466
+-    } else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
467
+-                         &gre_flags, &gre_proto)){
468
+ 
469
+-        tnl_type = OVS_VPORT_TYPE_GRE;
470
+-        greh->flags = htons(gre_flags);
471
+-        greh->protocol = htons(gre_proto);
472
++        gnh->proto_type = htons(ETH_TYPE_TEB);
473
++        put_16aligned_be32(&gnh->vni, htonl(geneve_vni << 8));
474
++        tnl_type = OVS_VPORT_TYPE_GENEVE;
475
++    } else {
476
++        return -EINVAL;
477
++    }
478
++} else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
479
++                     &gre_flags, &gre_proto)){
480
++
481
++         tnl_type = OVS_VPORT_TYPE_GRE;
482
++         greh->flags = htons(gre_flags);
483
++         greh->protocol = htons(gre_proto);
484
+         ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
485
+ 
486
+         if (greh->flags & htons(GRE_CSUM)) {
487
+@@ -941,7 +1010,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
488
+                      ((uint8_t *) options - (uint8_t *) greh);
489
+     } else {
490
+         return -EINVAL;
491
+-    }
492
++       }
493
+ 
494
+     /* check tunnel meta data. */
495
+     if (data->tnl_type != tnl_type) {
496
+@@ -1120,6 +1189,7 @@ parse_odp_action(const char *s, const struct simap *port_names,
497
+         struct ovs_action_push_tnl data;
498
+         int n;
499
+ 
500
++        memset(&data, 0, sizeof data);
501
+         n = ovs_parse_tnl_push(s, &data);
502
+         if (n > 0) {
503
+             odp_put_tnl_push_action(actions, &data);
504
+@@ -3285,7 +3355,6 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
505
+         SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
506
+     } SCAN_END_NESTED();
507
+ 
508
+-
509
+     SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT);
510
+ 
511
+     SCAN_BEGIN("eth(", struct ovs_key_ethernet) {
512
+diff --git a/lib/packets.h b/lib/packets.h
513
+index 12f2239..7f9ab98 100644
514
+--- a/lib/packets.h
515
++++ b/lib/packets.h
516
+@@ -33,7 +33,6 @@
517
+ struct dp_packet;
518
+ struct ds;
519
+ 
520
+-/* Tunnel information used in flow key and metadata. */
521
+ struct flow_tnl {
522
+     ovs_be32 ip_dst;
523
+     ovs_be32 ip_src;
524
+@@ -913,6 +912,120 @@ struct vxlanhdr {
525
+ /* VXLAN GPE UDP DST PORT */
526
+ #define VXGPE_DST_PORT 4790
527
+ 
528
++/**
529
++ * struct vxlan_gpehdr - Generic Protocol Extension for VXLAN header.
530
++ * @p: Next Protocol field indicator bit
531
++ * @o: Operations and Management Packet indicator bit.
532
++ * @proto: IEEE Ethertypes to indicate the frame within.
533
++ * @vni: VXLAN Network Identifier.
534
++ */
535
++struct vxgpehdr {
536
++#ifdef WORDS_BIGENDIAN
537
++    uint8_t    res1:4;
538
++    uint8_t    i:1;
539
++    uint8_t    p:1;
540
++    uint8_t    res2:1;
541
++    uint8_t    o:1;
542
++
543
++    uint8_t    ver:2;
544
++    uint8_t    res3:6;
545
++#else
546
++    uint8_t    o:1;
547
++    uint8_t    res2:1;
548
++    uint8_t    p:1;
549
++    uint8_t    i:1;
550
++    uint8_t    res1:4;
551
++
552
++    uint8_t    res3:6;
553
++    uint8_t    ver:2;
554
++#endif
555
++    uint8_t    res4;
556
++    uint8_t    proto;
557
++    ovs_16aligned_be32 vx_vni;
558
++};
559
++
560
++/* VxLAN-GPE Header Next Protocol */
561
++#define VXG_P_IPV4        0x01
562
++#define VXG_P_IPV6        0x02
563
++#define VXG_P_ETHERNET    0x03
564
++#define VXG_P_NSH        0x04
565
++
566
++/**
567
++ * struct nsh_bhdr - Network Service Base Header.
568
++ * @o: Operations and Management Packet indicator bit
569
++ * @c: If this bit is set then one or more contexts are in use.
570
++ * @proto: IEEE Ethertypes to indicate the frame within.
571
++ * @svc_idx: TTL functionality and location within service path.
572
++ * @svc_path: To uniquely identify service path.
573
++ */
574
++struct nsh_base {
575
++#ifdef WORDS_BIGENDIAN
576
++    uint8_t    ver:2;
577
++    uint8_t    o:1;
578
++    uint8_t    c:1;
579
++    uint8_t    res1:4;
580
++
581
++    uint8_t    res2:2;
582
++    uint8_t    len:6;
583
++#else
584
++    uint8_t    res1:4;
585
++    uint8_t    c:1;
586
++    uint8_t    o:1;
587
++    uint8_t    ver:2;
588
++
589
++    uint8_t    len:6;
590
++    uint8_t    res2:2;
591
++#endif
592
++    uint8_t    mdtype;
593
++    uint8_t    proto;
594
++    union {
595
++        struct {
596
++            uint8_t    svc_path[3];
597
++            uint8_t    svc_idx;
598
++        };
599
++        ovs_be32 b2;
600
++    };
601
++};
602
++
603
++/**
604
++ * struct nsh_ctx - Keeps track of NSH context data
605
++ * @npc: NSH network platform context
606
++ * @nsc: NSH network shared context
607
++ * @spc: NSH service platform context
608
++ * @ssc: NSH service shared context
609
++ */
610
++struct nsh_ctx {
611
++    ovs_be32 nshc1;
612
++    ovs_be32 nshc2;
613
++    ovs_be32 nshc3;
614
++    ovs_be32 nshc4;
615
++};
616
++
617
++/**
618
++ * struct nshdr - Network Service header
619
++ * @nsh_base: Network Service Base Header.
620
++ * @nsh_ctx: Network Service Context Header.
621
++ */
622
++struct nshhdr {
623
++    struct nsh_base b;
624
++    struct nsh_ctx c;
625
++};
626
++
627
++/* NSH Base Header Next Protocol */
628
++#define NSH_P_IPV4        0x01
629
++#define NSH_P_IPV6        0x02
630
++#define NSH_P_ETHERNET    0x03
631
++
632
++/* MD Type Registry */
633
++#define NSH_M_TYPE1     0x01
634
++#define NSH_M_TYPE2     0x02
635
++#define NSH_M_EXP1      0xFE
636
++#define NSH_M_EXP2      0xFF
637
++
638
++/* Used for masking nsp and nsi values in field nsp below */
639
++#define NSH_M_NSP    0xFFFFFF00 //uncertain
640
++#define NSH_M_NSI    0x000000FF
641
++
642
+ #define VXLAN_FLAGS 0x08000000  /* struct vxlanhdr.vx_flags required value. */
643
+ 
644
+ void format_ipv6_addr(char *addr_str, const struct in6_addr *addr);
645
+diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
646
+index 4bb9801..9c64c24 100644
647
+--- a/ofproto/ofproto-dpif-xlate.c
648
++++ b/ofproto/ofproto-dpif-xlate.c
649
+@@ -4780,7 +4780,6 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
650
+     if (!xbridge) {
651
+         return;
652
+     }
653
+-
654
+     struct flow *flow = &xin->flow;
655
+ 
656
+     union mf_subvalue stack_stub[1024 / sizeof(union mf_subvalue)];
657
+diff --git a/tests/tunnel.at b/tests/tunnel.at
658
+index f43a07d..5ec5e6c 100644
659
+--- a/tests/tunnel.at
660
++++ b/tests/tunnel.at
661
+@@ -527,6 +527,124 @@ AT_CHECK([tail -1 stdout], [0],
662
+ OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"])
663
+ AT_CLEANUP
664
+ 
665
++AT_SETUP([tunnel - VXLAN-GPE NSH user space])
666
++OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
667
++                    options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4790])
668
++
669
++AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
670
++		br0 65534/100: (dummy)
671
++		p1 1/4790: (vxlan: dst_port=4790, remote_ip=1.1.1.1)
672
++])
673
++OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
674
++AT_CLEANUP
675
++
676
++AT_SETUP([tunnel VXLAN-GPE NSH - encap - nsh/nsi/nshc user space])
677
++OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan options:key=flow \
678
++        options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=1 \
679
++    -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
680
++        options:remote_ip=flow options:dst_port=4790 ofport_request=2 \
681
++    -- add-port br0 p3 -- set Interface p3 type=vxlan options:key=flow \
682
++        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 \
683
++    -- add-port br0 p4 -- set Interface p4 type=vxlan options:key=flow \
684
++        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 \
685
++    -- add-port br0 p5 -- set Interface p5 type=vxlan options:key=flow \
686
++        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 \
687
++    -- add-port br0 p6 -- set Interface p6 type=vxlan options:key=flow \
688
++        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])
689
++
690
++AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
691
++])
692
++AT_CHECK([ovs-vsctl add-port br0 p7 -- set Interface p7 type=dummy ofport_request=7])
693
++AT_CHECK([ovs-vsctl add-port br0 p8 -- set Interface p8 type=dummy ofport_request=8])
694
++
695
++AT_CHECK([
696
++ovs-appctl ovs/route/add 1.1.1.1/24 br0
697
++ovs-appctl tnl/arp/set br0 1.1.1.1 68:05:ca:30:6b:d1
698
++ovs-appctl ovs/route/add 2.2.2.2/24 br0
699
++ovs-appctl tnl/arp/set br0 2.2.2.2 68:05:ca:30:6b:d2
700
++ovs-appctl ovs/route/add 3.3.3.3/24 br0
701
++ovs-appctl tnl/arp/set br0 3.3.3.3 68:05:ca:30:6b:d3
702
++ovs-appctl ovs/route/add 4.4.4.4/24 br0
703
++ovs-appctl tnl/arp/set br0 4.4.4.4 68:05:ca:30:6b:d4
704
++ovs-appctl ovs/route/add 5.5.5.5/24 br0
705
++ovs-appctl tnl/arp/set br0 5.5.5.5 68:05:ca:30:6b:d5
706
++],[0],[stdout])
707
++
708
++AT_DATA([flows.txt], [dnl
709
++in_port=7  actions=resubmit:1,resubmit:2,resubmit:3
710
++in_port=1 actions=output:1
711
++in_port=2 actions=set_field:3.3.3.3->tun_dst,output:2
712
++in_port=3 actions=output:3
713
++])
714
++AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
715
++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])
716
++AT_CHECK([tail -1 stdout], [0],
717
++  [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))
718
++])
719
++
720
++AT_DATA([flows.txt], [dnl
721
++in_port=8  actions=resubmit:4,resubmit:5,resubmit:6
722
++in_port=4 actions=set_nshc1:22,set_nshc2:23,set_nshc3:24,set_nshc4:25,output:4
723
++in_port=5 actions=set_nsp:333,set_nsi:33,set_nshc1:33,set_nshc2:34,set_nshc3:35,set_nshc4:36,output:5
724
++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
725
++])
726
++AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
727
++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])
728
++AT_CHECK([tail -1 stdout], [0],
729
++  [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))
730
++])
731
++OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
732
++AT_CLEANUP
733
++
734
++AT_SETUP([tunnel VXLAN-GPE NSH - decap - nsh/nsi/nshc user space])
735
++OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
736
++AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0])
737
++AT_CHECK([ovs-vsctl add-port int-br p1 -- set Interface p1 type=vxlan options:key=flow \
738
++        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 \
739
++    -- add-port int-br p2 -- set Interface p2 type=vxlan options:key=flow \
740
++        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 \
741
++    -- add-port int-br p3 -- set Interface p3 type=vxlan options:key=flow \
742
++        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])
743
++
744
++AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
745
++])
746
++AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK
747
++])
748
++AT_CHECK([ovs-ofctl add-flow br0 action=normal])
749
++
750
++AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
751
++		br0 65534/100: (dummy)
752
++		p0 1/1: (dummy)
753
++	int-br:
754
++		int-br 65534/2: (dummy)
755
++		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)
756
++		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)
757
++		p3 4/4790: (vxlan: dst_port=4790, key=flow, nshc1=flow, nshc2=flow, nshc3=flow, nshc4=flow, nsi=flow, nsp=flow, remote_ip=flow)
758
++])
759
++
760
++AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
761
++Listening ports:
762
++vxlan_sys_4790 (4790)
763
++])
764
++
765
++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])
766
++AT_CHECK([tail -1 stdout], [0],
767
++  [Datapath actions: tnl_pop(4790)
768
++])
769
++
770
++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])
771
++AT_CHECK([tail -1 stdout], [0],
772
++  [Datapath actions: tnl_pop(4790)
773
++])
774
++
775
++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])
776
++AT_CHECK([tail -1 stdout], [0],
777
++  [Datapath actions: tnl_pop(4790)
778
++])
779
++
780
++OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
781
++AT_CLEANUP
782
++
783
+ AT_SETUP([tunnel - Geneve metadata])
784
+ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
785
+                     options:remote_ip=1.1.1.1 ofport_request=1 \
786
+--
787
+1.9.3
788
+ 

+ 1025
- 0
ovs-nsh/patches/060681.patch
File diff suppressed because it is too large
View File


+ 730
- 0
ovs-nsh/patches/060682.patch View File

@@ -0,0 +1,730 @@
1
+Ethernet-NSH packet.
2
+ 
3
+With this feature(options:nsh-convert=true),when VxLAN-GPE NSH packets (Outer
4
+MAC header + Outer IP header + UDP header + VxLAN-GPE + NSH + original packet)
5
+are received by VxLAN-GPE NSH port, the vport will remove Outer MAC header,
6
+Outer IP header, UDP header, VxLAN-GPE header, and then modify and push the
7
+outer MAC header. Then the packet with VxLAN-GPE+NSH format is converted to
8
+Outer MAC header + NSH header + original packet.
9
+ 
10
+Signed-off-by: Ricky Li <<A HREF="http://openvswitch.org/mailman/listinfo/dev">ricky.li at intel.com</A>>
11
+Signed-off-by: Mengke Liu <<A HREF="http://openvswitch.org/mailman/listinfo/dev">mengke.liu at intel.com</A>>
12
+---
13
+ datapath/linux/compat/include/linux/openvswitch.h |   6 +-
14
+ lib/netdev-vport.c                                | 109 +++++++++++++++++++++-
15
+ lib/netdev.h                                      |   6 ++
16
+ lib/odp-util.c                                    |  80 ++++++++++++++--
17
+ lib/ovs-router.c                                  |  64 +++++++++++++
18
+ lib/ovs-router.h                                  |   1 +
19
+ lib/packets.h                                     |  13 ++-
20
+ ofproto/ofproto-dpif-xlate.c                      |  64 ++++++++++++-
21
+ ofproto/tunnel.c                                  |  37 ++++++++
22
+ ofproto/tunnel.h                                  |   5 +
23
+ tests/tunnel.at                                   |  32 +++++++
24
+ 11 files changed, 398 insertions(+), 19 deletions(-)
25
+ 
26
+diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
27
+index b8ac152..3d588bb 100644
28
+--- a/datapath/linux/compat/include/linux/openvswitch.h
29
++++ b/datapath/linux/compat/include/linux/openvswitch.h
30
+@@ -230,6 +230,7 @@ enum ovs_vport_type {
31
+ 	OVS_VPORT_TYPE_GENEVE,	 /* Geneve tunnel. */
32
+ 	OVS_VPORT_TYPE_LISP = 105,  /* LISP tunnel */
33
+ 	OVS_VPORT_TYPE_STT = 106, /* STT tunnel */
34
++	OVS_VPORT_TYPE_NSH,     /* L2+NSH ENCAP tunnel. */
35
+ 	__OVS_VPORT_TYPE_MAX
36
+ };
37
+ 
38
+@@ -646,7 +647,10 @@ struct ovs_action_push_tnl {
39
+ 	uint8_t  header[TNL_PUSH_HEADER_SIZE];
40
+ };
41
+ 
42
+-#define OVS_POP_SPEC_ACTION_NO_DECAP 2
43
++enum ovs_pop_spec_action_type {
44
++    OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH,
45
++    OVS_POP_SPEC_ACTION_NO_DECAP = 2,
46
++};
47
+ 
48
+ /*
49
+  * struct ovs_action_pop_tnl - %OVS_ACTION_ATTR_TUNNEL_POP_SPEC
50
+diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
51
+index d926c00..6e0d5ba 100644
52
+--- a/lib/netdev-vport.c
53
++++ b/lib/netdev-vport.c
54
+@@ -561,6 +561,23 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
55
+             } else {
56
+                 tnl_cfg.ip_dst = in_addr.s_addr;
57
+             }
58
++        } else if (!strcmp(node->key, "remote_mac")) {
59
++            if (!strcmp(node->value, "flow")) {
60
++                tnl_cfg.eth_dst_flow = true;
61
++                VLOG_ERR("remote_mac doesn't support setting by flow");
62
++                return EINVAL;
63
++            } else if (eth_addr_from_string(node->value,&tnl_cfg.eth_dst)){
64
++                tnl_cfg.eth_dst_present = true;
65
++            } else {
66
++                VLOG_WARN("%s: bad %s 'remote_mac'", name, type);
67
++                return EINVAL;
68
++            }
69
++        } else if (!strcmp(node->key, "nsh_convert")) {
70
++            if (!strcmp(node->value, "true")) {
71
++                tnl_cfg.nsh_convert = true;
72
++            } else {
73
++                tnl_cfg.nsh_convert = false;
74
++            }
75
+         } else if (!strcmp(node->key, "tun_nodecap")) {
76
+             if (!strcmp(node->value, "true")) {
77
+                 tnl_cfg.tun_nodecap = true;
78
+@@ -883,6 +900,13 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
79
+     if (!tnl_cfg.dont_fragment) {
80
+         smap_add(args, "df_default", "false");
81
+     }
82
++    if (tnl_cfg.eth_dst_present) {
83
++        smap_add_format(args, "remote_mac", ETH_ADDR_FMT, ETH_ADDR_ARGS(tnl_cfg.eth_dst));
84
++    }
85
++
86
++    if (tnl_cfg.nsh_convert) {
87
++        smap_add(args, "nsh_convert", "true");
88
++    }
89
+ 
90
+     if (tnl_cfg.tun_nodecap) {
91
+         smap_add(args, "tun_nodecap", "true");
92
+@@ -1546,6 +1570,84 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
93
+ }
94
+ 
95
+ static int
96
++vxlan_extract_md_convert_to_eth_nsh(struct dp_packet *packet, const struct ovs_action_pop_tnl *data)
97
++{
98
++    struct pkt_metadata *md = &packet->md;
99
++    struct flow_tnl *tnl = &md->tunnel;
100
++    struct udp_header *udp;
101
++
102
++    memset(md, 0, sizeof *md);
103
++    if (VXLAN_HLEN > dp_packet_size(packet)) {
104
++        return EINVAL;
105
++    }
106
++
107
++    udp = ip_extract_tnl_md(packet, tnl);
108
++    if (!udp) {
109
++        return EINVAL;
110
++    }
111
++
112
++    if (ntohs(udp->udp_dst) == VXGPE_DST_PORT) {
113
++
114
++        struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
115
++
116
++        if (get_16aligned_be32(&vxg->vx_vni) & htonl(0xff)) {
117
++            VLOG_WARN_RL(&err_rl, "invalid vxlan-gpe vni=%#x\n",
118
++                         ntohl(get_16aligned_be32(&vxg->vx_vni)));
119
++            return EINVAL;
120
++        }
121
++
122
++        tnl->tp_src = udp->udp_src;
123
++        tnl->tp_dst = udp->udp_dst;
124
++        tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxg->vx_vni)) >> 8);
125
++
126
++        if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
127
++            struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
128
++            struct eth_header *eth = NULL;
129
++
130
++            tnl->nsp = nsh->b.b2 << 8;
131
++            tnl->nsi = nsh->b.svc_idx;
132
++            tnl->nshc1 = nsh->c.nshc1;
133
++            tnl->nshc2 = nsh->c.nshc2;
134
++            tnl->nshc3 = nsh->c.nshc3;
135
++            tnl->nshc4 = nsh->c.nshc4;
136
++            tnl->flags |= FLOW_TNL_F_NSP;
137
++            tnl->flags |= FLOW_TNL_F_NSI;
138
++            tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
139
++                        FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
140
++            tnl->nsh_flags = NSH_TNL_F_ETHERNET;
141
++
142
++            dp_packet_reset_packet(packet, VXNSH_HLEN - sizeof (struct nshhdr));
143
++            eth = (struct eth_header *) dp_packet_push_uninit(packet, data->header_len);
144
++            memcpy(eth, data->header, data->header_len);
145
++            eth->eth_type = htons(ETH_TYPE_NSH);
146
++        } else {
147
++            VLOG_WARN("Unsupported vxlan GPE + NSH format!");
148
++            return EINVAL;
149
++        }
150
++
151
++    } else {
152
++
153
++        struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
154
++
155
++        if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
156
++               (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
157
++            VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
158
++                         ntohl(get_16aligned_be32(&vxh->vx_flags)),
159
++                         ntohl(get_16aligned_be32(&vxh->vx_vni)));
160
++            return EINVAL;
161
++        }
162
++
163
++        tnl->tp_src = udp->udp_src;
164
++        tnl->tp_dst = udp->udp_dst;
165
++        tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
166
++        dp_packet_reset_packet(packet, VXLAN_HLEN);
167
++    }
168
++
169
++    return 0;
170
++
171
++}
172
++
173
++static int
174
+ vxlan_extract_md_no_decap(struct dp_packet *packet)
175
+ {
176
+     struct pkt_metadata *md = &packet->md;
177
+@@ -1595,6 +1697,7 @@ vxlan_extract_md_no_decap(struct dp_packet *packet)
178
+             tnl->flags |= FLOW_TNL_F_NSI;
179
+             tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
180
+                         FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
181
++            tnl->tun_len = VXNSH_HLEN;
182
+             tnl->nsh_flags = NSH_TNL_F_NODECAP;
183
+         } else {
184
+             VLOG_WARN("Unsupported vxlan GPE + NSH format!");
185
+@@ -1606,19 +1709,19 @@ vxlan_extract_md_no_decap(struct dp_packet *packet)
186
+     return 0;
187
+ }
188
+ 
189
+-
190
+ static int
191
+ netdev_vxlan_pop_header_spec(struct dp_packet *packet,
192
+                              const struct ovs_action_pop_tnl *data)
193
+ {
194
+-    if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
195
++    if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
196
++        return vxlan_extract_md_convert_to_eth_nsh(packet, data);
197
++    } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
198
+         return vxlan_extract_md_no_decap(packet);
199
+     }
200
+ 
201
+     return EINVAL;
202
+ }
203
+ 
204
+-
205
+ static int
206
+ netdev_vxlan_build_header(const struct netdev *netdev,
207
+                           struct ovs_action_push_tnl *data,
208
+diff --git a/lib/netdev.h b/lib/netdev.h
209
+index b30c932..26013ef 100644
210
+--- a/lib/netdev.h
211
++++ b/lib/netdev.h
212
+@@ -150,6 +150,10 @@ struct netdev_tunnel_config {
213
+     bool ipsec;
214
+     bool dont_fragment;
215
+ 
216
++    bool eth_dst_present;
217
++    bool eth_dst_flow;
218
++    struct eth_addr eth_dst;
219
++
220
+     bool in_nshc1_present;
221
+     bool in_nshc1_flow;
222
+     ovs_be32 in_nshc1;         /* incoming NSH context c1 */
223
+@@ -182,6 +186,7 @@ struct netdev_tunnel_config {
224
+     bool out_nshc4_flow;
225
+     ovs_be32 out_nshc4;        /* outgoing NSH context c4 */
226
+ 
227
++    bool nsh_convert;
228
+     bool tun_nodecap;
229
+ 
230
+ };
231
+@@ -247,6 +252,7 @@ int netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers,
232
+ int netdev_pop_header_spec(struct netdev *netdev,
233
+                            struct dp_packet **buffers, int cnt,
234
+                            const struct ovs_action_pop_tnl *data);
235
++
236
+ /* Hardware address. */
237
+ int netdev_set_etheraddr(struct netdev *, const struct eth_addr mac);
238
+ int netdev_get_etheraddr(const struct netdev *, struct eth_addr *mac);
239
+diff --git a/lib/odp-util.c b/lib/odp-util.c
240
+index 190117f..6da2d5b 100644
241
+--- a/lib/odp-util.c
242
++++ b/lib/odp-util.c
243
+@@ -552,16 +552,22 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
244
+ }
245
+ 
246
+ static void
247
+-format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
248
++format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data)
249
+ {
250
+-    struct ovs_action_pop_tnl *data;
251
++    const struct eth_header *eth;
252
+ 
253
+-    data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
254
++    eth = (const struct eth_header *)data->header;
255
++    if (data->tnl_type == OVS_VPORT_TYPE_NSH) {
256
++        const struct nshhdr *nsh = (const struct nshhdr *) (eth + 1);
257
+ 
258
+-    ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
259
+-    if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
260
+-        ds_put_format(ds, "pop_type=%"PRIu16")",
261
+-                      OVS_POP_SPEC_ACTION_NO_DECAP);
262
++        /* Ethernet */
263
++        ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
264
++                      data->header_len, data->tnl_type);
265
++        ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
266
++        ds_put_format(ds, ",src=");
267
++        ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
268
++        ds_put_format(ds, ",dl_type=0x%04"PRIx16")", ntohs(eth->eth_type));
269
++        ds_put_format(ds, "),");
270
+     }
271
+ }
272
+ 
273
+@@ -578,6 +584,26 @@ format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr)
274
+ }
275
+ 
276
+ static void
277
++format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
278
++{
279
++    struct ovs_action_pop_tnl *data;
280
++
281
++    data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
282
++
283
++    ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
284
++    if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
285
++        ds_put_format(ds, "pop_type=%"PRIu16",",
286
++                      OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH);
287
++        format_odp_tnl_pop_header(ds, data);
288
++        ds_put_format(ds, "out_port(%"PRIu32"))", data->out_port);
289
++
290
++    } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
291
++        ds_put_format(ds, "pop_type=%"PRIu16")",
292
++                      OVS_POP_SPEC_ACTION_NO_DECAP);
293
++    }
294
++}
295
++
296
++static void
297
+ format_odp_action(struct ds *ds, const struct nlattr *a)
298
+ {
299
+     int expected_len;
300
+@@ -1050,11 +1076,8 @@ static int
301
+ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
302
+ {
303
+     struct eth_header *eth;
304
+-    struct nshhdr *nsh;
305
+     uint32_t tnl_type = 0, header_len = 0;
306
+     uint16_t dl_type;
307
+-    ovs_be32 nsp, nshc1, nshc2, nshc3, nshc4;
308
+-    uint8_t nsi;
309
+     int n = 0;
310
+     if (!ovs_scan_len(s, &n, "tnl_pop_spec(tnl_port(%"SCNi32"),",
311
+                          &data->tnl_port)) {
312
+@@ -1068,6 +1091,42 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
313
+ 
314
+     if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
315
+         return n;
316
++
317
++    } else if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
318
++
319
++        eth = (struct eth_header *) data->header;
320
++
321
++        if (!ovs_scan_len(s, &n, ",header(size=%"SCNi32",type=%"SCNi32","
322
++                             "eth(dst="ETH_ADDR_SCAN_FMT",",
323
++                             &data->header_len,
324
++                             &data->tnl_type,
325
++                             ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
326
++            return -EINVAL;
327
++        }
328
++        if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
329
++                      ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
330
++            return -EINVAL;
331
++        }
332
++        if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
333
++            return -EINVAL;
334
++        }
335
++        eth->eth_type = htons(dl_type);
336
++
337
++        tnl_type = OVS_VPORT_TYPE_NSH;
338
++        header_len = sizeof *eth;
339
++
340
++        /* check tunnel meta data. */
341
++        if (data->tnl_type != tnl_type) {
342
++            return -EINVAL;
343
++        }
344
++        if (data->header_len != header_len) {
345
++            return -EINVAL;
346
++        }
347
++
348
++        /* Out port */
349
++        if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
350
++            return -EINVAL;
351
++        }
352
+     } else {
353
+         return -EINVAL;
354
+     }
355
+@@ -1075,6 +1134,7 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
356
+     return n;
357
+ }
358
+ 
359
++
360
+ static int
361
+ parse_odp_action(const char *s, const struct simap *port_names,
362
+                  struct ofpbuf *actions)
363
+diff --git a/lib/ovs-router.c b/lib/ovs-router.c
364
+index d6c7652..9f61bac 100644
365
+--- a/lib/ovs-router.c
366
++++ b/lib/ovs-router.c
367
+@@ -82,6 +82,24 @@ ovs_router_lookup(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw)
368
+     return route_table_fallback_lookup(ip_dst, output_bridge, gw);
369
+ }
370
+ 
371
++bool
372
++ovs_router_lookup_mac(const struct eth_addr *mac, char output_bridge[])
373
++{
374
++    const struct cls_rule *cr;
375
++    struct flow s_flow;
376
++
377
++    memset(&s_flow, 0, sizeof (struct flow));
378
++    memcpy(s_flow.dl_dst.ea, mac->ea, ETH_ADDR_LEN);
379
++    cr = classifier_lookup(&cls,CLS_MAX_VERSION, &s_flow, NULL);
380
++    if (cr) {
381
++        struct ovs_router_entry *p = ovs_router_entry_cast(cr);
382
++
383
++        strncpy(output_bridge, p->output_bridge, IFNAMSIZ);
384
++        return true;
385
++    }
386
++    return false;
387
++}
388
++
389
+ static void
390
+ rt_entry_free(struct ovs_router_entry *p)
391
+ {
392
+@@ -133,6 +151,36 @@ ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, uint8_t plen,
393
+     seq_change(tnl_conf_seq);
394
+ }
395
+ 
396
++static void
397
++ovs_router_insert_mac__(uint8_t priority, struct eth_addr *mac,
398
++                    const char output_bridge[])
399
++{
400
++    const struct cls_rule *cr;
401
++    struct ovs_router_entry *p;
402
++    struct match s_match;
403
++
404
++    memset(&s_match, 0, sizeof (struct match));
405
++    memcpy(s_match.flow.dl_dst.ea, mac->ea, ETH_ADDR_LEN);
406
++
407
++    p = xzalloc(sizeof *p);
408
++    strncpy(p->output_bridge, output_bridge, IFNAMSIZ);
409
++    p->gw = 0;
410
++    p->nw_addr = 0;
411
++    p->plen = 32;
412
++    p->priority = priority;
413
++    cls_rule_init(&p->cr, &s_match, priority); /* Longest prefix matches first. */
414
++
415
++    ovs_mutex_lock(&mutex);
416
++    cr = classifier_replace(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0);
417
++    ovs_mutex_unlock(&mutex);
418
++
419
++    if (cr) {
420
++        /* An old rule with the same match was displaced. */
421
++        ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
422
++    }
423
++    seq_change(tnl_conf_seq);
424
++}
425
++
426
+ void
427
+ ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[],
428
+                   ovs_be32 gw)
429
+@@ -231,6 +279,20 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
430
+ }
431
+ 
432
+ static void
433
++ovs_router_add_mac(struct unixctl_conn *conn, int argc OVS_UNUSED,
434
++              const char *argv[], void *aux OVS_UNUSED)
435
++{
436
++    struct eth_addr mac;
437
++
438
++    if (eth_addr_from_string(argv[1], &mac)) {
439
++        ovs_router_insert_mac__(48, &mac, argv[2]);
440
++        unixctl_command_reply(conn, "OK");
441
++    } else {
442
++        unixctl_command_reply(conn, "Invalid parameters");
443
++    }
444
++}
445
++
446
++static void
447
+ ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
448
+               const char *argv[], void *aux OVS_UNUSED)
449
+ {
450
+@@ -326,6 +388,8 @@ ovs_router_init(void)
451
+     classifier_init(&cls, NULL);
452
+     unixctl_command_register("ovs/route/add", "ipv4_addr/prefix_len out_br_name gw", 2, 3,
453
+                              ovs_router_add, NULL);
454
++    unixctl_command_register("ovs/route/addmac", "mac_addr out_br_name", 2, 2,
455
++                             ovs_router_add_mac, NULL);
456
+     unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
457
+     unixctl_command_register("ovs/route/del", "ipv4_addr/prefix_len", 1, 1, ovs_router_del,
458
+                              NULL);
459
+diff --git a/lib/ovs-router.h b/lib/ovs-router.h
460
+index cc0ebc2..3f5a504 100644
461
+--- a/lib/ovs-router.h
462
++++ b/lib/ovs-router.h
463
+@@ -23,6 +23,7 @@
464
+ extern "C" {
465
+ #endif
466
+ 
467
++bool ovs_router_lookup_mac(const struct eth_addr *mac, char output_bridge[]);
468
+ bool ovs_router_lookup(ovs_be32 ip_dst, char out_dev[], ovs_be32 *gw);
469
+ void ovs_router_init(void);
470
+ void ovs_router_insert(ovs_be32 ip_dst, uint8_t plen,
471
+diff --git a/lib/packets.h b/lib/packets.h
472
+index 87c955a..c586390 100644
473
+--- a/lib/packets.h
474
++++ b/lib/packets.h
475
+@@ -33,6 +33,8 @@
476
+ struct dp_packet;
477
+ struct ds;
478
+ 
479
++#define ETH_ADDR_LEN           6
480
++
481
+ /* Tunnel information used in flow key and metadata. */
482
+ struct flow_tnl {
483
+     ovs_be32 ip_dst;
484
+@@ -52,7 +54,9 @@ struct flow_tnl {
485
+     ovs_be32 nshc2;
486
+     ovs_be32 nshc3;
487
+     ovs_be32 nshc4;
488
+-    uint8_t  pad1[7];        /* Pad to 64 bits. */
489
++    struct eth_addr eth_dst;
490
++    uint8_t tun_len;
491
++    uint8_t  pad1[4];        /* Pad to 64 bits. */
492
+     struct tun_metadata metadata;
493
+ };
494
+ 
495
+@@ -83,7 +87,9 @@ struct flow_tnl {
496
+ #define FLOW_TNL_F_NSH_C3 (1 << 9)
497
+ #define FLOW_TNL_F_NSH_C4 (1 << 10)
498
+ 
499
+-#define NSH_TNL_F_NODECAP (1 << 1)
500
++#define NSH_TNL_F_ETHERNET (1 << 0)
501
++#define NSH_TNL_F_VXLAN (1 << 1)
502
++#define NSH_TNL_F_NODECAP (1 << 2)
503
+ 
504
+ /* Returns an offset to 'src' covering all the meaningful fields in 'src'. */
505
+ static inline size_t
506
+@@ -160,8 +166,6 @@ pkt_metadata_init(struct pkt_metadata *md, odp_port_t port)
507
+ 
508
+ bool dpid_from_string(const char *s, uint64_t *dpidp);
509
+ 
510
+-#define ETH_ADDR_LEN           6
511
+-
512
+ static const struct eth_addr eth_addr_broadcast OVS_UNUSED
513
+     = { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } };
514
+ 
515
+@@ -352,6 +356,7 @@ ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos,
516
+ #define ETH_TYPE_RARP          0x8035
517
+ #define ETH_TYPE_MPLS          0x8847
518
+ #define ETH_TYPE_MPLS_MCAST    0x8848
519
++#define ETH_TYPE_NSH           0x894f
520
+ 
521
+ static inline bool eth_type_mpls(ovs_be16 eth_type)
522
+ {
523
+diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
524
+index 71e255e..bff0a83 100644
525
+--- a/ofproto/ofproto-dpif-xlate.c
526
++++ b/ofproto/ofproto-dpif-xlate.c
527
+@@ -2690,6 +2690,36 @@ tnl_route_lookup_flow(const struct flow *oflow,
528
+ }
529
+ 
530
+ static int
531
++tnl_outdev_lookup_mac(const struct eth_addr *mac,
532
++                      struct xport **out_port)
533
++{
534
++    char out_dev[IFNAMSIZ];
535
++    struct xbridge *xbridge;
536
++    struct xlate_cfg *xcfg;
537
++
538
++    if (!ovs_router_lookup_mac(mac, out_dev)) {
539
++        return -ENOENT;
540
++    }
541
++
542
++    xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
543
++    ovs_assert(xcfg);
544
++
545
++    HMAP_FOR_EACH (xbridge, hmap_node, &xcfg->xbridges) {
546
++        if (!strncmp(xbridge->name, out_dev, IFNAMSIZ)) {
547
++            struct xport *port;
548
++
549
++            HMAP_FOR_EACH (port, ofp_node, &xbridge->xports) {
550
++                if (!strncmp(netdev_get_name(port->netdev), out_dev, IFNAMSIZ)) {
551
++                    *out_port = port;
552
++                    return 0;
553
++                }
554
++            }
555
++        }
556
++    }
557
++    return -ENOENT;
558
++}
559
++
560
++static int
561
+ compose_table_xlate(struct xlate_ctx *ctx, const struct xport *out_dev,
562
+                     struct dp_packet *packet)
563
+ {
564
+@@ -2795,7 +2825,39 @@ build_tunnel_pop(const struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct
565
+     cfg = tnl_port_cfg(tunnel_odp_port, flow);
566
+ 
567
+     if (cfg) {
568
+-        if (cfg->tun_nodecap) {
569
++        if (cfg->nsh_convert && (ntohs(cfg->dst_port) == VXGPE_DST_PORT)) {
570
++            struct ovs_action_pop_tnl tnl_pop_data;
571
++            struct xport *out_dev = NULL;
572
++            struct eth_addr smac;
573
++
574
++            int err;
575
++
576
++            err = tnl_outdev_lookup_mac(&cfg->eth_dst, &out_dev);
577
++            if (err) {
578
++                VLOG_WARN("tnl_outdev_lookup_mac failed...");
579
++                return err;
580
++            }
581
++
582
++            /* Use mac addr of bridge port of the peer. */
583
++            err = netdev_get_etheraddr(out_dev->netdev, &smac);
584
++            if (err) {
585
++                VLOG_WARN("netdev_get_etheraddr failed...");
586
++                return err;
587
++            }
588
++
589
++            err = tnl_port_build_nsh_header_odport_popspec(tunnel_odp_port, flow,
590
++                                        &cfg->eth_dst, &smac, &tnl_pop_data);
591
++            if (err) {
592
++                VLOG_WARN("tnl_port_build_nsh_header failed...");
593
++                return err;
594
++            }
595
++            tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
596
++            tnl_pop_data.out_port = odp_to_u32(out_dev->odp_port);
597
++            tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH;
598
++            tnl_pop_data.tnl_type = OVS_VPORT_TYPE_NSH;
599
++            odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
600
++
601
++        } else if (cfg->tun_nodecap) {
602
+             struct ovs_action_pop_tnl tnl_pop_data;
603
+             memset(&tnl_pop_data, 0, sizeof tnl_pop_data);
604
+ 
605
+diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
606
+index 4606fb6..b0e46e6 100644
607
+--- a/ofproto/tunnel.c
608
++++ b/ofproto/tunnel.c
609
+@@ -45,6 +45,8 @@ VLOG_DEFINE_THIS_MODULE(tunnel);
610
+ /* skb mark used for IPsec tunnel packets */
611
+ #define IPSEC_MARK 1
612
+ 
613
++#define ETH_NSH_HLEN     (sizeof(struct eth_header) +         \
614
++                      sizeof(struct nshhdr))
615
+ struct tnl_match {
616
+     ovs_be64 in_key;
617
+     ovs_be32 in_nsp;
618
+@@ -568,6 +570,9 @@ tnl_port_cfg(odp_port_t odp_port, struct flow *flow) OVS_EXCLUDED(rwlock)
619
+     cfg = netdev_get_tunnel_config(tnl_port->netdev);
620
+     ovs_assert(cfg);
621
+ 
622
++    if (!cfg->eth_dst_flow) {
623
++        memcpy(flow->tunnel.eth_dst.ea, cfg->eth_dst.ea, ETH_ADDR_LEN);
624
++    }
625
+     if (!cfg->out_nsp_flow) {
626
+         flow->tunnel.nsp = cfg->out_nsp;
627
+     }
628
+@@ -602,6 +607,7 @@ out:
629
+     return cfg;
630
+ }
631
+ 
632
++
633
+ static uint32_t
634
+ tnl_hash(struct tnl_match *match)
635
+ {
636
+@@ -1063,3 +1069,34 @@ tnl_port_build_header(const struct ofport_dpif *ofport,
637
+ 
638
+     return res;
639
+ }
640
++
641
++int
642
++tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port,
643
++                                         const struct flow *tnl_flow OVS_UNUSED,
644
++                                         const struct eth_addr *dmac,
645
++                                         const struct eth_addr *smac,
646
++                                         struct ovs_action_pop_tnl *data)
647
++{
648
++    struct tnl_port *tnl_port;
649
++    struct eth_header *eth;
650
++    int res = 0;
651
++
652
++    fat_rwlock_rdlock(&rwlock);
653
++    tnl_port = tnl_find_odp_port(odp_port);
654
++    ovs_assert(tnl_port);
655
++
656
++    /* Build Ethernet and IP headers. */
657
++    memset(data->header, 0, sizeof data->header);
658
++
659
++    eth = (struct eth_header *)data->header;
660
++    memcpy(eth->eth_dst.ea, dmac->ea, ETH_ADDR_LEN);
661
++    memcpy(eth->eth_src.ea, smac->ea, ETH_ADDR_LEN);
662
++    eth->eth_type = htons(ETH_TYPE_NSH);
663
++
664
++    data->header_len = ETH_NSH_HLEN - sizeof (struct nshhdr);
665
++    data->tnl_type = OVS_VPORT_TYPE_NSH;
666
++
667
++    fat_rwlock_unlock(&rwlock);
668
++
669
++    return res;
670
++}
671
+diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h
672
+index 2b608ce..0c51a4e 100644
673
+--- a/ofproto/tunnel.h
674
++++ b/ofproto/tunnel.h
675
+@@ -59,4 +59,9 @@ int tnl_port_build_header(const struct ofport_dpif *ofport,
676
+                           const struct eth_addr dmac,
677
+                           const struct eth_addr smac,
678
+                           ovs_be32 ip_src, struct ovs_action_push_tnl *data);
679
++int tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port,
680
++                                             const struct flow *tnl_flow OVS_UNUSED,
681
++                                             const struct eth_addr *dmac,
682
++                                             const struct eth_addr *smac,
683
++                                             struct ovs_action_pop_tnl *data);
684
+ #endif /* tunnel.h */
685
+diff --git a/tests/tunnel.at b/tests/tunnel.at
686
+index 851afdc..dc35809 100644
687
+--- a/tests/tunnel.at
688
++++ b/tests/tunnel.at
689
+@@ -673,6 +673,38 @@ AT_CHECK([tail -1 stdout], [0],
690
+ OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
691
+ AT_CLEANUP
692
+ 
693
++AT_SETUP([tunnel - VXLAN-GPE NSH - nsh_convert from VXLAN-GPE NSH to Ethernet NSH - user space])
694
++OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
695
++AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0])
696
++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 \
697
++        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])
698
++AT_CHECK([ovs-vsctl add-port int-br p2 -- set Interface p2 type=dummy ofport_request=3])
699
++
700
++AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
701
++])
702
++AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK
703
++])
704
++AT_CHECK([ovs-appctl ovs/route/addmac 00:00:00:11:11:22 br0],[0],[dnl
705
++OK
706
++])
707
++
708
++AT_CHECK([ovs-ofctl add-flow br0 action=normal])
709
++
710
++AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
711
++Listening ports:
712
++vxlan_sys_4790 (4790)
713
++])
714
++
715
++dnl remote_ip p0
716
++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])
717
++
718
++AT_CHECK([tail -1 stdout], [0],
719
++  [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))
720
++])
721
++
722
++OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
723
++AT_CLEANUP
724
++
725
+ AT_SETUP([tunnel - Geneve metadata])
726
+ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
727
+                     options:remote_ip=1.1.1.1 ofport_request=1 \
728
+--
729
+1.9.3
730
+ 

+ 1334
- 0
ovs-nsh/patches/060683.patch
File diff suppressed because it is too large
View File


+ 151
- 0
ovs-nsh/patches/060684.patch View File

@@ -0,0 +1,151 @@
1
+decapsulation-reencapsulation case.
2
+ 
3
+When Ethernet NSH packets are received and then resent to Ethernet NSH port.
4
+The decapsulation and encapsulation will be implemented. However, tunnel pop
5
+and tunnel push actions are very time-consuming when decapsulation and
6
+encapsulation.
7
+ 
8
+With this feature (options:tun_nodecap=true), tunnel port will parse the input
9
+tunnel packets, but the tunnel header will be kept. And the tunnel header can
10
+be modified by the set field actions. This feature can improve performance.
11
+ 
12
+Signed-off-by: Ricky Li <<A HREF="http://openvswitch.org/mailman/listinfo/dev">ricky.li at intel.com</A>>
13
+Signed-off-by: Mengke Liu <<A HREF="http://openvswitch.org/mailman/listinfo/dev">mengke.liu at intel.com</A>>
14
+---
15
+ lib/netdev-vport.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
16
+ lib/odp-util.c     |  3 ++-
17
+ lib/packets.c      | 10 +++++++++-
18
+ tests/tunnel.at    | 21 +++++++++++++++++++++
19
+ 4 files changed, 74 insertions(+), 3 deletions(-)
20
+ 
21
+diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
22
+index 0a3da8d..038f1e1 100644
23
+--- a/lib/netdev-vport.c
24
++++ b/lib/netdev-vport.c
25
+@@ -1865,6 +1865,47 @@ netdev_nsh_pop_header(struct dp_packet *packet)
26
+ }
27
+ 
28
+ static int
29
++netdev_nsh_pop_header_spec(struct dp_packet *packet,
30
++                           const struct ovs_action_pop_tnl *data)
31
++{
32
++    struct pkt_metadata *md = &packet->md;
33
++    struct flow_tnl *tnl = &md->tunnel;
34
++    struct eth_header *eth;
35
++    struct nshhdr *nsh;
36
++
37
++    if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
38
++
39
++        pkt_metadata_init_tnl(md);
40
++        if (ETH_NSH_HLEN > dp_packet_size(packet)) {
41
++            return EINVAL;
42
++        }
43
++
44
++        eth = (struct eth_header *) dp_packet_data(packet);
45
++        memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
46
++        memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
47
++
48
++        nsh = (struct nshhdr *) (eth + 1);
49
++        tnl->nsp = nsh->b.b2 << 8;
50
++        tnl->nsi = nsh->b.svc_idx;
51
++        tnl->nshc1 = nsh->c.nshc1;
52
++        tnl->nshc2 = nsh->c.nshc2;
53
++        tnl->nshc3 = nsh->c.nshc3;
54
++        tnl->nshc4 = nsh->c.nshc4;
55
++        tnl->flags |= FLOW_TNL_F_NSP;
56
++        tnl->flags |= FLOW_TNL_F_NSI;
57
++        tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
58
++                        FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
59
++
60
++		tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_ETHERNET_PRST| NSH_TNL_F_NODECAP;
61
++        tnl->tun_len = ETH_NSH_HLEN;
62
++
63
++        return 0;
64
++    }
65
++
66
++    return EINVAL;
67
++}
68
++
69
++static int
70
+ netdev_nsh_build_header(const struct netdev *netdev,
71
+                         struct ovs_action_push_tnl *data,
72
+                         const struct flow *tnl_flow)
73
+@@ -2144,7 +2185,7 @@ netdev_vport_tunnel_register(void)
74
+         TUNNEL_CLASS("eth_nsh", "nsh_sys", netdev_nsh_build_header,
75
+                                            netdev_nsh_push_header,
76
+                                            netdev_nsh_pop_header,
77
+-                                           NULL),
78
++                                           netdev_nsh_pop_header_spec),
79
+     };
80
+     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
81
+ 
82
+diff --git a/lib/odp-util.c b/lib/odp-util.c
83
+index c2af063..a87b3be 100644
84
+--- a/lib/odp-util.c
85
++++ b/lib/odp-util.c
86
+@@ -4837,7 +4837,8 @@ commit_odp_tunnel_set_action(const struct flow_tnl *tunnel, struct flow_tnl *bas
87
+                              struct ofpbuf *odp_actions)
88
+ {
89
+     /* A valid IPV4_TUNNEL must have non-zero ip_dst. */
90
+-    if (tunnel->ip_dst) {
91
++    if (tunnel->ip_dst ||
92
++		tunnel->nsh_flags & NSH_TNL_F_ETHERNET_PARSED) {
93
+ 
94
+         if (!memcmp(tunnel, base, sizeof *tunnel)) {
95
+             return;
96
+diff --git a/lib/packets.c b/lib/packets.c
97
+index 7dab4b5..14a19b1 100644
98
+--- a/lib/packets.c
99
++++ b/lib/packets.c
100
+@@ -934,7 +934,15 @@ packet_set_nsh(struct dp_packet *packet, struct flow_tnl *tun_key)
101
+ 
102
+     eth = (struct eth_header *) dp_packet_data(packet);
103
+ 
104
+-	if (tun_key->nsh_flags & NSH_TNL_F_VXLAN_PRST) {
105
++	if (tun_key->nsh_flags & NSH_TNL_F_ETHERNET_PRST) {
106
++        nsh = (struct nshhdr *) (eth + 1);
107
++        nsh->b.b2 = tun_key->nsp >> 8;
108
++        nsh->b.svc_idx = tun_key->nsi;
109
++        nsh->c.nshc1 = tun_key->nshc1;
110
++        nsh->c.nshc2 = tun_key->nshc2;
111
++        nsh->c.nshc3 = tun_key->nshc3;
112
++        nsh->c.nshc4 = tun_key->nshc4;
113
++	} else if (tun_key->nsh_flags & NSH_TNL_F_VXLAN_PRST) {
114
+         struct ip_header *ip = (struct ip_header *) (eth + 1);
115
+         struct udp_header *udp = (struct udp_header *) (ip + 1);
116
+         struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
117
+diff --git a/tests/tunnel.at b/tests/tunnel.at
118
+index 19221fb..1bbf5e2 100644
119
+--- a/tests/tunnel.at
120
++++ b/tests/tunnel.at
121
+@@ -765,6 +765,27 @@ AT_CHECK([tail -1 stdout], [0],
122
+ OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
123
+ AT_CLEANUP
124
+ 
125
++AT_SETUP([tunnel - ETHERNET NSH tun_nodecap - user space])
126
++OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
127
++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 \
128
++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])
129
++
130
++AT_CHECK([ovs-ofctl add-flow br0 "priority=16, in_port=1, action=local"])
131
++
132
++AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
133
++		br0 65534/100: (dummy)
134
++		p0 1/1: (dummy)
135
++		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)
136
++])
137
++
138
++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])
139
++AT_CHECK([tail -1 stdout], [0],
140
++  [Datapath actions: tnl_pop_spec(tnl_port(2),pop_type=2)
141
++])
142
++
143
++OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
144
++AT_CLEANUP
145
++
146
+ AT_SETUP([tunnel - Geneve metadata])
147
+ OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
148
+                     options:remote_ip=1.1.1.1 ofport_request=1 \
149
+--
150
+1.9.3
151
+ 

+ 675
- 0
ovs-nsh/patches/060685.patch View File

@@ -0,0 +1,675 @@
1
+VxLAN-GPE NSH packet.
2
+ 
3
+With this feature (options:nsh-convert=true),when Ethernet-NSH packet (Outer
4
+MAC header + original packet) are received by Ethernet-NSH port, the vport
5
+will remove Outer MAC header, and then modify and push the
6
+outer MAC header + Outer IP header + UDP header + VxLAN-GPE. Then the packet
7
+with Ethernet+NSH format is converted to VxLAN-GPE NSH packet.
8
+ 
9
+Signed-off-by: Ricky Li <<A HREF="http://openvswitch.org/mailman/listinfo/dev">ricky.li at intel.com</A>>
10
+Signed-off-by: Mengke Liu <<A HREF="http://openvswitch.org/mailman/listinfo/dev">mengke.liu at intel.com</A>>
11
+---
12
+ datapath/linux/compat/include/linux/openvswitch.h |   3 +-
13
+ lib/netdev-vport.c                                | 128 +++++++++++++++++-----
14
+ lib/odp-util.c                                    | 121 +++++++++++++++++++-
15
+ ofproto/ofproto-dpif-xlate.c                      | 113 ++++++++++++++++++-
16
+ ofproto/tunnel.c                                  |  75 +++++++++++++
17
+ ofproto/tunnel.h                                  |   7 ++
18
+ tests/tunnel.at                                   |  29 +++++
19
+ 7 files changed, 440 insertions(+), 36 deletions(-)
20
+ 
21
+diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
22
+index 045a1f4..916aeae 100644
23
+--- a/datapath/linux/compat/include/linux/openvswitch.h
24
++++ b/datapath/linux/compat/include/linux/openvswitch.h
25
+@@ -651,7 +651,8 @@ struct ovs_action_push_tnl {
26
+ 
27
+ enum ovs_pop_spec_action_type {
28
+     OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH,
29
+-    OVS_POP_SPEC_ACTION_NO_DECAP = 2,
30
++    OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH,
31
++    OVS_POP_SPEC_ACTION_NO_DECAP,
32
+ };
33
+ 
34
+ /*
35
+diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
36
+index 038f1e1..6142935 100644
37
+--- a/lib/netdev-vport.c
38
++++ b/lib/netdev-vport.c
39
+@@ -167,7 +167,8 @@ netdev_vport_needs_dst_port(const struct netdev *dev)
40
+ 
41
+     return (class->get_config == get_tunnel_config &&
42
+             (!strcmp("geneve", type) || !strcmp("vxlan", type) ||
43
+-             !strcmp("lisp", type) || !strcmp("stt", type)) );
44
++             !strcmp("lisp", type) || !strcmp("stt", type) ||
45
++			 !strcmp("eth_nsh", type)) );
46
+ }
47
+ 
48
+ const char *
49
+@@ -890,7 +891,8 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
50
+         if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
51
+             (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
52
+             (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
53
+-            (!strcmp("stt", type) && dst_port != STT_DST_PORT)) {
54
++            (!strcmp("stt", type) && dst_port != STT_DST_PORT) ||
55
++			(!strcmp("eth_nsh", type) && tnl_cfg.nsh_convert)) {
56
+             smap_add_format(args, "dst_port", "%d", dst_port);
57
+         }
58
+     }
59
+@@ -1864,42 +1866,116 @@ netdev_nsh_pop_header(struct dp_packet *packet)
60
+     return 0;
61
+ }
62
+ 
63
++
64
+ static int
65
+-netdev_nsh_pop_header_spec(struct dp_packet *packet,
66
+-                           const struct ovs_action_pop_tnl *data)
67
++eth_nsh_extract_md_no_decap(struct dp_packet *packet)
68
+ {
69
+     struct pkt_metadata *md = &packet->md;
70
+     struct flow_tnl *tnl = &md->tunnel;
71
+     struct eth_header *eth;
72
+     struct nshhdr *nsh;
73
+ 
74
+-    if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
75
++    pkt_metadata_init_tnl(md);
76
++    if (ETH_NSH_HLEN > dp_packet_size(packet)) {
77
++        return EINVAL;
78
++    }
79
+ 
80
+-        pkt_metadata_init_tnl(md);
81
+-        if (ETH_NSH_HLEN > dp_packet_size(packet)) {
82
+-            return EINVAL;
83
+-        }
84
++    eth = (struct eth_header *) dp_packet_data(packet);
85
++    memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
86
++    memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
87
++
88
++    nsh = (struct nshhdr *) (eth + 1);
89
++    tnl->nsp = nsh->b.b2 << 8;
90
++    tnl->nsi = nsh->b.svc_idx;
91
++    tnl->nshc1 = nsh->c.nshc1;
92
++    tnl->nshc2 = nsh->c.nshc2;
93
++    tnl->nshc3 = nsh->c.nshc3;
94
++    tnl->nshc4 = nsh->c.nshc4;
95
++    tnl->flags |= FLOW_TNL_F_NSP;
96
++    tnl->flags |= FLOW_TNL_F_NSI;
97
++    tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
98
++                    FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
99
++	tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_ETHERNET_PRST| NSH_TNL_F_NODECAP;
100
++    tnl->tun_len = ETH_NSH_HLEN;
101
++
102
++    return 0;
103
++}
104
++
105
++static int
106
++eth_nsh_extract_md_convert_to_vxlan_gpe_nsh(struct dp_packet *packet,
107
++                                                    const struct ovs_action_pop_tnl *data)
108
++{
109
++    struct pkt_metadata *md = &packet->md;
110
++    struct flow_tnl *tnl = &md->tunnel;
111
++    struct eth_header *eth;
112
++    struct ip_header *ip;
113
++    struct udp_header *udp;
114
++    struct nshhdr *nsh;
115
++
116
++    pkt_metadata_init_tnl(md);
117
++    if (ETH_NSH_HLEN > dp_packet_size(packet)) {
118
++        return EINVAL;
119
++    }
120
++
121
++    eth = (struct eth_header *) dp_packet_data(packet);
122
++    memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
123
++    memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
124
+ 
125
+-        eth = (struct eth_header *) dp_packet_data(packet);
126
+-        memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
127
+-        memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
128
+-
129
+-        nsh = (struct nshhdr *) (eth + 1);
130
+-        tnl->nsp = nsh->b.b2 << 8;
131
+-        tnl->nsi = nsh->b.svc_idx;
132
+-        tnl->nshc1 = nsh->c.nshc1;
133
+-        tnl->nshc2 = nsh->c.nshc2;
134
+-        tnl->nshc3 = nsh->c.nshc3;
135
+-        tnl->nshc4 = nsh->c.nshc4;
136
+-        tnl->flags |= FLOW_TNL_F_NSP;
137
+-        tnl->flags |= FLOW_TNL_F_NSI;
138
+-        tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
139
++    nsh = (struct nshhdr *) (eth + 1);
140
++    tnl->nsp = nsh->b.b2 << 8;
141
++    tnl->nsi = nsh->b.svc_idx;
142
++    tnl->nshc1 = nsh->c.nshc1;
143
++    tnl->nshc2 = nsh->c.nshc2;
144
++    tnl->nshc3 = nsh->c.nshc3;
145
++    tnl->nshc4 = nsh->c.nshc4;
146
++
147
++    tnl->flags |= FLOW_TNL_F_NSP;
148
++    tnl->flags |= FLOW_TNL_F_NSI;
149
++    tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
150
+                         FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
151
+ 
152
+-		tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_ETHERNET_PRST| NSH_TNL_F_NODECAP;
153
+-        tnl->tun_len = ETH_NSH_HLEN;
154
++	tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_VXLAN_PRST;
155
+ 
156
+-        return 0;
157
++    dp_packet_reset_packet(packet, ETH_NSH_HLEN - sizeof (struct nshhdr));
158
++    eth = (struct eth_header *) dp_packet_push_uninit(packet, data->header_len);
159
++    memcpy(eth, data->header, data->header_len);
160
++
161
++
162
++    /* set IP length, csum */
163
++    int ip_tot_size = dp_packet_size(packet) - sizeof (struct eth_header);
164
++    ip = ip_hdr(eth);
165
++    ip->ip_tot_len = htons(ip_tot_size);
166
++    ip->ip_csum = recalc_csum16(ip->ip_csum, 0, ip->ip_tot_len);
167
++
168
++    /* set udp src port */
169
++    udp = (struct udp_header *) (ip + 1);
170
++    udp->udp_src = get_src_port(packet);
171
++    udp->udp_len = htons(ip_tot_size - sizeof (struct ip_header));
172
++
173
++    /* udp_csum is zero */
174
++    if (udp->udp_csum) {
175
++        uint32_t csum = packet_csum_pseudoheader(ip);
176
++
177
++        csum = csum_continue(csum, udp,
178
++                             ip_tot_size - sizeof (struct ip_header));
179
++        udp->udp_csum = csum_finish(csum);
180
++
181
++        if (!udp->udp_csum) {
182
++            udp->udp_csum = htons(0xffff);
183
++        }
184
++    }
185
++
186
++    return 0;
187
++}
188
++
189
++static int
190
++netdev_nsh_pop_header_spec(struct dp_packet *packet,
191
++                             const struct ovs_action_pop_tnl *data)
192
++{
193
++    if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH) {
194
++        return eth_nsh_extract_md_convert_to_vxlan_gpe_nsh(packet, data);
195
++    } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
196
++        return eth_nsh_extract_md_no_decap(packet);
197
+     }
198
+ 
199
+     return EINVAL;
200
+diff --git a/lib/odp-util.c b/lib/odp-util.c
201
+index a87b3be..183844f 100644
202
+--- a/lib/odp-util.c
203
++++ b/lib/odp-util.c
204
+@@ -515,7 +515,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
205
+                       gnh->oam ? "oam," : "",
206
+                       gnh->critical ? "crit," : "",
207
+                       ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
208
+-
209
++
210
+         if (gnh->opt_len) {
211
+             ds_put_cstr(ds, ",options(");
212
+             format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
213
+@@ -579,10 +579,11 @@ static void
214
+ format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data)
215
+ {
216
+     const struct eth_header *eth;
217
++    const struct ip_header *ip;
218
++    const void *l3;
219
+ 
220
+     eth = (const struct eth_header *)data->header;
221
+     if (data->tnl_type == OVS_VPORT_TYPE_NSH) {
222
+-        const struct nshhdr *nsh = (const struct nshhdr *) (eth + 1);
223
+ 
224
+         /* Ethernet */
225
+         ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
226
+@@ -591,7 +592,37 @@ format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data)
227
+         ds_put_format(ds, ",src=");
228
+         ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
229
+         ds_put_format(ds, ",dl_type=0x%04"PRIx16")", ntohs(eth->eth_type));
230
+-        ds_put_format(ds, "),");
231
++		ds_put_format(ds, "),");
232
++    } else if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
233
++        l3 = eth + 1;
234
++        ip = (const struct ip_header *)l3;
235
++
236
++        /* Ethernet */
237
++        ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
238
++                      data->header_len, data->tnl_type);
239
++        ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
240
++        ds_put_format(ds, ",src=");
241
++        ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
242
++        ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));
243
++
244
++        /* IPv4 */
245
++        ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
246
++                      ",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
247
++                      IP_ARGS(get_16aligned_be32(&ip->ip_src)),
248
++                      IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
249
++                      ip->ip_proto, ip->ip_tos,
250
++                      ip->ip_ttl,
251
++                      ip->ip_frag_off);
252
++        if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
253
++            const struct vxlanhdr *vxh;
254
++
255
++            vxh = format_udp_tnl_push_header(ds, ip);
256
++
257
++            ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
258
++                          ntohl(get_16aligned_be32(&vxh->vx_flags)),
259
++                          ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
260
++        }
261
++		ds_put_format(ds, "),");
262
+     }
263
+ }
264
+ 
265
+@@ -615,9 +646,10 @@ format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
266
+     data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
267
+ 
268
+     ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
269
+-    if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
270
++    if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH ||
271
++			data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH) {
272
+         ds_put_format(ds, "pop_type=%"PRIu16",",
273
+-                      OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH);
274
++                      data->pop_type);
275
+         format_odp_tnl_pop_header(ds, data);
276
+         ds_put_format(ds, "out_port(%"PRIu32"))", data->out_port);
277
+ 
278
+@@ -1174,6 +1206,85 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
279
+         if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
280
+             return -EINVAL;
281
+         }
282
++    }  else if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH) {
283
++        struct eth_header *eth;
284
++        struct ip_header *ip;
285
++        struct udp_header *udp;
286
++        uint16_t dl_type, udp_src, udp_dst, csum;
287
++        ovs_be32 sip, dip;
288
++        uint32_t tnl_type = 0, header_len = 0;
289
++        void *l3, *l4;
290
++        int n = 0;
291
++
292
++        eth = (struct eth_header *) data->header;
293
++        l3 = (data->header + sizeof *eth);
294
++        l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
295
++        ip = (struct ip_header *) l3;
296
++        if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
297
++                             "eth(dst="ETH_ADDR_SCAN_FMT",",
298
++                             &data->header_len,
299
++                             &data->tnl_type,
300
++                             ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
301
++            return -EINVAL;
302
++        }
303
++
304
++        if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
305
++                      ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
306
++            return -EINVAL;
307
++        }
308
++        if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
309
++            return -EINVAL;
310
++        }
311
++        eth->eth_type = htons(dl_type);
312
++
313
++        /* IPv4 */
314
++        if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
315
++                             ",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
316
++                             IP_SCAN_ARGS(&sip),
317
++                             IP_SCAN_ARGS(&dip),
318
++                             &ip->ip_proto, &ip->ip_tos,
319
++                             &ip->ip_ttl, &ip->ip_frag_off)) {
320
++            return -EINVAL;
321
++        }
322
++        put_16aligned_be32(&ip->ip_src, sip);
323
++        put_16aligned_be32(&ip->ip_dst, dip);
324
++
325
++        /* Tunnel header */
326
++        udp = (struct udp_header *) l4;
327
++        if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
328
++                         &udp_src, &udp_dst, &csum)) {
329
++            uint32_t vx_flags, vni;
330
++
331
++            udp->udp_src = htons(udp_src);
332
++            udp->udp_dst = htons(udp_dst);
333
++            udp->udp_len = 0;
334
++            udp->udp_csum = htons(csum);
335
++
336
++            if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
337
++                                &vx_flags, &vni)) {
338
++                struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
339
++
340
++                put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
341
++                put_16aligned_be32(&vxh->vx_vni, htonl(vni << 8));
342
++                tnl_type = OVS_VPORT_TYPE_VXLAN;
343
++                header_len = sizeof *eth + sizeof *ip +
344
++                             sizeof *udp + sizeof *vxh;
345
++            } else {
346
++                return -EINVAL;
347
++            }
348
++            /* check tunnel meta data. */
349
++            if (data->tnl_type != tnl_type) {
350
++                return -EINVAL;
351
++            }
352
++            if (data->header_len != header_len) {
353
++                return -EINVAL;
354
++            }
355
++
356
++            /* Out port */
357
++            if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
358
++                return -EINVAL;
359
++            }
360
++        }
361
+     } else {
362
+         return -EINVAL;
363
+     }
364
+diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
365
+index 90b5a95..1578a0c 100644
366
+--- a/ofproto/ofproto-dpif-xlate.c
367
++++ b/ofproto/ofproto-dpif-xlate.c
368
+@@ -2653,7 +2653,7 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
369
+ }
370
+ 
371
+ static int
372
+-tnl_route_lookup_flow(const struct flow *oflow,
373
++tnl_route_lookup_flow__(ovs_be32 ip_dst,
374
+                       ovs_be32 *ip, struct xport **out_port)
375
+ {
376
+     char out_dev[IFNAMSIZ];
377
+@@ -2661,14 +2661,14 @@ tnl_route_lookup_flow(const struct flow *oflow,
378
+     struct xlate_cfg *xcfg;
379
+     ovs_be32 gw;
380
+ 
381
+-    if (!ovs_router_lookup(oflow->tunnel.ip_dst, out_dev, &gw)) {
382
++    if (!ovs_router_lookup(ip_dst, out_dev, &gw)) {
383
+         return -ENOENT;
384
+     }
385
+ 
386
+     if (gw) {
387
+         *ip = gw;
388
+     } else {
389
+-        *ip = oflow->tunnel.ip_dst;
390
++        *ip = ip_dst;
391
+     }
392
+ 
393
+     xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
394
+@@ -2690,6 +2690,12 @@ tnl_route_lookup_flow(const struct flow *oflow,
395
+ }
396
+ 
397
+ static int
398
++tnl_route_lookup_flow(const struct flow *oflow,
399
++                      ovs_be32 *ip, struct xport **out_port){
400
++    return tnl_route_lookup_flow__(oflow->tunnel.ip_dst, ip, out_port);
401
++}
402
++
403
++static int
404
+ tnl_outdev_lookup_mac(const struct eth_addr *mac,
405
+                       struct xport **out_port)
406
+ {
407
+@@ -2818,6 +2824,100 @@ build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport,
408
+ }
409
+ 
410
+ static int
411
++build_eth_nsh_tunnel_pop(struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct flow *flow)
412
++{
413
++    const struct netdev_tunnel_config * cfg;
414
++
415
++    cfg = tnl_port_cfg(tunnel_odp_port, flow);
416
++
417
++    if (cfg) {
418
++        if (cfg->nsh_convert && (ntohs(cfg->dst_port) == VXGPE_DST_PORT)) {
419
++
420
++			struct ovs_action_pop_tnl tnl_pop_data;
421
++            struct xport *out_dev = NULL;
422
++            ovs_be32 s_ip, d_ip = 0;
423
++            struct eth_addr smac;
424
++            struct eth_addr dmac;
425
++            int err;
426
++
427
++            err = tnl_route_lookup_flow__(cfg->ip_dst, &d_ip, &out_dev);
428
++            if (err) {
429
++                xlate_report(ctx, "native tunnel routing failed");
430
++                return err;
431
++            }
432
++            xlate_report(ctx, "tunneling to "IP_FMT" via %s",
433
++                         IP_ARGS(d_ip), netdev_get_name(out_dev->netdev));
434
++
435
++            /* Use mac addr of bridge port of the peer. */
436
++            err = netdev_get_etheraddr(out_dev->netdev, &smac);
437
++            if (err) {
438
++                xlate_report(ctx, "tunnel output device lacks Ethernet address");
439
++                return err;
440
++            }
441
++
442
++            err = netdev_get_in4(out_dev->netdev, (struct in_addr *) &s_ip, NULL);
443
++            if (err) {
444
++                xlate_report(ctx, "tunnel output device lacks IPv4 address");
445
++                return err;
446
++            }
447
++
448
++            err = tnl_arp_lookup(out_dev->xbridge->name, d_ip, &dmac);
449
++            if (err) {
450
++                xlate_report(ctx, "ARP cache miss for "IP_FMT" on bridge %s, "
451
++                             "sending ARP request",
452
++                             IP_ARGS(d_ip), out_dev->xbridge->name);
453
++                tnl_send_arp_request(ctx, out_dev, smac, s_ip, d_ip);
454
++                return err;
455
++            }
456
++            if (ctx->xin->xcache) {
457
++                struct xc_entry *entry;
458
++
459
++                entry = xlate_cache_add_entry(ctx->xin->xcache, XC_TNL_ARP);
460
++                ovs_strlcpy(entry->u.tnl_arp_cache.br_name, out_dev->xbridge->name,
461
++                            sizeof entry->u.tnl_arp_cache.br_name);
462
++                entry->u.tnl_arp_cache.d_ip = d_ip;
463
++            }
464
++
465
++            xlate_report(ctx, "tunneling from "ETH_ADDR_FMT" "IP_FMT
466
++                         " to "ETH_ADDR_FMT" "IP_FMT,
467
++                         ETH_ADDR_ARGS(smac), IP_ARGS(s_ip),
468
++                         ETH_ADDR_ARGS(dmac), IP_ARGS(d_ip));
469
++            err = tnl_port_build_header_odport_popspec(tunnel_odp_port, cfg,
470
++                                        dmac, smac, s_ip, &tnl_pop_data);
471
++            if (err) {
472
++                return err;
473
++            }
474
++            tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
475
++            tnl_pop_data.out_port = odp_to_u32(out_dev->odp_port);
476
++            tnl_pop_data.tnl_type = OVS_VPORT_TYPE_VXLAN;
477
++            tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH;
478
++            odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
479
++
480
++        } else if (cfg->tun_nodecap) {
481
++            struct ovs_action_pop_tnl tnl_pop_data;
482
++            memset(&tnl_pop_data, 0, sizeof tnl_pop_data);
483
++
484
++            tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
485
++            tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_NO_DECAP;
486
++            odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
487
++
488
++        } else {
489
++            nl_msg_put_odp_port(ctx->odp_actions,
490
++                OVS_ACTION_ATTR_TUNNEL_POP,
491
++                tunnel_odp_port);
492
++        }
493
++