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:
parent
373740ac0a
commit
7590019023
11
README.md
11
README.md
|
@ -14,7 +14,7 @@ Requirements
|
|||
|
||||
| Requirement | Version/Comment |
|
||||
|----------------------------------|-----------------|
|
||||
| Mirantis OpenStack compatibility | 6.1 |
|
||||
| Mirantis OpenStack compatibility | 7.0 |
|
||||
|
||||
Recommendations
|
||||
---------------
|
||||
|
@ -60,7 +60,7 @@ Openvswitch plugin installation
|
|||
|
||||
id | name | version | package_version
|
||||
---|-----------------|---------|----------------
|
||||
1 | fuel-plugin-ovs | 0.5.0 | 2.0.0
|
||||
1 | fuel-plugin-ovs | 0.5.1 | 3.0.0
|
||||
|
||||
8. Plugin is ready to use and can be enabled on the Settings tab of the Fuel web UI.
|
||||
|
||||
|
@ -102,13 +102,6 @@ Known issues
|
|||
|
||||
None.
|
||||
|
||||
Release Notes
|
||||
-------------
|
||||
|
||||
**0.5.0**
|
||||
|
||||
* Initial release of the plugin. This is a beta version.
|
||||
|
||||
|
||||
Development
|
||||
===========
|
||||
|
|
|
@ -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' {
|
||||
}
|
||||
|
|
|
@ -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' {
|
||||
}
|
||||
|
|
|
@ -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' {
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
@ -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, ð_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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue