Implement: Fuel install OVS with NSH/DPDK

Change-Id: I6933105a30fef4bc95e830ec86965c4417976454
Closes-Bug:#1507877
Signed-off-by: lingyu1 <ling.y.yu@intel.com>
This commit is contained in:
lingyu1 2015-11-27 23:17:37 +08:00
parent 373740ac0a
commit 7590019023
20 changed files with 8099 additions and 143 deletions

View File

@ -14,7 +14,7 @@ Requirements
| Requirement | Version/Comment |
|----------------------------------|-----------------|
| Mirantis OpenStack compatibility | 6.1 |
| Mirantis OpenStack compatibility | 7.0 |
Recommendations
---------------
@ -60,7 +60,7 @@ Openvswitch plugin installation
id | name | version | package_version
---|-----------------|---------|----------------
1 | fuel-plugin-ovs | 0.5.0 | 2.0.0
1 | fuel-plugin-ovs | 0.5.1 | 3.0.0
8. Plugin is ready to use and can be enabled on the Settings tab of the Fuel web UI.
@ -102,13 +102,6 @@ Known issues
None.
Release Notes
-------------
**0.5.0**
* Initial release of the plugin. This is a beta version.
Development
===========

View File

@ -1,40 +1,39 @@
$fuel_settings = parseyaml(file('/etc/compute.yaml'))
$ovs_version = "2.4.90-1"
if $operatingsystem == 'Ubuntu' {
package { 'openvswitch-datapath-dkms':
ensure => "${ovs_version}",
}
package { 'openvswitch-common':
ensure => "${ovs_version}",
}
package { 'openvswitch-switch':
ensure => "${ovs_version}",
require => Package['openvswitch-common','openvswitch-datapath-dkms'],
}
} elsif $operatingsystem == 'CentOS' {
if $fuel_settings['fuel-plugin-ovs']['use_dpdk'] {
package { 'openvswitch':
ensure => "2.4.90-1.el6",
exec { "wget dpdk package":
command => "wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk-install.tar.gz",
path => "/usr/bin:/usr/sbin:/bin:/sbin",
}
exec { "unzip dpdk package":
command => "tar -xvzf /etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install.tar.gz",
path => "/usr/bin:/usr/sbin:/bin:/sbin",
}
exec { "install dpdk package":
command => "/etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install/dpdk-install.sh",
path => "/usr/bin:/usr/sbin:/bin:/sbin",
}
package { 'openvswitch-datapath-dkms':
ensure => "2.4.90-1",
}
package { 'kmod-openvswitch':
ensure => "2.4.90-2.el6",
package { 'openvswitch-common':
ensure => "2.4.90-1",
}
package { 'dpdk':
ensure => "2.1.0-6.el6",
package { 'openvswitch-switch':
ensure => "2.4.90-1",
require => Package['openvswitch-common','openvswitch-datapath-dkms'],
}
package { 'dpdk-tools':
ensure => "2.1.0-6.el6",
} else {
package { 'openvswitch-datapath-dkms':
ensure => "2.4.90-1",
}
package { 'dpdk-devel':
ensure => "2.1.0-6.el6",
package { 'openvswitch-common':
ensure => "2.4.90-1",
}
} else {
package { 'openvswitch':
ensure =>"2.4.90-1",
package { 'openvswitch-switch':
ensure => "2.4.90-1",
require => Package['openvswitch-common','openvswitch-datapath-dkms'],
}
package { 'kmod-openvswitch':
ensure => "2.4.90-1.el6",
}
}
}
} elsif $operatingsystem == 'CentOS' {
}

View File

@ -1,39 +1,39 @@
$fuel_settings = parseyaml(file('/etc/controller.yaml'))
$ovs_version = "2.4.90-1"
if $operatingsystem == 'Ubuntu' {
package { 'openvswitch-datapath-dkms':
ensure => "${ovs_version}",
}
package { 'openvswitch-common':
ensure => "${ovs_version}",
}
package { 'openvswitch-switch':
ensure => "${ovs_version}",
require => Package['openvswitch-common','openvswitch-datapath-dkms'],
}
} elsif $operatingsystem == 'CentOS' {
if $fuel_settings['fuel-plugin-ovs']['use_dpdk'] {
package { 'openvswitch':
ensure => "2.4.90-1.el6",
}
package { 'kmod-openvswitch':
ensure => "2.4.90-2.el6",
}
package { 'dpdk':
ensure => "2.1.0-6.el6",
}
package { 'dpdk-tools':
ensure => "2.1.0-6.el6",
}
package { 'dpdk-devel':
ensure => "2.1.0-6.el6",
}
} else {
package { 'openvswitch':
exec { "wget dpdk package":
command => "wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk-install.tar.gz",
path => "/usr/bin:/usr/sbin:/bin:/sbin",
}
exec { "unzip dpdk package":
command => "tar -xvzf /etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install.tar.gz",
path => "/usr/bin:/usr/sbin:/bin:/sbin",
}
exec { "install dpdk package":
command => "/etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install/dpdk-install.sh",
path => "/usr/bin:/usr/sbin:/bin:/sbin",
}
package { 'openvswitch-datapath-dkms':
ensure => "2.4.90-1",
}
package { 'kmod-openvswitch':
ensure => "2.4.90-1.el6",
package { 'openvswitch-common':
ensure => "2.4.90-1",
}
}
package { 'openvswitch-switch':
ensure => "2.4.90-1",
require => Package['openvswitch-common','openvswitch-datapath-dkms'],
}
} else {
package { 'openvswitch-datapath-dkms':
ensure => "2.4.90-1",
}
package { 'openvswitch-common':
ensure => "2.4.90-1",
}
package { 'openvswitch-switch':
ensure => "2.4.90-1",
require => Package['openvswitch-common','openvswitch-datapath-dkms'],
}
}
} elsif $operatingsystem == 'CentOS' {
}

View File

@ -1,39 +1,39 @@
$fuel_settings = parseyaml(file('/etc/primary-controller.yaml'))
$ovs_version = "2.4.90-1"
if $operatingsystem == 'Ubuntu' {
package { 'openvswitch-datapath-dkms':
ensure => "${ovs_version}",
}
package { 'openvswitch-common':
ensure => "${ovs_version}",
}
package { 'openvswitch-switch':
ensure => "${ovs_version}",
require => Package['openvswitch-common','openvswitch-datapath-dkms'],
}
} elsif $operatingsystem == 'CentOS' {
if $fuel_settings['fuel-plugin-ovs']['use_dpdk'] {
package { 'openvswitch':
ensure => "2.4.90-1.el6",
}
package { 'kmod-openvswitch':
ensure => "2.4.90-2.el6",
}
package { 'dpdk':
ensure => "2.1.0-6.el6",
}
package { 'dpdk-tools':
ensure => "2.1.0-6.el6",
}
package { 'dpdk-devel':
ensure => "2.1.0-6.el6",
}
} else {
package { 'openvswitch':
exec { "wget dpdk package":
command => "wget http://10.20.0.2:8080/plugins/fuel-plugin-ovs-0.5/repositories/ubuntu/dpdk-install.tar.gz",
path => "/usr/bin:/usr/sbin:/bin:/sbin",
}
exec { "unzip dpdk package":
command => "tar -xvzf /etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install.tar.gz",
path => "/usr/bin:/usr/sbin:/bin:/sbin",
}
exec { "install dpdk package":
command => "/etc/fuel/plugins/fuel-plugin-ovs-0.5/dpdk-install/dpdk-install.sh",
path => "/usr/bin:/usr/sbin:/bin:/sbin",
}
package { 'openvswitch-datapath-dkms':
ensure => "2.4.90-1",
}
package { 'kmod-openvswitch':
ensure => "2.4.90-1.el6",
package { 'openvswitch-common':
ensure => "2.4.90-1",
}
}
package { 'openvswitch-switch':
ensure => "2.4.90-1",
require => Package['openvswitch-common','openvswitch-datapath-dkms'],
}
} else {
package { 'openvswitch-datapath-dkms':
ensure => "2.4.90-1",
}
package { 'openvswitch-common':
ensure => "2.4.90-1",
}
package { 'openvswitch-switch':
ensure => "2.4.90-1",
require => Package['openvswitch-common','openvswitch-datapath-dkms'],
}
}
} elsif $operatingsystem == 'CentOS' {
}

1
deployment_tasks.yaml Normal file
View File

@ -0,0 +1 @@
[]

View File

@ -3,11 +3,11 @@ name: fuel-plugin-ovs
# Human-readable name for your plugin
title: Openvswitch with NSH support
# Plugin version
version: '0.5.0'
version: '0.5.1'
# Description
description: 'This plugin provides to deploy openvswitch with nsh'
# Required fuel version
fuel_version: ['6.1']
fuel_version: ['7.0']
# Specify license of your plugin
licenses: ['Apache License Version 2.0']
# Specify author or company name
@ -21,15 +21,10 @@ groups: ['network']
# The plugin is compatible with releases in the list
releases:
- os: ubuntu
version: 2014.2-6.1
version: 2015.1.0-7.0
mode: ['ha', 'multinode']
deployment_scripts_path: deployment_scripts/
repository_path: repositories/ubuntu
- os: centos
version: 2014.2-6.1
mode: ['ha', 'multinode']
deployment_scripts_path: deployment_scripts/
repository_path: repositories/centos
# Version of plugin package
package_version: '2.0.0'
package_version: '3.0.0'

21
ovs-nsh/Dockerfile Normal file
View File

@ -0,0 +1,21 @@
#!/bin/bash
FROM ubuntu:14.04.3
RUN apt-get update
RUN apt-get install -y software-properties-common python-software-properties \
make python-setuptools python-all dpkg-dev debhelper \
fuseiso git genisoimage bind9-host wget curl lintian tmux lxc iptables \
ca-certificates sudo apt-utils lsb-release libtool autoconf automake build-essential fakeroot libssl-dev graphviz dh-autoreconf python-qt4 python-twisted-conch python-zopeinterface
RUN echo "ALL ALL=NOPASSWD: ALL" > /etc/sudoers.d/open-sudo
RUN chmod 0440 /etc/sudoers.d/open-sudo
ADD ./patches /patches
ADD ./download-ovs-nsh.sh /root/download-ovs-nsh.sh
RUN chmod +x /root/download-ovs-nsh.sh
RUN /root/download-ovs-nsh.sh
ADD ./build-ovs-nsh.sh /root/build-ovs-nsh.sh
RUN chmod +x /root/build-ovs-nsh.sh
RUN /root/build-ovs-nsh.sh

4
ovs-nsh/build-ovs-nsh.sh Executable file
View File

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

77
ovs-nsh/download-ovs-nsh.sh Executable file
View File

@ -0,0 +1,77 @@
#!/bin/bash
# Author: Johnson Li <johnson.li@intel.com>
# Change Log:
# 10-28-2015: Initial version
#
# This script helps to setup the NSH test environment.
# Since the NSH patches are not merged into OVS's mainline,
# users cannot test the feature easily.
# In order to get OVS installed for tests, this script helps
# to get patch files from the mail archivements and apply
# the patches to a special commit of OVS.
#
# This script is for free use, feel free to modify the script
# for your own use. Any questions about the installation script,
# please send an email to the author.
######################## Global Variables #####################
WORK_DIR=`pwd`
PATCHES="060679 060680 060681 060682 060683 060684 060685"
URL_OVS=https://github.com/openvswitch/ovs.git
OVS_COMMIT=121daded51b9798fe3722824b27a05c16806cbd1
######################## Functions ############################
################################################################
# Function Name: get_ovs_codes_from_github()
# Desription: Clone sources for OVS from github.
################################################################
function get_ovs_codes_from_github()
{
git clone ${URL_OVS} openvswitch 2>&1 | tee -a ${WORK_DIR}/install.log
return $?
}
################################################################
# Function Name: apply_patches_to_ovs()
# Desription: Apply patches to a specific commit of OVS.
################################################################
function apply_patches_to_ovs()
{
if [ ! -d openvswitch ] ;then
echo "No source found for Openvswitch, exit!" | tee -a ${WORK_DIR}/install.log
return -1
fi
if [ ! -d patches ] ;then
echo "No source found for Openvswitch, exit!" | tee -a ${WORK_DIR}/install.log
return -1
fi
cd openvswitch
git checkout ${OVS_COMMIT} -b development 2>&1 | tee -a ${WORK_DIR}/install.log
for patch in ${PATCHES}
do
patch -p1 < ${WORK_DIR}/patches/${patch}.patch 2>&1 | tee -a ${WORK_DIR}/install.log
done
cd ${WORK_DIR}
return 0
}
################### MAIN ############################
get_ovs_codes_from_github
if [ $? -ne 0 ] ;then
echo "Error occured when cloning OVS, exit." | tee -a ${WORK_DIR}/install.log
exit 1
fi
apply_patches_to_ovs
if [ $? -ne 0 ] ;then
echo "Error occured when applying patches, exit." | tee -a ${WORK_DIR}/install.log
exit 1
fi
exit 0

3187
ovs-nsh/patches/060679.patch Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,788 @@
at data plane level in user space and modify the related codes at control plane
level in user space.
The design is based on basic VxLAN impletation. When packets are received at
data plane level in user space, function 'dp_netdev_input' will be called for
processing the packets at data plane level in user space.
When VXLAN-GPE NSH packets are received, decapsulation will be implemented. For
the first time, the packets are sent to control plane by function 'upcall_cb',
and tunnel port will be lookuped by matching the UDP port which is 4790 for
VxLAN-GPE NSH port, if VxLAN-GPE NSH tunnel port are matched
successfully, the tunnel pop action will be appended and implemented at data
plane level, and the NSH related field will be parsed, then packets will be
reprocessed by function 'dp_netdev_input'.
When original packets are sent to VxLAN-GPE NSH port, the encapsulation will
be implemented. For the first time, in the control plane the tunnel
tunnel_push_data are built according to VxLAN-GPE NSH port configuration and
related rules, then the tunnel push actions are appended and implemented at
data plane level. Finally packets will be reprocessed by function
'dp_netdev_input'.
Signed-off-by: Ricky Li <<A HREF="http://openvswitch.org/mailman/listinfo/dev">ricky.li at intel.com</A>>
Signed-off-by: Mengke Liu <<A HREF="http://openvswitch.org/mailman/listinfo/dev">mengke.liu at intel.com</A>>
---
lib/netdev-vport.c | 166 +++++++++++++++++++++++++++++++++++-----
lib/odp-util.c | 175 ++++++++++++++++++++++++++++++-------------
lib/packets.h | 115 +++++++++++++++++++++++++++-
ofproto/ofproto-dpif-xlate.c | 1 -
tests/tunnel.at | 118 +++++++++++++++++++++++++++++
5 files changed, 500 insertions(+), 75 deletions(-)
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 3f85386..a0a4da2 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -67,6 +67,12 @@ static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5);
sizeof(struct udp_header) + \
sizeof(struct genevehdr))
+#define VXNSH_HLEN (sizeof(struct eth_header) + \
+ sizeof(struct ip_header) + \
+ sizeof(struct udp_header) + \
+ sizeof(struct vxgpehdr) + \
+ sizeof(struct nshhdr))
+
#define DEFAULT_TTL 64
struct netdev_vport {
@@ -1462,29 +1468,69 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
{
struct pkt_metadata *md = &packet->md;
struct flow_tnl *tnl = &md->tunnel;
- struct vxlanhdr *vxh;
+ struct udp_header *udp;
pkt_metadata_init_tnl(md);
if (VXLAN_HLEN > dp_packet_size(packet)) {
return EINVAL;
}
- vxh = udp_extract_tnl_md(packet, tnl);
- if (!vxh) {
- return EINVAL;
+ udp = ip_extract_tnl_md(packet, tnl);
+ if (!udp) {
+ return EINVAL;;
}
- if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
- (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
- VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
- ntohl(get_16aligned_be32(&vxh->vx_flags)),
- ntohl(get_16aligned_be32(&vxh->vx_vni)));
- return EINVAL;
- }
- tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
- tnl->flags |= FLOW_TNL_F_KEY;
+ if (ntohs(udp->udp_dst) == VXGPE_DST_PORT) {
+
+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
+
+ if (get_16aligned_be32(&vxg->vx_vni) & htonl(0xff)) {
+ VLOG_WARN_RL(&err_rl, "invalid vxlan-gpe vni=%#x\n",
+ ntohl(get_16aligned_be32(&vxg->vx_vni)));
+ return EINVAL;;
+ }
+
+ tnl->tp_src = udp->udp_src;
+ tnl->tp_dst = udp->udp_dst;
+ tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxg->vx_vni)) >> 8);
+
+ if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
+
+ tnl->nsp = nsh->b.b2 << 8;
+ tnl->nsi = nsh->b.svc_idx;
+ tnl->nshc1 = nsh->c.nshc1;
+ tnl->nshc2 = nsh->c.nshc2;
+ tnl->nshc3 = nsh->c.nshc3;
+ tnl->nshc4 = nsh->c.nshc4;
+ tnl->flags |= FLOW_TNL_F_NSP;
+ tnl->flags |= FLOW_TNL_F_NSI;
+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
+
+ dp_packet_reset_packet(packet, VXNSH_HLEN);
+ } else {
+ VLOG_WARN("Unsupported vxlan GPE + NSH format!");
+ return EINVAL;;
+ }
+
+ } else {
+
+ struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
- dp_packet_reset_packet(packet, VXLAN_HLEN);
+ if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
+ (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
+ VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
+ ntohl(get_16aligned_be32(&vxh->vx_vni)));
+ return EINVAL;;
+ }
+
+ tnl->tp_src = udp->udp_src;
+ tnl->tp_dst = udp->udp_dst;
+ tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
+ dp_packet_reset_packet(packet, VXLAN_HLEN);
+ }
return 0;
}
@@ -1496,23 +1542,103 @@ netdev_vxlan_build_header(const struct netdev *netdev,
{
struct netdev_vport *dev = netdev_vport_cast(netdev);
struct netdev_tunnel_config *tnl_cfg;
- struct vxlanhdr *vxh;
+ struct ip_header *ip;
+ struct udp_header *udp;
+ bool isnsh = false;
/* XXX: RCUfy tnl_cfg. */
ovs_mutex_lock(&dev->mutex);
tnl_cfg = &dev->tnl_cfg;
- vxh = udp_build_header(tnl_cfg, tnl_flow, data);
+ ip = ip_hdr(data->header);
+ ip->ip_proto = IPPROTO_UDP;
+
+ udp = (struct udp_header *) (ip + 1);
+ udp->udp_dst = tnl_cfg->dst_port;
+
+ if (tnl_flow->tunnel.flags & FLOW_TNL_F_CSUM) {
+ /* Write a value in now to mark that we should compute the checksum
+ * later. 0xffff is handy because it is transparent to the
+ * calculation. */
+ udp->udp_csum = htons(0xffff);
+ }
+
+ if (ntohs(udp->udp_dst) == VXGPE_DST_PORT){
+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
- put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
- put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
+ memset(vxg, 0, sizeof *vxg);
+ vxg->i = 0x01;
+ vxg->p = 0x01;
+ vxg->ver = 0x01;
+ vxg->proto = VXG_P_NSH;
+ put_16aligned_be32(&vxg->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
+
+ if (vxg->p && vxg->proto == VXG_P_NSH){
+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
+
+ memset(nsh, 0, sizeof *nsh);
+ nsh->b.ver = 0x01;
+ nsh->b.len = 6;
+ nsh->b.mdtype = NSH_M_TYPE1;
+ nsh->b.proto = NSH_P_ETHERNET;
+
+ nsh->b.b2 = tnl_flow->tunnel.nsp >> 8;
+ nsh->b.svc_idx = tnl_flow->tunnel.nsi;
+
+ nsh->c.nshc1 = tnl_flow->tunnel.nshc1;
+ nsh->c.nshc2 = tnl_flow->tunnel.nshc2;
+ nsh->c.nshc3 = tnl_flow->tunnel.nshc3;
+ nsh->c.nshc4 = tnl_flow->tunnel.nshc4;
+
+ isnsh = true;
+ }
+
+ } else {
+ struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
+ put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
+ put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
+ }
ovs_mutex_unlock(&dev->mutex);
- data->header_len = VXLAN_HLEN;
+
+ if(isnsh)
+ data->header_len = VXNSH_HLEN;
+ else
+ data->header_len = VXLAN_HLEN;
data->tnl_type = OVS_VPORT_TYPE_VXLAN;
+
return 0;
}
+static void
+netdev_vxlan_push_header(struct dp_packet *packet,
+ const struct ovs_action_push_tnl *data)
+{
+ int ip_tot_size;
+ int size = data->header_len;
+ const void *header = data->header;
+ struct udp_header *udp;
+
+ udp = push_ip_header(packet, header, size, &ip_tot_size);
+
+ /* set udp src port */
+ udp->udp_src = get_src_port(packet);
+ udp->udp_len = htons(ip_tot_size - sizeof (struct ip_header));
+ /* udp_csum is zero */
+
+ if (udp->udp_csum) {
+ uint32_t csum = packet_csum_pseudoheader(ip_hdr(dp_packet_data(packet)));
+
+ csum = csum_continue(csum, udp,
+ ip_tot_size - sizeof (struct ip_header));
+ udp->udp_csum = csum_finish(csum);
+
+ if (!udp->udp_csum) {
+ udp->udp_csum = htons(0xffff);
+ }
+ }
+}
+
static int
netdev_geneve_pop_header(struct dp_packet *packet)
{
@@ -1736,7 +1862,7 @@ netdev_vport_tunnel_register(void)
netdev_gre_pop_header),
TUNNEL_CLASS("ipsec_gre", "gre_sys", NULL, NULL, NULL),
TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header,
- push_udp_header,
+ netdev_vxlan_push_header,
netdev_vxlan_pop_header),
TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL),
TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL),
diff --git a/lib/odp-util.c b/lib/odp-util.c
index e8bc86d..1696f77 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -468,12 +468,42 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
const struct vxlanhdr *vxh;
-
- vxh = format_udp_tnl_push_header(ds, ip);
-
- ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
- ntohl(get_16aligned_be32(&vxh->vx_flags)),
- ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
+ const struct udp_header *udp;
+ const struct vxgpehdr *vxg;
+
+ /* UDP */
+ udp = (const struct udp_header *) (ip + 1);
+ ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16",csum=0x%"PRIx16"),",
+ ntohs(udp->udp_src), ntohs(udp->udp_dst),
+ ntohs(udp->udp_csum));
+
+ /* VxLan & VxLan GPE(UDP port: 4790) */
+ if (ntohs(udp->udp_dst) == 4790) {
+ vxg = (const struct vxgpehdr *) (udp + 1);
+
+ ds_put_format(ds, "vxlangpe(vni=0x%"PRIx32",",
+ ntohl(get_16aligned_be32(&vxg->vx_vni)));
+ ds_put_format(ds, "proto=%"PRIu8"),", vxg->proto);
+ if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
+ const struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
+
+ /* NSH */
+ ds_put_format(ds, "nsh(mdtype=%"PRIu8",proto=%"PRIu8",",
+ nsh->b.mdtype, nsh->b.proto);
+ ds_put_format(ds, "nsp=%"PRIx32",nsi=%"PRIu8",",
+ nsh->b.b2 & 0x00FFFFFF, nsh->b.svc_idx);
+ ds_put_format(ds, "nshc1=%"PRIx32",nshc2=%"PRIx32",",
+ ntohl(nsh->c.nshc1), ntohl(nsh->c.nshc2));
+ ds_put_format(ds, "nshc3=%"PRIx32",nshc4=%"PRIx32",",
+ ntohl(nsh->c.nshc3), ntohl(nsh->c.nshc4));
+ ds_put_format(ds, ")");
+ }
+ } else {
+ vxh = (const struct vxlanhdr *) (udp + 1);
+ ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
+ ntohl(get_16aligned_be32(&vxh->vx_vni))>>8);
+ }
} else if (data->tnl_type == OVS_VPORT_TYPE_GENEVE) {
const struct genevehdr *gnh;
@@ -490,8 +520,8 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
ds, false);
ds_put_char(ds, ')');
}
-
ds_put_char(ds, ')');
+
} else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
const struct gre_base_hdr *greh;
ovs_16aligned_be32 *options;
@@ -504,7 +534,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
ntohs(greh->flags), ntohs(greh->protocol));
options = (ovs_16aligned_be32 *)(greh + 1);
if (greh->flags & htons(GRE_CSUM)) {
- ds_put_format(ds, ",csum=0x%"PRIx16, ntohs(*((ovs_be16 *)options)));
+ ds_put_format(ds, ",csum=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
options++;
}
if (greh->flags & htons(GRE_KEY)) {
@@ -791,8 +821,10 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
struct ip_header *ip;
struct udp_header *udp;
struct gre_base_hdr *greh;
+ struct nshhdr *nsh;
uint16_t gre_proto, gre_flags, dl_type, udp_src, udp_dst, csum;
- ovs_be32 sip, dip;
+ ovs_be32 sip, dip, nsp, nshc1,nshc2,nshc3,nshc4;
+ uint8_t nsi;
uint32_t tnl_type = 0, header_len = 0;
void *l3, *l4;
int n = 0;
@@ -837,71 +869,108 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
udp = (struct udp_header *) l4;
greh = (struct gre_base_hdr *) l4;
if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
- &udp_src, &udp_dst, &csum)) {
- uint32_t vx_flags, vni;
+ &udp_src, &udp_dst, &csum)) {
+ struct vxlanhdr *vxh;
+ struct vxgpehdr *vxg;
+ uint32_t vx_flags, vx_vni;
+ uint32_t geneve_vni;
udp->udp_src = htons(udp_src);
udp->udp_dst = htons(udp_dst);
udp->udp_len = 0;
udp->udp_csum = htons(csum);
+ vxh = (struct vxlanhdr *) (udp + 1);
+ vxg = (struct vxgpehdr *) (udp + 1);
+
if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
- &vx_flags, &vni)) {
- struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
+ &vx_flags, &vx_vni)) {
+ tnl_type = OVS_VPORT_TYPE_VXLAN;
put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
- put_16aligned_be32(&vxh->vx_vni, htonl(vni << 8));
- tnl_type = OVS_VPORT_TYPE_VXLAN;
+ put_16aligned_be32(&vxh->vx_vni, htonl(vx_vni<<8));
+
header_len = sizeof *eth + sizeof *ip +
sizeof *udp + sizeof *vxh;
- } else if (ovs_scan_len(s, &n, "geneve(")) {
- struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
- memset(gnh, 0, sizeof *gnh);
- header_len = sizeof *eth + sizeof *ip +
- sizeof *udp + sizeof *gnh;
+ } else if (ovs_scan_len(s, &n, "vxlangpe(vni=0x%"SCNx32",proto="SCNi8"),",
+ &vx_vni, &vxg->proto)) {
+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
- if (ovs_scan_len(s, &n, "oam,")) {
- gnh->oam = 1;
- }
- if (ovs_scan_len(s, &n, "crit,")) {
- gnh->critical = 1;
- }
- if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &vni)) {
+ tnl_type = OVS_VPORT_TYPE_VXLAN;
+ vxg->i = 0x01;
+ vxg->p = 0x01;
+ vxg->ver = 0x01;
+ put_16aligned_be32(&vxg->vx_vni, htonl(vx_vni));
+
+ if (ovs_scan_len(s, &n, "nsh(mdtype=%"SCNi8",proto=%"SCNi8",nsp=0x%"SCNx32
+ ",nsi=%"SCNi8",nshc1=0x%"SCNx32",nshc2=0x%"SCNx32
+ ",nshc3=0x%"SCNx32",nshc4=0x%"SCNx32"))",
+ &nsh->b.mdtype, &nsh->b.proto,
+ &nsp, &nsi,
+ &nshc1, &nshc2,
+ &nshc3, &nshc4)) {
+ nsh->b.ver = 0x01;
+ nsh->b.len = 6;
+ nsh->b.b2 = nsp;
+ nsh->b.svc_idx = nsi;
+ nsh->c.nshc1=nshc1;
+ nsh->c.nshc2=nshc2;
+ nsh->c.nshc3=nshc3;
+ nsh->c.nshc4=nshc4;
+ header_len = sizeof *eth + sizeof *ip +
+ sizeof *udp + sizeof *vxh + sizeof *nsh;
+ } else {
return -EINVAL;
}
- if (ovs_scan_len(s, &n, ",options(")) {
- struct geneve_scan options;
- int len;
-
- memset(&options, 0, sizeof options);
- len = scan_geneve(s + n, &options, NULL);
- if (!len) {
- return -EINVAL;
- }
+ } else if (ovs_scan_len(s, &n, "geneve(")) {
+ struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
- memcpy(gnh->options, options.d, options.len);
- gnh->opt_len = options.len / 4;
- header_len += options.len;
+ memset(gnh, 0, sizeof *gnh);
+ header_len = sizeof *eth + sizeof *ip +
+ sizeof *udp + sizeof *gnh;
- n += len;
- }
- if (!ovs_scan_len(s, &n, "))")) {
+ if (ovs_scan_len(s, &n, "oam,")) {
+ gnh->oam = 1;
+ }
+ if (ovs_scan_len(s, &n, "crit,")) {
+ gnh->critical = 1;
+ }
+ if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &geneve_vni)) {
+ return -EINVAL;
+ }
+ if (ovs_scan_len(s, &n, ",options(")) {
+ struct geneve_scan options;
+ int len;
+
+ memset(&options, 0, sizeof options);
+ len = scan_geneve(s + n, &options, NULL);
+ if (!len) {
return -EINVAL;
}
- gnh->proto_type = htons(ETH_TYPE_TEB);
- put_16aligned_be32(&gnh->vni, htonl(vni << 8));
- tnl_type = OVS_VPORT_TYPE_GENEVE;
- } else {
+ memcpy(gnh->options, options.d, options.len);
+ gnh->opt_len = options.len / 4;
+ header_len += options.len;
+
+ n += len;
+ }
+ if (!ovs_scan_len(s, &n, "))")) {
return -EINVAL;
}
- } else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
- &gre_flags, &gre_proto)){
- tnl_type = OVS_VPORT_TYPE_GRE;
- greh->flags = htons(gre_flags);
- greh->protocol = htons(gre_proto);
+ gnh->proto_type = htons(ETH_TYPE_TEB);
+ put_16aligned_be32(&gnh->vni, htonl(geneve_vni << 8));
+ tnl_type = OVS_VPORT_TYPE_GENEVE;
+ } else {
+ return -EINVAL;
+ }
+} else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
+ &gre_flags, &gre_proto)){
+
+ tnl_type = OVS_VPORT_TYPE_GRE;
+ greh->flags = htons(gre_flags);
+ greh->protocol = htons(gre_proto);
ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
if (greh->flags & htons(GRE_CSUM)) {
@@ -941,7 +1010,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
((uint8_t *) options - (uint8_t *) greh);
} else {
return -EINVAL;
- }
+ }
/* check tunnel meta data. */
if (data->tnl_type != tnl_type) {
@@ -1120,6 +1189,7 @@ parse_odp_action(const char *s, const struct simap *port_names,
struct ovs_action_push_tnl data;
int n;
+ memset(&data, 0, sizeof data);
n = ovs_parse_tnl_push(s, &data);
if (n > 0) {
odp_put_tnl_push_action(actions, &data);
@@ -3285,7 +3355,6 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
} SCAN_END_NESTED();
-
SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT);
SCAN_BEGIN("eth(", struct ovs_key_ethernet) {
diff --git a/lib/packets.h b/lib/packets.h
index 12f2239..7f9ab98 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -33,7 +33,6 @@
struct dp_packet;
struct ds;
-/* Tunnel information used in flow key and metadata. */
struct flow_tnl {
ovs_be32 ip_dst;
ovs_be32 ip_src;
@@ -913,6 +912,120 @@ struct vxlanhdr {
/* VXLAN GPE UDP DST PORT */
#define VXGPE_DST_PORT 4790
+/**
+ * struct vxlan_gpehdr - Generic Protocol Extension for VXLAN header.
+ * @p: Next Protocol field indicator bit
+ * @o: Operations and Management Packet indicator bit.
+ * @proto: IEEE Ethertypes to indicate the frame within.
+ * @vni: VXLAN Network Identifier.
+ */
+struct vxgpehdr {
+#ifdef WORDS_BIGENDIAN
+ uint8_t res1:4;
+ uint8_t i:1;
+ uint8_t p:1;
+ uint8_t res2:1;
+ uint8_t o:1;
+
+ uint8_t ver:2;
+ uint8_t res3:6;
+#else
+ uint8_t o:1;
+ uint8_t res2:1;
+ uint8_t p:1;
+ uint8_t i:1;
+ uint8_t res1:4;
+
+ uint8_t res3:6;
+ uint8_t ver:2;
+#endif
+ uint8_t res4;
+ uint8_t proto;
+ ovs_16aligned_be32 vx_vni;
+};
+
+/* VxLAN-GPE Header Next Protocol */
+#define VXG_P_IPV4 0x01
+#define VXG_P_IPV6 0x02
+#define VXG_P_ETHERNET 0x03
+#define VXG_P_NSH 0x04
+
+/**
+ * struct nsh_bhdr - Network Service Base Header.
+ * @o: Operations and Management Packet indicator bit
+ * @c: If this bit is set then one or more contexts are in use.
+ * @proto: IEEE Ethertypes to indicate the frame within.
+ * @svc_idx: TTL functionality and location within service path.
+ * @svc_path: To uniquely identify service path.
+ */
+struct nsh_base {
+#ifdef WORDS_BIGENDIAN
+ uint8_t ver:2;
+ uint8_t o:1;
+ uint8_t c:1;
+ uint8_t res1:4;
+
+ uint8_t res2:2;
+ uint8_t len:6;
+#else
+ uint8_t res1:4;
+ uint8_t c:1;
+ uint8_t o:1;
+ uint8_t ver:2;
+
+ uint8_t len:6;
+ uint8_t res2:2;
+#endif
+ uint8_t mdtype;
+ uint8_t proto;
+ union {
+ struct {
+ uint8_t svc_path[3];
+ uint8_t svc_idx;
+ };
+ ovs_be32 b2;
+ };
+};
+
+/**
+ * struct nsh_ctx - Keeps track of NSH context data
+ * @npc: NSH network platform context
+ * @nsc: NSH network shared context
+ * @spc: NSH service platform context
+ * @ssc: NSH service shared context
+ */
+struct nsh_ctx {
+ ovs_be32 nshc1;
+ ovs_be32 nshc2;
+ ovs_be32 nshc3;
+ ovs_be32 nshc4;
+};
+
+/**
+ * struct nshdr - Network Service header
+ * @nsh_base: Network Service Base Header.
+ * @nsh_ctx: Network Service Context Header.
+ */
+struct nshhdr {
+ struct nsh_base b;
+ struct nsh_ctx c;
+};
+
+/* NSH Base Header Next Protocol */
+#define NSH_P_IPV4 0x01
+#define NSH_P_IPV6 0x02
+#define NSH_P_ETHERNET 0x03
+
+/* MD Type Registry */
+#define NSH_M_TYPE1 0x01
+#define NSH_M_TYPE2 0x02
+#define NSH_M_EXP1 0xFE
+#define NSH_M_EXP2 0xFF
+
+/* Used for masking nsp and nsi values in field nsp below */
+#define NSH_M_NSP 0xFFFFFF00 //uncertain
+#define NSH_M_NSI 0x000000FF
+
#define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */
void format_ipv6_addr(char *addr_str, const struct in6_addr *addr);
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 4bb9801..9c64c24 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -4780,7 +4780,6 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
if (!xbridge) {
return;
}
-
struct flow *flow = &xin->flow;
union mf_subvalue stack_stub[1024 / sizeof(union mf_subvalue)];
diff --git a/tests/tunnel.at b/tests/tunnel.at
index f43a07d..5ec5e6c 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -527,6 +527,124 @@ AT_CHECK([tail -1 stdout], [0],
OVS_VSWITCHD_STOP(["/receive tunnel port not found/d"])
AT_CLEANUP
+AT_SETUP([tunnel - VXLAN-GPE NSH user space])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
+ options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4790])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
+ br0 65534/100: (dummy)
+ p1 1/4790: (vxlan: dst_port=4790, remote_ip=1.1.1.1)
+])
+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
+AT_CLEANUP
+
+AT_SETUP([tunnel VXLAN-GPE NSH - encap - nsh/nsi/nshc user space])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan options:key=flow \
+ options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=1 \
+ -- add-port br0 p2 -- set Interface p2 type=vxlan options:key=flow \
+ options:remote_ip=flow options:dst_port=4790 ofport_request=2 \
+ -- add-port br0 p3 -- set Interface p3 type=vxlan options:key=flow \
+ options:remote_ip=2.2.2.2 options:dst_port=4790 options:nsp=111 options:nsi=11 options:nshc1=11 options:nshc2=12 options:nshc3=13 options:nshc4=14 ofport_request=3 \
+ -- add-port br0 p4 -- set Interface p4 type=vxlan options:key=flow \
+ options:remote_ip=3.3.3.3 options:dst_port=4790 options:nsp=222 options:nsi=22 options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=4 \
+ -- add-port br0 p5 -- set Interface p5 type=vxlan options:key=flow \
+ options:remote_ip=4.4.4.4 options:dst_port=4790 options:nsp=flow options:nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=5 \
+ -- add-port br0 p6 -- set Interface p6 type=vxlan options:key=flow \
+ options:remote_ip=flow options:dst_port=4790 options:nsp=flow options:nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=6])
+
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
+])
+AT_CHECK([ovs-vsctl add-port br0 p7 -- set Interface p7 type=dummy ofport_request=7])
+AT_CHECK([ovs-vsctl add-port br0 p8 -- set Interface p8 type=dummy ofport_request=8])
+
+AT_CHECK([
+ovs-appctl ovs/route/add 1.1.1.1/24 br0
+ovs-appctl tnl/arp/set br0 1.1.1.1 68:05:ca:30:6b:d1
+ovs-appctl ovs/route/add 2.2.2.2/24 br0
+ovs-appctl tnl/arp/set br0 2.2.2.2 68:05:ca:30:6b:d2
+ovs-appctl ovs/route/add 3.3.3.3/24 br0
+ovs-appctl tnl/arp/set br0 3.3.3.3 68:05:ca:30:6b:d3
+ovs-appctl ovs/route/add 4.4.4.4/24 br0
+ovs-appctl tnl/arp/set br0 4.4.4.4 68:05:ca:30:6b:d4
+ovs-appctl ovs/route/add 5.5.5.5/24 br0
+ovs-appctl tnl/arp/set br0 5.5.5.5 68:05:ca:30:6b:d5
+],[0],[stdout])
+
+AT_DATA([flows.txt], [dnl
+in_port=7 actions=resubmit:1,resubmit:2,resubmit:3
+in_port=1 actions=output:1
+in_port=2 actions=set_field:3.3.3.3->tun_dst,output:2
+in_port=3 actions=output:3
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(7),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no), icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d1,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=1.1.1.1,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=0,nsi=1,nshc1=0,nshc2=0,nshc3=0,nshc4=0,)),out_port(100)),tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d3,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=3.3.3.3,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=0,nsi=1,nshc1=0,nshc2=0,nshc3=0,nshc4=0,)),out_port(100)),tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d2,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=2.2.2.2,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=6f0000,nsi=11,nshc1=b,nshc2=c,nshc3=d,nshc4=e,)),out_port(100))
+])
+
+AT_DATA([flows.txt], [dnl
+in_port=8 actions=resubmit:4,resubmit:5,resubmit:6
+in_port=4 actions=set_nshc1:22,set_nshc2:23,set_nshc3:24,set_nshc4:25,output:4
+in_port=5 actions=set_nsp:333,set_nsi:33,set_nshc1:33,set_nshc2:34,set_nshc3:35,set_nshc4:36,output:5
+in_port=6 actions=set_field:5.5.5.5->tun_dst,set_nsp:444,set_nsi:44,set_nshc1:44,set_nshc2:45,set_nshc3:46,set_nshc4:47,output:6
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(8),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no), icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d3,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=3.3.3.3,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=de0000,nsi=22,nshc1=16,nshc2=17,nshc3=18,nshc4=19,)),out_port(100)),tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d4,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=4.4.4.4,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=4d0100,nsi=33,nshc1=21,nshc2=22,nshc3=23,nshc4=24,)),out_port(100)),tnl_push(tnl_port(4790),header(size=74,type=4,eth(dst=68:05:ca:30:6b:d5,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=5.5.5.5,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlangpe(vni=0x0,proto=4),nsh(mdtype=1,proto=3,nsp=bc0100,nsi=44,nshc1=2c,nshc2=2d,nshc3=2e,nshc4=2f,)),out_port(100))
+])
+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
+AT_CLEANUP
+
+AT_SETUP([tunnel VXLAN-GPE NSH - decap - nsh/nsi/nshc user space])
+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
+AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0])
+AT_CHECK([ovs-vsctl add-port int-br p1 -- set Interface p1 type=vxlan options:key=flow \
+ options:remote_ip=1.1.1.1 options:dst_port=4790 options:nsp=111 options:nsi=11 options:nshc1=11 options:nshc2=12 options:nshc3=13 options:nshc4=14 ofport_request=2 \
+ -- add-port int-br p2 -- set Interface p2 type=vxlan options:key=flow \
+ options:remote_ip=2.2.2.2 options:dst_port=4790 options:nsp=flow options:nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=3 \
+ -- add-port int-br p3 -- set Interface p3 type=vxlan options:key=flow \
+ options:remote_ip=flow options:dst_port=4790 options:nsp=flow options:nsi=flow options:nshc1=flow options:nshc2=flow options:nshc3=flow options:nshc4=flow ofport_request=4], [0])
+
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK
+])
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
+ br0 65534/100: (dummy)
+ p0 1/1: (dummy)
+ int-br:
+ int-br 65534/2: (dummy)
+ p1 2/4790: (vxlan: dst_port=4790, key=flow, nshc1=0xb, nshc2=0xc, nshc3=0xd, nshc4=0xe, nsi=11, nsp=0x6f, remote_ip=1.1.1.1)
+ p2 3/4790: (vxlan: dst_port=4790, key=flow, nshc1=flow, nshc2=flow, nshc3=flow, nshc4=flow, nsi=flow, nsp=flow, remote_ip=2.2.2.2)
+ p3 4/4790: (vxlan: dst_port=4790, key=flow, nshc1=flow, nshc2=flow, nshc3=flow, nshc4=flow, nsi=flow, nsp=flow, remote_ip=flow)
+])
+
+AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
+Listening ports:
+vxlan_sys_4790 (4790)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4790)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: tnl_pop(4790)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=2.2.2.2,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4790)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: tnl_pop(4790)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=3.4.5.6,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4790)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: tnl_pop(4790)
+])
+
+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
+AT_CLEANUP
+
AT_SETUP([tunnel - Geneve metadata])
OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
options:remote_ip=1.1.1.1 ofport_request=1 \
--
1.9.3

