[ovs-dev] [PATCH v5 6/6] userspace: add vxlan gpe support to vport

Zoltán Balogh zoltan.balogh at ericsson.com
Sat May 6 15:50:48 UTC 2017


From: Georg Schmuecking <georg.schmuecking at ericsson.com>

This patch is based on the "datapath: enable vxlangpe creation in compat mode"
from Yi Yang. It introduces an extension option "gpe" to the vxlan port in the
netdev-dpdk datapath. Furthermore it introduces a new protocol header file
vxlangpe.h in which the vxlan gpe protocoll is described. In the vxlan specific
methods the different packet are introduced and handled.

Added VXLAN GPE tunnel push test.

Signed-off-by: Yi Yang <yi.y.yang at intel.com>
Signed-off-by: Georg Schmuecking <georg.schmuecking at ericsson.com>
---
 datapath/linux/compat/include/linux/openvswitch.h |  1 +
 include/openvswitch/automake.mk                   |  4 +-
 include/openvswitch/vxlangpe.h                    | 76 +++++++++++++++++++++++
 lib/netdev-native-tnl.c                           | 61 ++++++++++++++++--
 lib/netdev-vport.c                                | 18 +++++-
 tests/tunnel-push-pop.at                          | 10 +++
 6 files changed, 160 insertions(+), 10 deletions(-)
 create mode 100755 include/openvswitch/vxlangpe.h

diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 7990638..2ae1797 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -291,6 +291,7 @@ enum ovs_vport_attr {
 enum {
 	OVS_VXLAN_EXT_UNSPEC,
 	OVS_VXLAN_EXT_GBP,      /* Flag or __u32 */
+	OVS_VXLAN_EXT_GPE = 8,  /* Flag or __u32 */
 	__OVS_VXLAN_EXT_MAX,
 };
 
diff --git a/include/openvswitch/automake.mk b/include/openvswitch/automake.mk
index c0e276f..c125f1e 100644
--- a/include/openvswitch/automake.mk
+++ b/include/openvswitch/automake.mk
@@ -29,5 +29,5 @@ openvswitchinclude_HEADERS = \
 	include/openvswitch/uuid.h \
 	include/openvswitch/version.h \
 	include/openvswitch/vconn.h \
-	include/openvswitch/vlog.h
-
+	include/openvswitch/vlog.h \
+	include/openvswitch/vxlangpe.h
diff --git a/include/openvswitch/vxlangpe.h b/include/openvswitch/vxlangpe.h
new file mode 100755
index 0000000..4c18d90
--- /dev/null
+++ b/include/openvswitch/vxlangpe.h
@@ -0,0 +1,76 @@
+#ifndef __OPENVSWITCH_VXLANGPE_H
+#define __OPENVSWITCH_VXLANGPE_H 1
+
+#include "openvswitch/types.h"
+
+#define u8 uint8_t
+#define u32 uint8_t
+#define __be32 ovs_be32
+
+/*
+ * VXLAN Generic Protocol Extension (VXLAN_F_GPE):
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|R|Ver|I|P|R|O|       Reserved                |Next Protocol  |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                VXLAN Network Identifier (VNI) |   Reserved    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Ver = Version. Indicates VXLAN GPE protocol version.
+ *
+ * P = Next Protocol Bit. The P bit is set to indicate that the
+ *     Next Protocol field is present.
+ *
+ * O = OAM Flag Bit. The O bit is set to indicate that the packet
+ *     is an OAM packet.
+ *
+ * Next Protocol = This 8 bit field indicates the protocol header
+ * immediately following the VXLAN GPE header.
+ *
+ * https://tools.ietf.org/html/draft-ietf-nvo3-vxlan-gpe-01
+ */
+
+struct vxlanhdr_gpe {
+#ifdef WORDS_BIGENDIAN
+       u8      reserved_flags2:2,
+               version:2,
+               instance_applied:1,
+               np_applied:1,
+               reserved_flags1:1,
+               oam_flag:1;
+#else
+       u8      oam_flag:1,
+               reserved_flags1:1,
+               np_applied:1,
+               instance_applied:1,
+               version:2,
+               reserved_flags2:2;
+#endif
+       u8      reserved_flags3;
+       u8      reserved_flags4;
+       u8      next_protocol;
+       __be32  vx_vni;
+};
+
+/* VXLAN-GPE header flags. */
+#define VXLAN_HF_VER   ((1U <<29) | (1U <<28))
+#define VXLAN_HF_NP    (1U <<26)
+#define VXLAN_HF_OAM   (1U <<24)
+
+#define VXLAN_GPE_USED_BITS (VXLAN_HF_VER | VXLAN_HF_NP | VXLAN_HF_OAM | \
+                            0xff)
+
+/* VXLAN-GPE header Next Protocol. */
+#define VXLAN_GPE_NP_IPV4      0x01
+#define VXLAN_GPE_NP_IPV6      0x02
+#define VXLAN_GPE_NP_ETHERNET  0x03
+#define VXLAN_GPE_NP_NSH       0x04
+
+struct vxlan_metadata {
+       u32             gbp;
+       u32             gpe;
+};
+
+#define VXLAN_F_GPE                    0x4000
+#define VXLAN_HF_GPE 0x04000000
+
+#endif /* __OPENVSWITCH_VXLANGPE_H */
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 4590e25..2d947c2 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -44,6 +44,7 @@
 #include "unaligned.h"
 #include "unixctl.h"
 #include "openvswitch/vlog.h"
