diff mbox

[ovs-dev,v6,5/5] userspace: add vxlan gpe support to vport

Message ID AM2PR07MB10429FB8FE16FF98EE42A3138AE20@AM2PR07MB1042.eurprd07.prod.outlook.com
State Changes Requested
Headers show

Commit Message

Zoltan Balogh May 12, 2017, 11:07 a.m. UTC
From: Georg Schmuecking <georg.schmuecking@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@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

Comments

Ben Pfaff May 19, 2017, 4:54 a.m. UTC | #1
On Fri, May 12, 2017 at 11:07:46AM +0000, Zoltán Balogh wrote:
> From: Georg Schmuecking <georg.schmuecking@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@ericsson.com>

checkpatch says:

    ERROR: Inappropriate bracing around statement
    #138 FILE: lib/netdev-native-tnl.c:519:
            if (gpe->oam_flag)

    WARNING: Line has non-spaces leading whitespace
    #153 FILE: lib/netdev-native-tnl.c:534:
            }

    WARNING: Line length is >79-characters long
    #188 FILE: lib/netdev-native-tnl.c:578:
            put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));

    WARNING: Line length is >79-characters long
    #206 FILE: lib/netdev-native-tnl.c:596:
            put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));

In netdev_vxlan_pop_header(), I don't see anything that verifies that
the packet is long enough when GPE is present.

The formatting of the switch statements look different from the usual
OVS style, which is like this:

        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;
        }

I suspect that struct vxlanhdr_gpe should use ovs_16aligned_be32 for the
vx_vni member.

I doubt that these #defines are a good idea, in vxlangpe.h:

    #define u8 uint8_t
    #define u32 uint8_t
    #define __be32 ovs_be32

struct vxlanhdr in lib/packets.h is pretty different in philosophy from
struct vxlanhdr_gpe in vxlangpe.h.  I'd suggest making the new structure
more like the existing one; OVS doesn't really use bit-fields much and
in my experience they do not make code much easier to deal with.

The new struct vxlan_metadata doesn't seem to be used anywhere and I
wonder whether it should be included.

Thanks,

Ben.
Yang, Yi May 19, 2017, 5:18 a.m. UTC | #2
Ben, vxlangpe.h is from Linux kernel header file, so style is Linux but not ovs, keeping these intact is to make sure there are same pieces in both userspace and kernel, so code is also almost same. We'll change it to ovs style per your comments. Zoltan, please let me know if you need any help from me.

-----Original Message-----
From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Ben Pfaff
Sent: Friday, May 19, 2017 12:54 PM
To: Zoltán Balogh <zoltan.balogh@ericsson.com>
Cc: 'dev@openvswitch.org' <dev@openvswitch.org>
Subject: Re: [ovs-dev] [PATCH v6 5/5] userspace: add vxlan gpe support to vport

On Fri, May 12, 2017 at 11:07:46AM +0000, Zoltán Balogh wrote:
> From: Georg Schmuecking <georg.schmuecking@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@ericsson.com>

checkpatch says:

    ERROR: Inappropriate bracing around statement
    #138 FILE: lib/netdev-native-tnl.c:519:
            if (gpe->oam_flag)

    WARNING: Line has non-spaces leading whitespace
    #153 FILE: lib/netdev-native-tnl.c:534:
            }

    WARNING: Line length is >79-characters long
    #188 FILE: lib/netdev-native-tnl.c:578:
            put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));

    WARNING: Line length is >79-characters long
    #206 FILE: lib/netdev-native-tnl.c:596:
            put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));

In netdev_vxlan_pop_header(), I don't see anything that verifies that the packet is long enough when GPE is present.

The formatting of the switch statements look different from the usual OVS style, which is like this:

        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;
        }

I suspect that struct vxlanhdr_gpe should use ovs_16aligned_be32 for the vx_vni member.

I doubt that these #defines are a good idea, in vxlangpe.h:

    #define u8 uint8_t
    #define u32 uint8_t
    #define __be32 ovs_be32

struct vxlanhdr in lib/packets.h is pretty different in philosophy from struct vxlanhdr_gpe in vxlangpe.h.  I'd suggest making the new structure more like the existing one; OVS doesn't really use bit-fields much and in my experience they do not make code much easier to deal with.

The new struct vxlan_metadata doesn't seem to be used anywhere and I wonder whether it should be included.

Thanks,

Ben.
Jan Scheurich May 22, 2017, 3:10 p.m. UTC | #3
I had a similar comment to avoid bit-fields in the nsh_hdr struct in the NSH patch series.
@Yi: Have a look at the restructured nsh.h in my new branch.

I suggest you just add any required vxlan-gpe specific items to the VXLAN section in lib/packets.h rather than creating a new header file for vxlan-gpe.