1025
ovs-nsh/patches/060681.patch Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,730 @@
Ethernet-NSH packet.
With this feature(options:nsh-convert=true),when VxLAN-GPE NSH packets (Outer
MAC header + Outer IP header + UDP header + VxLAN-GPE + NSH + original packet)
are received by VxLAN-GPE NSH port, the vport will remove Outer MAC header,
Outer IP header, UDP header, VxLAN-GPE header, and then modify and push the
outer MAC header. Then the packet with VxLAN-GPE+NSH format is converted to
Outer MAC header + NSH header + original packet.
Signed-off-by: Ricky Li <<A HREF="http://openvswitch.org/mailman/listinfo/dev">ricky.li at intel.com</A>>
Signed-off-by: Mengke Liu <<A HREF="http://openvswitch.org/mailman/listinfo/dev">mengke.liu at intel.com</A>>
---
datapath/linux/compat/include/linux/openvswitch.h | 6 +-
lib/netdev-vport.c | 109 +++++++++++++++++++++-
lib/netdev.h | 6 ++
lib/odp-util.c | 80 ++++++++++++++--
lib/ovs-router.c | 64 +++++++++++++
lib/ovs-router.h | 1 +
lib/packets.h | 13 ++-
ofproto/ofproto-dpif-xlate.c | 64 ++++++++++++-
ofproto/tunnel.c | 37 ++++++++
ofproto/tunnel.h | 5 +
tests/tunnel.at | 32 +++++++
11 files changed, 398 insertions(+), 19 deletions(-)
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index b8ac152..3d588bb 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -230,6 +230,7 @@ enum ovs_vport_type {
OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */
OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */
OVS_VPORT_TYPE_STT = 106, /* STT tunnel */
+ OVS_VPORT_TYPE_NSH, /* L2+NSH ENCAP tunnel. */
__OVS_VPORT_TYPE_MAX
};
@@ -646,7 +647,10 @@ struct ovs_action_push_tnl {
uint8_t header[TNL_PUSH_HEADER_SIZE];
};
-#define OVS_POP_SPEC_ACTION_NO_DECAP 2
+enum ovs_pop_spec_action_type {
+ OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH,
+ OVS_POP_SPEC_ACTION_NO_DECAP = 2,
+};
/*
* struct ovs_action_pop_tnl - %OVS_ACTION_ATTR_TUNNEL_POP_SPEC
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index d926c00..6e0d5ba 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -561,6 +561,23 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
} else {
tnl_cfg.ip_dst = in_addr.s_addr;
}
+ } else if (!strcmp(node->key, "remote_mac")) {
+ if (!strcmp(node->value, "flow")) {
+ tnl_cfg.eth_dst_flow = true;
+ VLOG_ERR("remote_mac doesn't support setting by flow");
+ return EINVAL;
+ } else if (eth_addr_from_string(node->value,&tnl_cfg.eth_dst)){
+ tnl_cfg.eth_dst_present = true;
+ } else {
+ VLOG_WARN("%s: bad %s 'remote_mac'", name, type);
+ return EINVAL;
+ }
+ } else if (!strcmp(node->key, "nsh_convert")) {
+ if (!strcmp(node->value, "true")) {
+ tnl_cfg.nsh_convert = true;
+ } else {
+ tnl_cfg.nsh_convert = false;
+ }
} else if (!strcmp(node->key, "tun_nodecap")) {
if (!strcmp(node->value, "true")) {
tnl_cfg.tun_nodecap = true;
@@ -883,6 +900,13 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
if (!tnl_cfg.dont_fragment) {
smap_add(args, "df_default", "false");
}
+ if (tnl_cfg.eth_dst_present) {
+ smap_add_format(args, "remote_mac", ETH_ADDR_FMT, ETH_ADDR_ARGS(tnl_cfg.eth_dst));
+ }
+
+ if (tnl_cfg.nsh_convert) {
+ smap_add(args, "nsh_convert", "true");
+ }
if (tnl_cfg.tun_nodecap) {
smap_add(args, "tun_nodecap", "true");
@@ -1546,6 +1570,84 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
}
static int
+vxlan_extract_md_convert_to_eth_nsh(struct dp_packet *packet, const struct ovs_action_pop_tnl *data)
+{
+ struct pkt_metadata *md = &packet->md;
+ struct flow_tnl *tnl = &md->tunnel;
+ struct udp_header *udp;
+
+ memset(md, 0, sizeof *md);
+ if (VXLAN_HLEN > dp_packet_size(packet)) {
+ return EINVAL;
+ }
+
+ udp = ip_extract_tnl_md(packet, tnl);
+ if (!udp) {
+ return EINVAL;
+ }
+
+ if (ntohs(udp->udp_dst) == VXGPE_DST_PORT) {
+
+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
+
+ if (get_16aligned_be32(&vxg->vx_vni) & htonl(0xff)) {
+ VLOG_WARN_RL(&err_rl, "invalid vxlan-gpe vni=%#x\n",
+ ntohl(get_16aligned_be32(&vxg->vx_vni)));
+ return EINVAL;
+ }
+
+ tnl->tp_src = udp->udp_src;
+ tnl->tp_dst = udp->udp_dst;
+ tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxg->vx_vni)) >> 8);
+
+ if (vxg->p == 0x01 && vxg->proto == VXG_P_NSH) {
+ struct nshhdr *nsh = (struct nshhdr *) (vxg + 1);
+ struct eth_header *eth = NULL;
+
+ tnl->nsp = nsh->b.b2 << 8;
+ tnl->nsi = nsh->b.svc_idx;
+ tnl->nshc1 = nsh->c.nshc1;
+ tnl->nshc2 = nsh->c.nshc2;
+ tnl->nshc3 = nsh->c.nshc3;
+ tnl->nshc4 = nsh->c.nshc4;
+ tnl->flags |= FLOW_TNL_F_NSP;
+ tnl->flags |= FLOW_TNL_F_NSI;
+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
+ tnl->nsh_flags = NSH_TNL_F_ETHERNET;
+
+ dp_packet_reset_packet(packet, VXNSH_HLEN - sizeof (struct nshhdr));
+ eth = (struct eth_header *) dp_packet_push_uninit(packet, data->header_len);
+ memcpy(eth, data->header, data->header_len);
+ eth->eth_type = htons(ETH_TYPE_NSH);
+ } else {
+ VLOG_WARN("Unsupported vxlan GPE + NSH format!");
+ return EINVAL;
+ }
+
+ } else {
+
+ struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
+
+ if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
+ (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
+ VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
+ ntohl(get_16aligned_be32(&vxh->vx_vni)));
+ return EINVAL;
+ }
+
+ tnl->tp_src = udp->udp_src;
+ tnl->tp_dst = udp->udp_dst;
+ tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
+ dp_packet_reset_packet(packet, VXLAN_HLEN);
+ }
+
+ return 0;
+
+}
+
+static int
vxlan_extract_md_no_decap(struct dp_packet *packet)
{
struct pkt_metadata *md = &packet->md;
@@ -1595,6 +1697,7 @@ vxlan_extract_md_no_decap(struct dp_packet *packet)
tnl->flags |= FLOW_TNL_F_NSI;
tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
+ tnl->tun_len = VXNSH_HLEN;
tnl->nsh_flags = NSH_TNL_F_NODECAP;
} else {
VLOG_WARN("Unsupported vxlan GPE + NSH format!");
@@ -1606,19 +1709,19 @@ vxlan_extract_md_no_decap(struct dp_packet *packet)
return 0;
}
-
static int
netdev_vxlan_pop_header_spec(struct dp_packet *packet,
const struct ovs_action_pop_tnl *data)
{
- if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
+ if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
+ return vxlan_extract_md_convert_to_eth_nsh(packet, data);
+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
return vxlan_extract_md_no_decap(packet);
}
return EINVAL;
}
-
static int
netdev_vxlan_build_header(const struct netdev *netdev,
struct ovs_action_push_tnl *data,
diff --git a/lib/netdev.h b/lib/netdev.h
index b30c932..26013ef 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -150,6 +150,10 @@ struct netdev_tunnel_config {
bool ipsec;
bool dont_fragment;
+ bool eth_dst_present;
+ bool eth_dst_flow;
+ struct eth_addr eth_dst;
+
bool in_nshc1_present;
bool in_nshc1_flow;
ovs_be32 in_nshc1; /* incoming NSH context c1 */
@@ -182,6 +186,7 @@ struct netdev_tunnel_config {
bool out_nshc4_flow;
ovs_be32 out_nshc4; /* outgoing NSH context c4 */
+ bool nsh_convert;
bool tun_nodecap;
};
@@ -247,6 +252,7 @@ int netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers,
int netdev_pop_header_spec(struct netdev *netdev,
struct dp_packet **buffers, int cnt,
const struct ovs_action_pop_tnl *data);
+
/* Hardware address. */
int netdev_set_etheraddr(struct netdev *, const struct eth_addr mac);
int netdev_get_etheraddr(const struct netdev *, struct eth_addr *mac);
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 190117f..6da2d5b 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -552,16 +552,22 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
}
static void
-format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
+format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data)
{
- struct ovs_action_pop_tnl *data;
+ const struct eth_header *eth;
- data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
+ eth = (const struct eth_header *)data->header;
+ if (data->tnl_type == OVS_VPORT_TYPE_NSH) {
+ const struct nshhdr *nsh = (const struct nshhdr *) (eth + 1);
- ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
- if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
- ds_put_format(ds, "pop_type=%"PRIu16")",
- OVS_POP_SPEC_ACTION_NO_DECAP);
+ /* Ethernet */
+ ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
+ data->header_len, data->tnl_type);
+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
+ ds_put_format(ds, ",src=");
+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
+ ds_put_format(ds, ",dl_type=0x%04"PRIx16")", ntohs(eth->eth_type));
+ ds_put_format(ds, "),");
}
}
@@ -578,6 +584,26 @@ format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr)
}
static void
+format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
+{
+ struct ovs_action_pop_tnl *data;
+
+ data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
+
+ ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
+ if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
+ ds_put_format(ds, "pop_type=%"PRIu16",",
+ OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH);
+ format_odp_tnl_pop_header(ds, data);
+ ds_put_format(ds, "out_port(%"PRIu32"))", data->out_port);
+
+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
+ ds_put_format(ds, "pop_type=%"PRIu16")",
+ OVS_POP_SPEC_ACTION_NO_DECAP);
+ }
+}
+
+static void
format_odp_action(struct ds *ds, const struct nlattr *a)
{
int expected_len;
@@ -1050,11 +1076,8 @@ static int
ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
{
struct eth_header *eth;
- struct nshhdr *nsh;
uint32_t tnl_type = 0, header_len = 0;
uint16_t dl_type;
- ovs_be32 nsp, nshc1, nshc2, nshc3, nshc4;
- uint8_t nsi;
int n = 0;
if (!ovs_scan_len(s, &n, "tnl_pop_spec(tnl_port(%"SCNi32"),",
&data->tnl_port)) {
@@ -1068,6 +1091,42 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
return n;
+
+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
+
+ eth = (struct eth_header *) data->header;
+
+ if (!ovs_scan_len(s, &n, ",header(size=%"SCNi32",type=%"SCNi32","
+ "eth(dst="ETH_ADDR_SCAN_FMT",",
+ &data->header_len,
+ &data->tnl_type,
+ ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
+ return -EINVAL;
+ }
+ if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
+ ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
+ return -EINVAL;
+ }
+ if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
+ return -EINVAL;
+ }
+ eth->eth_type = htons(dl_type);
+
+ tnl_type = OVS_VPORT_TYPE_NSH;
+ header_len = sizeof *eth;
+
+ /* check tunnel meta data. */
+ if (data->tnl_type != tnl_type) {
+ return -EINVAL;
+ }
+ if (data->header_len != header_len) {
+ return -EINVAL;
+ }
+
+ /* Out port */
+ if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
+ return -EINVAL;
+ }
} else {
return -EINVAL;
}
@@ -1075,6 +1134,7 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
return n;
}
+
static int
parse_odp_action(const char *s, const struct simap *port_names,
struct ofpbuf *actions)
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index d6c7652..9f61bac 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -82,6 +82,24 @@ ovs_router_lookup(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw)
return route_table_fallback_lookup(ip_dst, output_bridge, gw);
}
+bool
+ovs_router_lookup_mac(const struct eth_addr *mac, char output_bridge[])
+{
+ const struct cls_rule *cr;
+ struct flow s_flow;
+
+ memset(&s_flow, 0, sizeof (struct flow));
+ memcpy(s_flow.dl_dst.ea, mac->ea, ETH_ADDR_LEN);
+ cr = classifier_lookup(&cls,CLS_MAX_VERSION, &s_flow, NULL);
+ if (cr) {
+ struct ovs_router_entry *p = ovs_router_entry_cast(cr);
+
+ strncpy(output_bridge, p->output_bridge, IFNAMSIZ);
+ return true;
+ }
+ return false;
+}
+
static void
rt_entry_free(struct ovs_router_entry *p)
{
@@ -133,6 +151,36 @@ ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, uint8_t plen,
seq_change(tnl_conf_seq);
}
+static void
+ovs_router_insert_mac__(uint8_t priority, struct eth_addr *mac,
+ const char output_bridge[])
+{
+ const struct cls_rule *cr;
+ struct ovs_router_entry *p;
+ struct match s_match;
+
+ memset(&s_match, 0, sizeof (struct match));
+ memcpy(s_match.flow.dl_dst.ea, mac->ea, ETH_ADDR_LEN);
+
+ p = xzalloc(sizeof *p);
+ strncpy(p->output_bridge, output_bridge, IFNAMSIZ);
+ p->gw = 0;
+ p->nw_addr = 0;
+ p->plen = 32;
+ p->priority = priority;
+ cls_rule_init(&p->cr, &s_match, priority); /* Longest prefix matches first. */
+
+ ovs_mutex_lock(&mutex);
+ cr = classifier_replace(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0);
+ ovs_mutex_unlock(&mutex);
+
+ if (cr) {
+ /* An old rule with the same match was displaced. */
+ ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
+ }
+ seq_change(tnl_conf_seq);
+}
+
void
ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[],
ovs_be32 gw)
@@ -231,6 +279,20 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
}
static void
+ovs_router_add_mac(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ struct eth_addr mac;
+
+ if (eth_addr_from_string(argv[1], &mac)) {
+ ovs_router_insert_mac__(48, &mac, argv[2]);
+ unixctl_command_reply(conn, "OK");
+ } else {
+ unixctl_command_reply(conn, "Invalid parameters");
+ }
+}
+
+static void
ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
const char *argv[], void *aux OVS_UNUSED)
{
@@ -326,6 +388,8 @@ ovs_router_init(void)
classifier_init(&cls, NULL);
unixctl_command_register("ovs/route/add", "ipv4_addr/prefix_len out_br_name gw", 2, 3,
ovs_router_add, NULL);
+ unixctl_command_register("ovs/route/addmac", "mac_addr out_br_name", 2, 2,
+ ovs_router_add_mac, NULL);
unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL);
unixctl_command_register("ovs/route/del", "ipv4_addr/prefix_len", 1, 1, ovs_router_del,
NULL);
diff --git a/lib/ovs-router.h b/lib/ovs-router.h
index cc0ebc2..3f5a504 100644
--- a/lib/ovs-router.h
+++ b/lib/ovs-router.h
@@ -23,6 +23,7 @@
extern "C" {
#endif
+bool ovs_router_lookup_mac(const struct eth_addr *mac, char output_bridge[]);
bool ovs_router_lookup(ovs_be32 ip_dst, char out_dev[], ovs_be32 *gw);
void ovs_router_init(void);
void ovs_router_insert(ovs_be32 ip_dst, uint8_t plen,
diff --git a/lib/packets.h b/lib/packets.h
index 87c955a..c586390 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -33,6 +33,8 @@
struct dp_packet;
struct ds;
+#define ETH_ADDR_LEN 6
+
/* Tunnel information used in flow key and metadata. */
struct flow_tnl {
ovs_be32 ip_dst;
@@ -52,7 +54,9 @@ struct flow_tnl {
ovs_be32 nshc2;
ovs_be32 nshc3;
ovs_be32 nshc4;
- uint8_t pad1[7]; /* Pad to 64 bits. */
+ struct eth_addr eth_dst;
+ uint8_t tun_len;
+ uint8_t pad1[4]; /* Pad to 64 bits. */
struct tun_metadata metadata;
};
@@ -83,7 +87,9 @@ struct flow_tnl {
#define FLOW_TNL_F_NSH_C3 (1 << 9)
#define FLOW_TNL_F_NSH_C4 (1 << 10)
-#define NSH_TNL_F_NODECAP (1 << 1)
+#define NSH_TNL_F_ETHERNET (1 << 0)
+#define NSH_TNL_F_VXLAN (1 << 1)
+#define NSH_TNL_F_NODECAP (1 << 2)
/* Returns an offset to 'src' covering all the meaningful fields in 'src'. */
static inline size_t
@@ -160,8 +166,6 @@ pkt_metadata_init(struct pkt_metadata *md, odp_port_t port)
bool dpid_from_string(const char *s, uint64_t *dpidp);
-#define ETH_ADDR_LEN 6
-
static const struct eth_addr eth_addr_broadcast OVS_UNUSED
= { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } };
@@ -352,6 +356,7 @@ ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos,
#define ETH_TYPE_RARP 0x8035
#define ETH_TYPE_MPLS 0x8847
#define ETH_TYPE_MPLS_MCAST 0x8848
+#define ETH_TYPE_NSH 0x894f
static inline bool eth_type_mpls(ovs_be16 eth_type)
{
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 71e255e..bff0a83 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2690,6 +2690,36 @@ tnl_route_lookup_flow(const struct flow *oflow,
}
static int
+tnl_outdev_lookup_mac(const struct eth_addr *mac,
+ struct xport **out_port)
+{
+ char out_dev[IFNAMSIZ];
+ struct xbridge *xbridge;
+ struct xlate_cfg *xcfg;
+
+ if (!ovs_router_lookup_mac(mac, out_dev)) {
+ return -ENOENT;
+ }
+
+ xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
+ ovs_assert(xcfg);
+
+ HMAP_FOR_EACH (xbridge, hmap_node, &xcfg->xbridges) {
+ if (!strncmp(xbridge->name, out_dev, IFNAMSIZ)) {
+ struct xport *port;
+
+ HMAP_FOR_EACH (port, ofp_node, &xbridge->xports) {
+ if (!strncmp(netdev_get_name(port->netdev), out_dev, IFNAMSIZ)) {
+ *out_port = port;
+ return 0;
+ }
+ }
+ }
+ }
+ return -ENOENT;
+}
+
+static int
compose_table_xlate(struct xlate_ctx *ctx, const struct xport *out_dev,
struct dp_packet *packet)
{
@@ -2795,7 +2825,39 @@ build_tunnel_pop(const struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct
cfg = tnl_port_cfg(tunnel_odp_port, flow);
if (cfg) {
- if (cfg->tun_nodecap) {
+ if (cfg->nsh_convert && (ntohs(cfg->dst_port) == VXGPE_DST_PORT)) {
+ struct ovs_action_pop_tnl tnl_pop_data;
+ struct xport *out_dev = NULL;
+ struct eth_addr smac;
+
+ int err;
+
+ err = tnl_outdev_lookup_mac(&cfg->eth_dst, &out_dev);
+ if (err) {
+ VLOG_WARN("tnl_outdev_lookup_mac failed...");
+ return err;
+ }
+
+ /* Use mac addr of bridge port of the peer. */
+ err = netdev_get_etheraddr(out_dev->netdev, &smac);
+ if (err) {
+ VLOG_WARN("netdev_get_etheraddr failed...");
+ return err;
+ }
+
+ err = tnl_port_build_nsh_header_odport_popspec(tunnel_odp_port, flow,
+ &cfg->eth_dst, &smac, &tnl_pop_data);
+ if (err) {
+ VLOG_WARN("tnl_port_build_nsh_header failed...");
+ return err;
+ }
+ tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
+ tnl_pop_data.out_port = odp_to_u32(out_dev->odp_port);
+ tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH;
+ tnl_pop_data.tnl_type = OVS_VPORT_TYPE_NSH;
+ odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
+
+ } else if (cfg->tun_nodecap) {
struct ovs_action_pop_tnl tnl_pop_data;
memset(&tnl_pop_data, 0, sizeof tnl_pop_data);
diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
index 4606fb6..b0e46e6 100644
--- a/ofproto/tunnel.c
+++ b/ofproto/tunnel.c
@@ -45,6 +45,8 @@ VLOG_DEFINE_THIS_MODULE(tunnel);
/* skb mark used for IPsec tunnel packets */
#define IPSEC_MARK 1
+#define ETH_NSH_HLEN (sizeof(struct eth_header) + \
+ sizeof(struct nshhdr))
struct tnl_match {
ovs_be64 in_key;
ovs_be32 in_nsp;
@@ -568,6 +570,9 @@ tnl_port_cfg(odp_port_t odp_port, struct flow *flow) OVS_EXCLUDED(rwlock)
cfg = netdev_get_tunnel_config(tnl_port->netdev);
ovs_assert(cfg);
+ if (!cfg->eth_dst_flow) {
+ memcpy(flow->tunnel.eth_dst.ea, cfg->eth_dst.ea, ETH_ADDR_LEN);
+ }
if (!cfg->out_nsp_flow) {
flow->tunnel.nsp = cfg->out_nsp;
}
@@ -602,6 +607,7 @@ out:
return cfg;
}
+
static uint32_t
tnl_hash(struct tnl_match *match)
{
@@ -1063,3 +1069,34 @@ tnl_port_build_header(const struct ofport_dpif *ofport,
return res;
}
+
+int
+tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port,
+ const struct flow *tnl_flow OVS_UNUSED,
+ const struct eth_addr *dmac,
+ const struct eth_addr *smac,
+ struct ovs_action_pop_tnl *data)
+{
+ struct tnl_port *tnl_port;
+ struct eth_header *eth;
+ int res = 0;
+
+ fat_rwlock_rdlock(&rwlock);
+ tnl_port = tnl_find_odp_port(odp_port);
+ ovs_assert(tnl_port);
+
+ /* Build Ethernet and IP headers. */
+ memset(data->header, 0, sizeof data->header);
+
+ eth = (struct eth_header *)data->header;
+ memcpy(eth->eth_dst.ea, dmac->ea, ETH_ADDR_LEN);
+ memcpy(eth->eth_src.ea, smac->ea, ETH_ADDR_LEN);
+ eth->eth_type = htons(ETH_TYPE_NSH);
+
+ data->header_len = ETH_NSH_HLEN - sizeof (struct nshhdr);
+ data->tnl_type = OVS_VPORT_TYPE_NSH;
+
+ fat_rwlock_unlock(&rwlock);
+
+ return res;
+}
diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h
index 2b608ce..0c51a4e 100644
--- a/ofproto/tunnel.h
+++ b/ofproto/tunnel.h
@@ -59,4 +59,9 @@ int tnl_port_build_header(const struct ofport_dpif *ofport,
const struct eth_addr dmac,
const struct eth_addr smac,
ovs_be32 ip_src, struct ovs_action_push_tnl *data);
+int tnl_port_build_nsh_header_odport_popspec(const odp_port_t odp_port,
+ const struct flow *tnl_flow OVS_UNUSED,
+ const struct eth_addr *dmac,
+ const struct eth_addr *smac,
+ struct ovs_action_pop_tnl *data);
#endif /* tunnel.h */
diff --git a/tests/tunnel.at b/tests/tunnel.at
index 851afdc..dc35809 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -673,6 +673,38 @@ AT_CHECK([tail -1 stdout], [0],
OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
AT_CLEANUP
+AT_SETUP([tunnel - VXLAN-GPE NSH - nsh_convert from VXLAN-GPE NSH to Ethernet NSH - user space])
+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
+AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0])
+AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=vxlan options:remote_ip=1.1.1.1 options:dst_port=4790 \
+ options:nsh_convert=true options:nsi=flow options:nsp=flow options:nshc1=flow options:in_key=flow options:remote_mac=00:00:00:11:11:22 ofport_request=2])
+AT_CHECK([ovs-vsctl add-port int-br p2 -- set Interface p2 type=dummy ofport_request=3])
+
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 1.1.1.1/24 br0], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/addmac 00:00:00:11:11:22 br0],[0],[dnl
+OK
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+
+AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
+Listening ports:
+vxlan_sys_4790 (4790)
+])
+
+dnl remote_ip p0
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(src=50:54:00:00:00:05,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.1.1,dst=2.2.2.22,proto=17,tos=0,ttl=64,frag=no),udp(src=8,dst=4790)'], [0], [stdout])
+
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: tnl_pop_spec(tnl_port(4790),pop_type=0,header(size=14,type=107,eth(dst=00:00:00:11:11:22,src=aa:55:aa:55:00:00,dl_type=0x894f)),out_port(100))
+])
+
+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
+AT_CLEANUP
+
AT_SETUP([tunnel - Geneve metadata])
OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
options:remote_ip=1.1.1.1 ofport_request=1 \
--
1.9.3