+#include "openvswitch/vxlangpe.h"
 
 VLOG_DEFINE_THIS_MODULE(native_tnl);
 static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5);
@@ -497,6 +498,8 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
     struct flow_tnl *tnl = &md->tunnel;
     struct vxlanhdr *vxh;
     unsigned int hlen;
+    ovs_be32 vx_flags;
+    enum packet_type next_pt = PT_ETH;
 
     pkt_metadata_init_tnl(md);
     if (VXLAN_HLEN > dp_packet_l4_size(packet)) {
@@ -508,18 +511,43 @@ netdev_vxlan_pop_header(struct dp_packet *packet)
         goto err;
     }
 
-    if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) ||
+    vx_flags = get_16aligned_be32(&vxh->vx_flags);
+    if (vx_flags & htonl(VXLAN_HF_GPE)) {
+        vx_flags &= htonl(~VXLAN_GPE_USED_BITS);
+        struct vxlanhdr_gpe *gpe = (struct vxlanhdr_gpe *)vxh;
+        /* Drop the OAM packets */
+        if (gpe->oam_flag)
+            goto err;
+        switch (gpe->next_protocol) {
+            case VXLAN_GPE_NP_IPV4:
+                next_pt = PT_IPV4;
+                break;
+            case VXLAN_GPE_NP_IPV6:
+                next_pt = PT_IPV6;
+                break;
+            case VXLAN_GPE_NP_ETHERNET:
+                next_pt = PT_ETH;
+                break;
+            default:
+                goto err;
+        }
+	}
+
+    if (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(vx_flags),
                      ntohl(get_16aligned_be32(&vxh->vx_vni)));
         goto err;
     }
     tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
     tnl->flags |= FLOW_TNL_F_KEY;
 
-    packet->packet_type = htonl(PT_ETH);
+    packet->packet_type = htonl(next_pt);
     dp_packet_reset_packet(packet, hlen + VXLAN_HLEN);
+    if (next_pt != PT_ETH) {
+        packet->l3_ofs = 0;
+    }
 
     return packet;
 err:
@@ -540,10 +568,33 @@ netdev_vxlan_build_header(const struct netdev *netdev,
     ovs_mutex_lock(&dev->mutex);
     tnl_cfg = &dev->tnl_cfg;
 
+
     vxh = udp_build_header(tnl_cfg, data, params);
 
-    put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
-    put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));
+    if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
+        struct vxlanhdr_gpe *gpe;
+        gpe = (struct vxlanhdr_gpe *)vxh;
+        put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS | VXLAN_HF_GPE));
+        put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));
+        if (tnl_cfg->is_layer3) {
+            switch (ntohs(params->flow->dl_type)) {
+                case ETH_TYPE_IP:
+                    gpe->next_protocol = VXLAN_GPE_NP_IPV4;
+                    break;
+                case ETH_TYPE_IPV6:
+                    gpe->next_protocol = VXLAN_GPE_NP_IPV6;
+                    break;
+                case ETH_TYPE_TEB:
+                    gpe->next_protocol = VXLAN_GPE_NP_ETHERNET;
+                    break;
+            }
+        } else {
+            gpe->next_protocol = VXLAN_GPE_NP_ETHERNET;
+        }
+    } else {
+        put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
+        put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));
+    }
 
     ovs_mutex_unlock(&dev->mutex);
     data->header_len += sizeof *vxh;
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index ba69461..466d5a8 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -414,6 +414,7 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
     uint16_t dst_proto = 0, src_proto = 0;
     struct netdev_tunnel_config tnl_cfg;
     struct smap_node *node;