/Jan

> -----Original Message-----
> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Yang, Yi Y
> Sent: Friday, 19 May, 2017 07:18
> To: Ben Pfaff <blp@ovn.org>; Zoltán Balogh <zoltan.balogh@ericsson.com>
> Cc: 'dev@openvswitch.org' <dev@openvswitch.org>
> Subject: Re: [ovs-dev] [PATCH v6 5/5] userspace: add vxlan gpe support to vport
> 
> Ben, vxlangpe.h is from Linux kernel header file, so style is Linux but not ovs, keeping these intact is to make sure there are same pieces in
> both userspace and kernel, so code is also almost same. We'll change it to ovs style per your comments. Zoltan, please let me know if you
> need any help from me.
> 
> -----Original Message-----
> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Ben Pfaff
> Sent: Friday, May 19, 2017 12:54 PM
> To: Zoltán Balogh <zoltan.balogh@ericsson.com>
> Cc: 'dev@openvswitch.org' <dev@openvswitch.org>
> Subject: Re: [ovs-dev] [PATCH v6 5/5] userspace: add vxlan gpe support to vport
> 
> On Fri, May 12, 2017 at 11:07:46AM +0000, Zoltán Balogh wrote:
> > From: Georg Schmuecking <georg.schmuecking@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@ericsson.com>
> 
> checkpatch says:
> 
>     ERROR: Inappropriate bracing around statement
>     #138 FILE: lib/netdev-native-tnl.c:519:
>             if (gpe->oam_flag)
> 
>     WARNING: Line has non-spaces leading whitespace
>     #153 FILE: lib/netdev-native-tnl.c:534:
>             }
> 
>     WARNING: Line length is >79-characters long
>     #188 FILE: lib/netdev-native-tnl.c:578:
>             put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));
> 
>     WARNING: Line length is >79-characters long
>     #206 FILE: lib/netdev-native-tnl.c:596:
>             put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8));
> 
> In netdev_vxlan_pop_header(), I don't see anything that verifies that the packet is long enough when GPE is present.
> 
> The formatting of the switch statements look different from the usual OVS style, which is like this:
> 
>         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;
>         }
> 
> I suspect that struct vxlanhdr_gpe should use ovs_16aligned_be32 for the vx_vni member.
> 
> I doubt that these #defines are a good idea, in vxlangpe.h:
> 
>     #define u8 uint8_t
>     #define u32 uint8_t
>     #define __be32 ovs_be32
> 
> struct vxlanhdr in lib/packets.h is pretty different in philosophy from struct vxlanhdr_gpe in vxlangpe.h.  I'd suggest making the new
> structure more like the existing one; OVS doesn't really use bit-fields much and in my experience they do not make code much easier to
> deal with.
> 
> The new struct vxlan_metadata doesn't seem to be used anywhere and I wonder whether it should be included.
> 
> Thanks,
> 
> Ben.
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Ben Pfaff May 25, 2017, 8:59 p.m. UTC | #4
On Mon, May 22, 2017 at 03:10:56PM +0000, Jan Scheurich wrote:
> I had a similar comment to avoid bit-fields in the nsh_hdr struct in the NSH patch series.
> @Yi: Have a look at the restructured nsh.h in my new branch.
> 
> I suggest you just add any required vxlan-gpe specific items to the VXLAN section in lib/packets.h rather than creating a new header file for vxlan-gpe.

Yes, that is my preference too.
Yang, Yi May 26, 2017, 12:55 a.m. UTC | #5
Zoltan has done that way.

-----Original Message-----
From: Ben Pfaff [mailto:blp@ovn.org] 
Sent: Friday, May 26, 2017 5:00 AM
To: Jan Scheurich <jan.scheurich@ericsson.com>
Cc: Yang, Yi Y <yi.y.yang@intel.com>; Zoltán Balogh <zoltan.balogh@ericsson.com>; 'dev@openvswitch.org' <dev@openvswitch.org>
Subject: Re: [ovs-dev] [PATCH v6 5/5] userspace: add vxlan gpe support to vport

On Mon, May 22, 2017 at 03:10:56PM +0000, Jan Scheurich wrote:
> I had a similar comment to avoid bit-fields in the nsh_hdr struct in the NSH patch series.
> @Yi: Have a look at the restructured nsh.h in my new branch.
> 
> I suggest you just add any required vxlan-gpe specific items to the VXLAN section in lib/packets.h rather than creating a new header file for vxlan-gpe.

Yes, that is my preference too.
diff mbox

Patch

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 6cbe45c..961ac34 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@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: 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))
 ])
 
+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: 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))
+])
+
 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])