1334
ovs-nsh/patches/060683.patch Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,151 @@
decapsulation-reencapsulation case.
When Ethernet NSH packets are received and then resent to Ethernet NSH port.
The decapsulation and encapsulation will be implemented. However, tunnel pop
and tunnel push actions are very time-consuming when decapsulation and
encapsulation.
With this feature (options:tun_nodecap=true), tunnel port will parse the input
tunnel packets, but the tunnel header will be kept. And the tunnel header can
be modified by the set field actions. This feature can improve performance.
Signed-off-by: Ricky Li <<A HREF="http://openvswitch.org/mailman/listinfo/dev">ricky.li at intel.com</A>>
Signed-off-by: Mengke Liu <<A HREF="http://openvswitch.org/mailman/listinfo/dev">mengke.liu at intel.com</A>>
---
lib/netdev-vport.c | 43 ++++++++++++++++++++++++++++++++++++++++++-
lib/odp-util.c | 3 ++-
lib/packets.c | 10 +++++++++-
tests/tunnel.at | 21 +++++++++++++++++++++
4 files changed, 74 insertions(+), 3 deletions(-)
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 0a3da8d..038f1e1 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -1865,6 +1865,47 @@ netdev_nsh_pop_header(struct dp_packet *packet)
}
static int
+netdev_nsh_pop_header_spec(struct dp_packet *packet,
+ const struct ovs_action_pop_tnl *data)
+{
+ struct pkt_metadata *md = &packet->md;
+ struct flow_tnl *tnl = &md->tunnel;
+ struct eth_header *eth;
+ struct nshhdr *nsh;
+
+ if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
+
+ pkt_metadata_init_tnl(md);
+ if (ETH_NSH_HLEN > dp_packet_size(packet)) {
+ return EINVAL;
+ }
+
+ eth = (struct eth_header *) dp_packet_data(packet);
+ memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
+ memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
+
+ nsh = (struct nshhdr *) (eth + 1);
+ tnl->nsp = nsh->b.b2 << 8;
+ tnl->nsi = nsh->b.svc_idx;
+ tnl->nshc1 = nsh->c.nshc1;
+ tnl->nshc2 = nsh->c.nshc2;
+ tnl->nshc3 = nsh->c.nshc3;
+ tnl->nshc4 = nsh->c.nshc4;
+ tnl->flags |= FLOW_TNL_F_NSP;
+ tnl->flags |= FLOW_TNL_F_NSI;
+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
+
+ tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_ETHERNET_PRST| NSH_TNL_F_NODECAP;
+ tnl->tun_len = ETH_NSH_HLEN;
+
+ return 0;
+ }
+
+ return EINVAL;
+}
+
+static int
netdev_nsh_build_header(const struct netdev *netdev,
struct ovs_action_push_tnl *data,
const struct flow *tnl_flow)
@@ -2144,7 +2185,7 @@ netdev_vport_tunnel_register(void)
TUNNEL_CLASS("eth_nsh", "nsh_sys", netdev_nsh_build_header,
netdev_nsh_push_header,
netdev_nsh_pop_header,
- NULL),
+ netdev_nsh_pop_header_spec),
};
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
diff --git a/lib/odp-util.c b/lib/odp-util.c
index c2af063..a87b3be 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -4837,7 +4837,8 @@ commit_odp_tunnel_set_action(const struct flow_tnl *tunnel, struct flow_tnl *bas
struct ofpbuf *odp_actions)
{
/* A valid IPV4_TUNNEL must have non-zero ip_dst. */
- if (tunnel->ip_dst) {
+ if (tunnel->ip_dst ||
+ tunnel->nsh_flags & NSH_TNL_F_ETHERNET_PARSED) {
if (!memcmp(tunnel, base, sizeof *tunnel)) {
return;
diff --git a/lib/packets.c b/lib/packets.c
index 7dab4b5..14a19b1 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -934,7 +934,15 @@ packet_set_nsh(struct dp_packet *packet, struct flow_tnl *tun_key)
eth = (struct eth_header *) dp_packet_data(packet);
- if (tun_key->nsh_flags & NSH_TNL_F_VXLAN_PRST) {
+ if (tun_key->nsh_flags & NSH_TNL_F_ETHERNET_PRST) {
+ nsh = (struct nshhdr *) (eth + 1);
+ nsh->b.b2 = tun_key->nsp >> 8;
+ nsh->b.svc_idx = tun_key->nsi;
+ nsh->c.nshc1 = tun_key->nshc1;
+ nsh->c.nshc2 = tun_key->nshc2;
+ nsh->c.nshc3 = tun_key->nshc3;
+ nsh->c.nshc4 = tun_key->nshc4;
+ } else if (tun_key->nsh_flags & NSH_TNL_F_VXLAN_PRST) {
struct ip_header *ip = (struct ip_header *) (eth + 1);
struct udp_header *udp = (struct udp_header *) (ip + 1);
struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
diff --git a/tests/tunnel.at b/tests/tunnel.at
index 19221fb..1bbf5e2 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -765,6 +765,27 @@ AT_CHECK([tail -1 stdout], [0],
OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
AT_CLEANUP
+AT_SETUP([tunnel - ETHERNET NSH tun_nodecap - user space])
+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
+AT_CHECK([ovs-vsctl add-port br0 p1 -- set interface p1 type=eth_nsh options:tun_nodecap=true options:remote_mac=00:00:00:11:11:22 \
+options:out_nsp=flow options:out_nsi=flow options:in_nshc1=flow options:in_nshc2=flow options:in_nshc3=flow options:in_nshc4=flow ofport_request=2], [0])
+
+AT_CHECK([ovs-ofctl add-flow br0 "priority=16, in_port=1, action=local"])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
+ br0 65534/100: (dummy)
+ p0 1/1: (dummy)
+ p1 2/2: (eth_nsh: in_nshc1=flow, in_nshc2=flow, in_nshc3=flow, in_nshc4=flow, out_nsi=flow, out_nsp=flow, remote_mac=00:00:00:11:11:22, tun_nodecap=true)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=00:00:00:11:11:22,dst=50:54:00:00:00:07),eth_type(0x894f)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: tnl_pop_spec(tnl_port(2),pop_type=2)
+])
+
+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
+AT_CLEANUP
+
AT_SETUP([tunnel - Geneve metadata])
OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
options:remote_ip=1.1.1.1 ofport_request=1 \
--
1.9.3

View File

@ -0,0 +1,675 @@
VxLAN-GPE NSH packet.
With this feature (options:nsh-convert=true),when Ethernet-NSH packet (Outer
MAC header + original packet) are received by Ethernet-NSH port, the vport
will remove Outer MAC header, and then modify and push the
outer MAC header + Outer IP header + UDP header + VxLAN-GPE. Then the packet
with Ethernet+NSH format is converted to VxLAN-GPE NSH packet.
Signed-off-by: Ricky Li <<A HREF="http://openvswitch.org/mailman/listinfo/dev">ricky.li at intel.com</A>>
Signed-off-by: Mengke Liu <<A HREF="http://openvswitch.org/mailman/listinfo/dev">mengke.liu at intel.com</A>>
---
datapath/linux/compat/include/linux/openvswitch.h | 3 +-
lib/netdev-vport.c | 128 +++++++++++++++++-----
lib/odp-util.c | 121 +++++++++++++++++++-
ofproto/ofproto-dpif-xlate.c | 113 ++++++++++++++++++-
ofproto/tunnel.c | 75 +++++++++++++
ofproto/tunnel.h | 7 ++
tests/tunnel.at | 29 +++++
7 files changed, 440 insertions(+), 36 deletions(-)
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 045a1f4..916aeae 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -651,7 +651,8 @@ struct ovs_action_push_tnl {
enum ovs_pop_spec_action_type {
OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH,
- OVS_POP_SPEC_ACTION_NO_DECAP = 2,
+ OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH,
+ OVS_POP_SPEC_ACTION_NO_DECAP,
};
/*
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 038f1e1..6142935 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -167,7 +167,8 @@ netdev_vport_needs_dst_port(const struct netdev *dev)
return (class->get_config == get_tunnel_config &&
(!strcmp("geneve", type) || !strcmp("vxlan", type) ||
- !strcmp("lisp", type) || !strcmp("stt", type)) );
+ !strcmp("lisp", type) || !strcmp("stt", type) ||
+ !strcmp("eth_nsh", type)) );
}
const char *
@@ -890,7 +891,8 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
(!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
(!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
- (!strcmp("stt", type) && dst_port != STT_DST_PORT)) {
+ (!strcmp("stt", type) && dst_port != STT_DST_PORT) ||
+ (!strcmp("eth_nsh", type) && tnl_cfg.nsh_convert)) {
smap_add_format(args, "dst_port", "%d", dst_port);
}
}
@@ -1864,42 +1866,116 @@ netdev_nsh_pop_header(struct dp_packet *packet)
return 0;
}
+
static int
-netdev_nsh_pop_header_spec(struct dp_packet *packet,
- const struct ovs_action_pop_tnl *data)
+eth_nsh_extract_md_no_decap(struct dp_packet *packet)
{
struct pkt_metadata *md = &packet->md;
struct flow_tnl *tnl = &md->tunnel;
struct eth_header *eth;
struct nshhdr *nsh;
- if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
+ pkt_metadata_init_tnl(md);
+ if (ETH_NSH_HLEN > dp_packet_size(packet)) {
+ return EINVAL;
+ }
- pkt_metadata_init_tnl(md);
- if (ETH_NSH_HLEN > dp_packet_size(packet)) {
- return EINVAL;
- }
+ eth = (struct eth_header *) dp_packet_data(packet);
+ memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
+ memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
+
+ nsh = (struct nshhdr *) (eth + 1);
+ tnl->nsp = nsh->b.b2 << 8;
+ tnl->nsi = nsh->b.svc_idx;
+ tnl->nshc1 = nsh->c.nshc1;
+ tnl->nshc2 = nsh->c.nshc2;
+ tnl->nshc3 = nsh->c.nshc3;
+ tnl->nshc4 = nsh->c.nshc4;
+ tnl->flags |= FLOW_TNL_F_NSP;
+ tnl->flags |= FLOW_TNL_F_NSI;
+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
+ FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
+ tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_ETHERNET_PRST| NSH_TNL_F_NODECAP;
+ tnl->tun_len = ETH_NSH_HLEN;
+
+ return 0;
+}
+
+static int
+eth_nsh_extract_md_convert_to_vxlan_gpe_nsh(struct dp_packet *packet,
+ const struct ovs_action_pop_tnl *data)
+{
+ struct pkt_metadata *md = &packet->md;
+ struct flow_tnl *tnl = &md->tunnel;
+ struct eth_header *eth;
+ struct ip_header *ip;
+ struct udp_header *udp;
+ struct nshhdr *nsh;
+
+ pkt_metadata_init_tnl(md);
+ if (ETH_NSH_HLEN > dp_packet_size(packet)) {
+ return EINVAL;
+ }
+
+ eth = (struct eth_header *) dp_packet_data(packet);
+ memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
+ memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
- eth = (struct eth_header *) dp_packet_data(packet);
- memcpy(tnl->eth_dst.ea, eth->eth_dst.ea, ETH_ADDR_LEN);
- memcpy(tnl->eth_src.ea, eth->eth_src.ea, ETH_ADDR_LEN);
-
- nsh = (struct nshhdr *) (eth + 1);
- tnl->nsp = nsh->b.b2 << 8;
- tnl->nsi = nsh->b.svc_idx;
- tnl->nshc1 = nsh->c.nshc1;
- tnl->nshc2 = nsh->c.nshc2;
- tnl->nshc3 = nsh->c.nshc3;
- tnl->nshc4 = nsh->c.nshc4;
- tnl->flags |= FLOW_TNL_F_NSP;
- tnl->flags |= FLOW_TNL_F_NSI;
- tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
+ nsh = (struct nshhdr *) (eth + 1);
+ tnl->nsp = nsh->b.b2 << 8;
+ tnl->nsi = nsh->b.svc_idx;
+ tnl->nshc1 = nsh->c.nshc1;
+ tnl->nshc2 = nsh->c.nshc2;
+ tnl->nshc3 = nsh->c.nshc3;
+ tnl->nshc4 = nsh->c.nshc4;
+
+ tnl->flags |= FLOW_TNL_F_NSP;
+ tnl->flags |= FLOW_TNL_F_NSI;
+ tnl->flags |= FLOW_TNL_F_NSH_C1 | FLOW_TNL_F_NSH_C2 | \
FLOW_TNL_F_NSH_C3 | FLOW_TNL_F_NSH_C4;
- tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_ETHERNET_PRST| NSH_TNL_F_NODECAP;
- tnl->tun_len = ETH_NSH_HLEN;
+ tnl->nsh_flags = NSH_TNL_F_ETHERNET_PARSED | NSH_TNL_F_VXLAN_PRST;
- return 0;
+ dp_packet_reset_packet(packet, ETH_NSH_HLEN - sizeof (struct nshhdr));
+ eth = (struct eth_header *) dp_packet_push_uninit(packet, data->header_len);
+ memcpy(eth, data->header, data->header_len);
+
+
+ /* set IP length, csum */
+ int ip_tot_size = dp_packet_size(packet) - sizeof (struct eth_header);
+ ip = ip_hdr(eth);
+ ip->ip_tot_len = htons(ip_tot_size);
+ ip->ip_csum = recalc_csum16(ip->ip_csum, 0, ip->ip_tot_len);
+
+ /* set udp src port */
+ udp = (struct udp_header *) (ip + 1);
+ udp->udp_src = get_src_port(packet);
+ udp->udp_len = htons(ip_tot_size - sizeof (struct ip_header));
+
+ /* udp_csum is zero */
+ if (udp->udp_csum) {
+ uint32_t csum = packet_csum_pseudoheader(ip);
+
+ csum = csum_continue(csum, udp,
+ ip_tot_size - sizeof (struct ip_header));
+ udp->udp_csum = csum_finish(csum);
+
+ if (!udp->udp_csum) {
+ udp->udp_csum = htons(0xffff);
+ }
+ }
+
+ return 0;
+}
+
+static int
+netdev_nsh_pop_header_spec(struct dp_packet *packet,
+ const struct ovs_action_pop_tnl *data)
+{
+ if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH) {
+ return eth_nsh_extract_md_convert_to_vxlan_gpe_nsh(packet, data);
+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_NO_DECAP) {
+ return eth_nsh_extract_md_no_decap(packet);
}
return EINVAL;
diff --git a/lib/odp-util.c b/lib/odp-util.c
index a87b3be..183844f 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -515,7 +515,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
gnh->oam ? "oam," : "",
gnh->critical ? "crit," : "",
ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
-
+
if (gnh->opt_len) {
ds_put_cstr(ds, ",options(");
format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
@@ -579,10 +579,11 @@ static void
format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data)
{
const struct eth_header *eth;
+ const struct ip_header *ip;
+ const void *l3;
eth = (const struct eth_header *)data->header;
if (data->tnl_type == OVS_VPORT_TYPE_NSH) {
- const struct nshhdr *nsh = (const struct nshhdr *) (eth + 1);
/* Ethernet */
ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
@@ -591,7 +592,37 @@ format_odp_tnl_pop_header(struct ds *ds, struct ovs_action_pop_tnl *data)
ds_put_format(ds, ",src=");
ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
ds_put_format(ds, ",dl_type=0x%04"PRIx16")", ntohs(eth->eth_type));
- ds_put_format(ds, "),");
+ ds_put_format(ds, "),");
+ } else if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
+ l3 = eth + 1;
+ ip = (const struct ip_header *)l3;
+
+ /* Ethernet */
+ ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
+ data->header_len, data->tnl_type);
+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
+ ds_put_format(ds, ",src=");
+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
+ ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));
+
+ /* IPv4 */
+ ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
+ ",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
+ IP_ARGS(get_16aligned_be32(&ip->ip_src)),
+ IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
+ ip->ip_proto, ip->ip_tos,
+ ip->ip_ttl,
+ ip->ip_frag_off);
+ if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
+ const struct vxlanhdr *vxh;
+
+ vxh = format_udp_tnl_push_header(ds, ip);
+
+ ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
+ ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
+ }
+ ds_put_format(ds, "),");
}
}
@@ -615,9 +646,10 @@ format_odp_tnl_pop_spec_action(struct ds *ds, const struct nlattr *attr)
data = (struct ovs_action_pop_tnl *) nl_attr_get(attr);
ds_put_format(ds, "tnl_pop_spec(tnl_port(%"PRIu32"),", data->tnl_port);
- if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH) {
+ if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH ||
+ data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH) {
ds_put_format(ds, "pop_type=%"PRIu16",",
- OVS_POP_SPEC_ACTION_CONVERT_TO_ETH_NSH);
+ data->pop_type);
format_odp_tnl_pop_header(ds, data);
ds_put_format(ds, "out_port(%"PRIu32"))", data->out_port);
@@ -1174,6 +1206,85 @@ ovs_parse_tnl_pop_spec(const char *s, struct ovs_action_pop_tnl *data)
if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
return -EINVAL;
}
+ } else if (data->pop_type == OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH) {
+ struct eth_header *eth;
+ struct ip_header *ip;
+ struct udp_header *udp;
+ uint16_t dl_type, udp_src, udp_dst, csum;
+ ovs_be32 sip, dip;
+ uint32_t tnl_type = 0, header_len = 0;
+ void *l3, *l4;
+ int n = 0;
+
+ eth = (struct eth_header *) data->header;
+ l3 = (data->header + sizeof *eth);
+ l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
+ ip = (struct ip_header *) l3;
+ if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
+ "eth(dst="ETH_ADDR_SCAN_FMT",",
+ &data->header_len,
+ &data->tnl_type,
+ ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
+ return -EINVAL;
+ }
+
+ if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
+ ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
+ return -EINVAL;
+ }
+ if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
+ return -EINVAL;
+ }
+ eth->eth_type = htons(dl_type);
+
+ /* IPv4 */
+ if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
+ ",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
+ IP_SCAN_ARGS(&sip),
+ IP_SCAN_ARGS(&dip),
+ &ip->ip_proto, &ip->ip_tos,
+ &ip->ip_ttl, &ip->ip_frag_off)) {
+ return -EINVAL;
+ }
+ put_16aligned_be32(&ip->ip_src, sip);
+ put_16aligned_be32(&ip->ip_dst, dip);
+
+ /* Tunnel header */
+ udp = (struct udp_header *) l4;
+ if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
+ &udp_src, &udp_dst, &csum)) {
+ uint32_t vx_flags, vni;
+
+ udp->udp_src = htons(udp_src);
+ udp->udp_dst = htons(udp_dst);
+ udp->udp_len = 0;
+ udp->udp_csum = htons(csum);
+
+ if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
+ &vx_flags, &vni)) {
+ struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
+
+ put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
+ put_16aligned_be32(&vxh->vx_vni, htonl(vni << 8));
+ tnl_type = OVS_VPORT_TYPE_VXLAN;
+ header_len = sizeof *eth + sizeof *ip +
+ sizeof *udp + sizeof *vxh;
+ } else {
+ return -EINVAL;
+ }
+ /* check tunnel meta data. */
+ if (data->tnl_type != tnl_type) {
+ return -EINVAL;
+ }
+ if (data->header_len != header_len) {
+ return -EINVAL;
+ }
+
+ /* Out port */
+ if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
+ return -EINVAL;
+ }
+ }
} else {
return -EINVAL;
}
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 90b5a95..1578a0c 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2653,7 +2653,7 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
}
static int
-tnl_route_lookup_flow(const struct flow *oflow,
+tnl_route_lookup_flow__(ovs_be32 ip_dst,
ovs_be32 *ip, struct xport **out_port)
{
char out_dev[IFNAMSIZ];
@@ -2661,14 +2661,14 @@ tnl_route_lookup_flow(const struct flow *oflow,
struct xlate_cfg *xcfg;
ovs_be32 gw;
- if (!ovs_router_lookup(oflow->tunnel.ip_dst, out_dev, &gw)) {
+ if (!ovs_router_lookup(ip_dst, out_dev, &gw)) {
return -ENOENT;
}
if (gw) {
*ip = gw;
} else {
- *ip = oflow->tunnel.ip_dst;
+ *ip = ip_dst;
}
xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
@@ -2690,6 +2690,12 @@ tnl_route_lookup_flow(const struct flow *oflow,
}
static int
+tnl_route_lookup_flow(const struct flow *oflow,
+ ovs_be32 *ip, struct xport **out_port){
+ return tnl_route_lookup_flow__(oflow->tunnel.ip_dst, ip, out_port);
+}
+
+static int
tnl_outdev_lookup_mac(const struct eth_addr *mac,
struct xport **out_port)
{
@@ -2818,6 +2824,100 @@ build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport,
}
static int
+build_eth_nsh_tunnel_pop(struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct flow *flow)
+{
+ const struct netdev_tunnel_config * cfg;
+
+ cfg = tnl_port_cfg(tunnel_odp_port, flow);
+
+ if (cfg) {
+ if (cfg->nsh_convert && (ntohs(cfg->dst_port) == VXGPE_DST_PORT)) {
+
+ struct ovs_action_pop_tnl tnl_pop_data;
+ struct xport *out_dev = NULL;
+ ovs_be32 s_ip, d_ip = 0;
+ struct eth_addr smac;
+ struct eth_addr dmac;
+ int err;
+
+ err = tnl_route_lookup_flow__(cfg->ip_dst, &d_ip, &out_dev);
+ if (err) {
+ xlate_report(ctx, "native tunnel routing failed");
+ return err;
+ }
+ xlate_report(ctx, "tunneling to "IP_FMT" via %s",
+ IP_ARGS(d_ip), netdev_get_name(out_dev->netdev));
+
+ /* Use mac addr of bridge port of the peer. */
+ err = netdev_get_etheraddr(out_dev->netdev, &smac);
+ if (err) {
+ xlate_report(ctx, "tunnel output device lacks Ethernet address");
+ return err;
+ }
+
+ err = netdev_get_in4(out_dev->netdev, (struct in_addr *) &s_ip, NULL);
+ if (err) {
+ xlate_report(ctx, "tunnel output device lacks IPv4 address");
+ return err;
+ }
+
+ err = tnl_arp_lookup(out_dev->xbridge->name, d_ip, &dmac);
+ if (err) {
+ xlate_report(ctx, "ARP cache miss for "IP_FMT" on bridge %s, "
+ "sending ARP request",
+ IP_ARGS(d_ip), out_dev->xbridge->name);
+ tnl_send_arp_request(ctx, out_dev, smac, s_ip, d_ip);
+ return err;
+ }
+ if (ctx->xin->xcache) {
+ struct xc_entry *entry;
+
+ entry = xlate_cache_add_entry(ctx->xin->xcache, XC_TNL_ARP);
+ ovs_strlcpy(entry->u.tnl_arp_cache.br_name, out_dev->xbridge->name,
+ sizeof entry->u.tnl_arp_cache.br_name);
+ entry->u.tnl_arp_cache.d_ip = d_ip;
+ }
+
+ xlate_report(ctx, "tunneling from "ETH_ADDR_FMT" "IP_FMT
+ " to "ETH_ADDR_FMT" "IP_FMT,
+ ETH_ADDR_ARGS(smac), IP_ARGS(s_ip),
+ ETH_ADDR_ARGS(dmac), IP_ARGS(d_ip));
+ err = tnl_port_build_header_odport_popspec(tunnel_odp_port, cfg,
+ dmac, smac, s_ip, &tnl_pop_data);
+ if (err) {
+ return err;
+ }
+ tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
+ tnl_pop_data.out_port = odp_to_u32(out_dev->odp_port);
+ tnl_pop_data.tnl_type = OVS_VPORT_TYPE_VXLAN;
+ tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_CONVERT_TO_VXLAN_GPE_NSH;
+ odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
+
+ } else if (cfg->tun_nodecap) {
+ struct ovs_action_pop_tnl tnl_pop_data;
+ memset(&tnl_pop_data, 0, sizeof tnl_pop_data);
+
+ tnl_pop_data.tnl_port = odp_to_u32(tunnel_odp_port);
+ tnl_pop_data.pop_type = OVS_POP_SPEC_ACTION_NO_DECAP;
+ odp_put_tnl_pop_spec_action(ctx->odp_actions, &tnl_pop_data);
+
+ } else {
+ nl_msg_put_odp_port(ctx->odp_actions,
+ OVS_ACTION_ATTR_TUNNEL_POP,
+ tunnel_odp_port);
+ }
+
+ } else {
+ nl_msg_put_odp_port(ctx->odp_actions,
+ OVS_ACTION_ATTR_TUNNEL_POP,
+ tunnel_odp_port);
+ }
+
+ return 0;
+}
+
+
+static int
build_tunnel_pop(const struct xlate_ctx *ctx, odp_port_t tunnel_odp_port, struct flow *flow)
{
const struct netdev_tunnel_config * cfg;
@@ -3185,7 +3285,12 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
if (odp_tnl_port != ODPP_NONE &&
!(flow->tunnel.nsh_flags & NSH_TNL_F_NODECAP)) {
flow_tnl = flow->tunnel;
- build_tunnel_pop(ctx, odp_tnl_port, flow);
+ if(flow->dl_type == htons(ETH_TYPE_NSH)) {
+ build_eth_nsh_tunnel_pop(ctx, odp_tnl_port, flow);
+ }
+ else {
+ build_tunnel_pop(ctx, odp_tnl_port, flow);
+ }
flow->tunnel = flow_tnl;
} else {
/* Tunnel push-pop action is not compatible with
diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
index cc0c91a..e279e92 100644
--- a/ofproto/tunnel.c
+++ b/ofproto/tunnel.c
@@ -47,6 +47,13 @@ VLOG_DEFINE_THIS_MODULE(tunnel);
#define ETH_NSH_HLEN (sizeof(struct eth_header) + \
sizeof(struct nshhdr))
+
+#define VXNSH_HLEN (sizeof(struct eth_header) + \
+ sizeof(struct ip_header) + \
+ sizeof(struct udp_header) + \
+ sizeof(struct vxgpehdr) + \
+ sizeof(struct nshhdr))
+
struct tnl_match {
ovs_be64 in_key;
ovs_be32 in_nsp;
@@ -1044,6 +1051,74 @@ tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock)
}
int
+tnl_port_build_header_odport_popspec(const odp_port_t odp_port,
+ const struct netdev_tunnel_config *cfg,
+ const struct eth_addr dmac,
+ const struct eth_addr smac,
+ ovs_be32 ip_src, struct ovs_action_pop_tnl *data)
+{
+ struct tnl_port *tnl_port;
+ struct eth_header *eth;
+ struct ip_header *ip;
+ struct udp_header *udp;
+ void *l3;
+
+ fat_rwlock_rdlock(&rwlock);
+ tnl_port = tnl_find_odp_port(odp_port);
+ ovs_assert(tnl_port);
+
+ /* Build Ethernet headers. */
+ memset(data->header, 0, sizeof data->header);
+
+ eth = (struct eth_header *)data->header;
+ eth->eth_dst = dmac;
+ eth->eth_src = smac;
+ eth->eth_type = htons(ETH_TYPE_IP);
+
+ l3 = (eth + 1);
+ ip = (struct ip_header *) l3;
+
+ /* Build IP header */
+ ip->ip_ihl_ver = IP_IHL_VER(5, 4);
+ ip->ip_tos = cfg->tos;
+ ip->ip_ttl = cfg->ttl;
+ ip->ip_frag_off = cfg->dont_fragment ? htons(IP_DF) : 0;
+ put_16aligned_be32(&ip->ip_src, ip_src);
+ put_16aligned_be32(&ip->ip_dst, cfg->ip_dst);
+ ip->ip_proto = IPPROTO_UDP;
+ ip->ip_csum = csum(ip, sizeof *ip);
+
+ /* Build UDP header */
+ udp = (struct udp_header *) (ip + 1);
+ udp->udp_dst = cfg->dst_port;
+
+ if (cfg->csum) {
+ /* Write a value in now to mark that we should compute the checksum
+ * later. 0xffff is handy because it is transparent to the
+ * calculation. */
+ udp->udp_csum = htons(0xffff);
+ }
+ /* Build VxLAN-GPE header */
+ if (ntohs(udp->udp_dst) == VXGPE_DST_PORT){
+ struct vxgpehdr *vxg = (struct vxgpehdr *) (udp + 1);
+
+ memset(vxg, 0, sizeof *vxg);
+ vxg->i = 0x01;
+ vxg->p = 0x01;
+ vxg->ver = 0x01;
+ vxg->proto = VXG_P_NSH;
+ put_16aligned_be32(&vxg->vx_vni, htonl(ntohll(cfg->out_key) << 8));
+
+ }
+
+ data->header_len = VXNSH_HLEN - sizeof (struct nshhdr);
+ data->tnl_type = OVS_VPORT_TYPE_VXLAN;
+
+ fat_rwlock_unlock(&rwlock);
+ return 0;
+}
+
+int
tnl_port_build_header(const struct ofport_dpif *ofport,
const struct flow *tnl_flow,
const struct eth_addr dmac,
diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h
index d771476..9f2f11d 100644
--- a/ofproto/tunnel.h
+++ b/ofproto/tunnel.h
@@ -55,6 +55,13 @@ tnl_port_should_receive(const struct flow *flow)
memcmp(flow->tunnel.eth_dst.ea, &eth_addr_zero, ETH_ADDR_LEN));
}
+int
+tnl_port_build_header_odport_popspec(const odp_port_t odp_port,
+ const struct netdev_tunnel_config *cfg,
+ const struct eth_addr dmac,
+ const struct eth_addr smac,
+ ovs_be32 ip_src, struct ovs_action_pop_tnl *data);
+
int tnl_port_build_header(const struct ofport_dpif *ofport,
const struct flow *tnl_flow,
const struct eth_addr dmac,
diff --git a/tests/tunnel.at b/tests/tunnel.at
index 1bbf5e2..c740966 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -786,6 +786,35 @@ AT_CHECK([tail -1 stdout], [0],
OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
AT_CLEANUP
+AT_SETUP([tunnel - ETHERNET - nsh_convert from Ethernet NSH to VXLAN-GPE NSH - user space])
+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
+AT_CHECK([ovs-vsctl add-port br0 p1 -- set interface p1 type=eth_nsh options:remote_mac=00:00:00:11:11:22 options:nsh_convert=true \
+options:nsi=flow options:nsp=flow options:nshc1=flow options:in_key=flow options:remote_ip=1.1.1.1 options:dst_port=4790 ofport_request=2], [0])
+
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 2.2.2.22/24], [0], [OK
+])
+
+AT_CHECK([
+ovs-appctl ovs/route/add 1.1.1.1/24 br0
+ovs-appctl tnl/arp/set br0 1.1.1.1 68:05:ca:30:6b:d1
+],[0],[stdout])
+
+AT_CHECK([ovs-ofctl add-flow br0 "priority=16, in_port=1, action=local"])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
+ br0 65534/100: (dummy)
+ p0 1/1: (dummy)
+ p1 2/4790: (eth_nsh: dst_port=4790, in_key=flow, nsh_convert=true, nshc1=flow, nsi=flow, nsp=flow, remote_ip=1.1.1.1, remote_mac=00:00:00:11:11:22)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=00:00:00:11:11:22,dst=50:54:00:00:00:07),eth_type(0x894f)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: tnl_pop_spec(tnl_port(4790),pop_type=1,header(size=50,type=4,eth(dst=68:05:ca:30:6b:d1,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=2.2.2.22,dst=1.1.1.1,proto=17,tos=0,ttl=64,frag=0x40),udp(src=0,dst=4790,csum=0x0),vxlan(flags=0xc400004,vni=0x0)),out_port(100))
+])
+
+OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
+AT_CLEANUP
+
AT_SETUP([tunnel - Geneve metadata])
OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \
options:remote_ip=1.1.1.1 ofport_request=1 \
--
1.9.3

View File

@ -1,8 +0,0 @@
https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/dpdk-2.1.0-6.el6.x86_64.rpm?raw=true
https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/dpdk-devel-2.1.0-6.el6.x86_64.rpm?raw=true
https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/dpdk-tools-2.1.0-6.el6.x86_64.rpm?raw=true
https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/kmod-openvswitch-2.4.90-1.el6.x86_64.rpm?raw=true
https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/kmod-openvswitch-2.4.90-2.el6.x86_64.rpm?raw=true
https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/openvswitch-2.4.90-1.el6.x86_64.rpm?raw=true
https://github.com/lingyu1/fuel-rpm/blob/master/centos/6.5/x86_64/Packages/openvswitch-2.4.90-1.x86_64.rpm?raw=true

View File

@ -1,3 +0,0 @@
https://github.com/lingyu1/fuel-rpm/blob/master/ubuntu/14.04.1/x86_64/Packages/openvswitch-common_2.4.90-1_amd64.deb?raw=true
https://github.com/lingyu1/fuel-rpm/blob/master/ubuntu/14.04.1/x86_64/Packages/openvswitch-datapath-dkms_2.4.90-1_all.deb?raw=true
https://github.com/lingyu1/fuel-rpm/blob/master/ubuntu/14.04.1/x86_64/Packages/openvswitch-switch_2.4.90-1_amd64.deb?raw=true

View File

@ -5,36 +5,23 @@
# The script should return 0 if there were no errors.
set -eux
BUILD_FOR=${BUILD_FOR:-centos ubuntu}
BUILD_FOR=${BUILD_FOR:-ubuntu}
DIR="$(dirname `readlink -f $0`)"
INCLUDE_DEPENDENCIES=${INCLUDE_DEPENDENCIES:-true}
function download_dependencies {
if [ "$INCLUDE_DEPENDENCIES" = true ]
then
wget --content-disposition -N -i "${DIR}/ovs_package/${1}/dependencies.txt"
fi
}
function build_pkg {
case $1 in
centos)
pushd "${DIR}/repositories/${1}/"
download_dependencies ${1}
popd
;;
ubuntu)
pushd "${DIR}/repositories/${1}/"
download_dependencies ${1}
popd
cd ${DIR}/ovs-nsh
sudo docker build -t ovs-nsh .
container_id=`sudo docker run -d ovs-nsh`
sudo docker cp $container_id:/openvswitch/openvswitch-common_2.4.90-1_amd64.deb ${DIR}/repositories/ubuntu/
sudo docker cp $container_id:/openvswitch/openvswitch-datapath-dkms_2.4.90-1_all.deb ${DIR}/repositories/ubuntu/
sudo docker cp $container_id:/openvswitch/openvswitch-switch_2.4.90-1_amd64.deb ${DIR}/repositories/ubuntu/
;;
*) echo "Not supported system"; exit 1;;
esac
}
for system in $BUILD_FOR
do
build_pkg $system