+    bool is_layer3 = false;
     int err;
 
     has_csum = strstr(type, "gre") || strstr(type, "geneve") ||
@@ -508,6 +509,9 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
             while (ext) {
                 if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) {
                     tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP);
+                } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) {
+                    tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE);
+                    optional_layer3 = true;
                 } else {
                     ds_put_format(&errors, "%s: unknown extension '%s'\n",
                                   name, ext);
@@ -520,9 +524,9 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
         } else if (!strcmp(node->key, "egress_pkt_mark")) {
             tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10);
             tnl_cfg.set_egress_pkt_mark = true;
-        } else if (!strcmp(node->key, "layer3") && optional_layer3) {
+        } else if (!strcmp(node->key, "layer3")) {
             if (!strcmp(node->value, "true")) {
-                tnl_cfg.is_layer3 = true;
+                is_layer3 = true;
             }
         } else {
             ds_put_format(&errors, "%s: unknown %s argument '%s'\n",
@@ -530,6 +534,13 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
         }
     }
 
+    if (optional_layer3 && is_layer3) {
+       tnl_cfg.is_layer3 = is_layer3;
+    } else if (!optional_layer3 && is_layer3) {
+        ds_put_format(&errors, "%s: unknown %s argument '%s'\n",
+                      name, type, "layer3");
+    }
+
     if (!ipv6_addr_is_set(&tnl_cfg.ipv6_dst) && !tnl_cfg.ip_dst_flow) {
         ds_put_format(&errors,
                       "%s: %s type requires valid 'remote_ip' argument\n",
@@ -660,7 +671,8 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
         smap_add(args, "csum", "true");
     }
 
-    if (tnl_cfg.is_layer3 && !strcmp("gre", type)) {
+    if (tnl_cfg.is_layer3 && (!strcmp("gre", type) ||
+        !strcmp("vxlan", type))) {
         smap_add(args, "layer3", "true");
     }
 
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index b114e26..4e6c2aa 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -16,6 +16,8 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
                        options:remote_ip=1.1.2.93 options:out_key=flow options:egress_pkt_mark=1234 ofport_request=6\
                     -- add-port int-br t6 -- set Interface t6 type=gre \
                        options:remote_ip=1.1.2.92 options:key=456 options:layer3=true ofport_request=7\
+                    -- add-port int-br t7 -- set Interface t7 type=vxlan \
+                       options:remote_ip=1.1.2.92 options:key=345 options:exts=gpe ofport_request=8\
                        ], [0])
 
 AT_CHECK([ovs-appctl dpif/show], [0], [dnl
@@ -31,6 +33,7 @@ dummy at ovs-dummy: hit:0 missed:0
 		t4 5/6081: (geneve: key=123, remote_ip=flow)
 		t5 6/6081: (geneve: egress_pkt_mark=1234, out_key=flow, remote_ip=1.1.2.93)
 		t6 7/3: (gre: key=456, layer3=true, remote_ip=1.1.2.92)
+		t7 8/4789: (vxlan: key=345, remote_ip=1.1.2.92)
 ])
 
 dnl First setup dummy interface IP address, then add the route
@@ -113,6 +116,13 @@ AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)),1)
 ])
 
+dnl Check VXLAN GPE tunnel push
+AT_CHECK([ovs-ofctl add-flow int-br action=8])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000003,vni=0x159)),out_port(100)),1)
+])
+
 dnl Check VXLAN tunnel push set tunnel id by flow and checksum
 AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:124,4"])
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
-- 
2.7.4



More information about the dev mailing list