diff --git a/datapath-windows/automake.mk<https://urldefense.proofpoint.com/v2/url?u=http-3A__automake.mk&d=CwMFaQ&c=Sqcl0Ez6M0X8aeM67LKIiDJAXVeAw-YihVMNtXt-uEs&r=rcjwdr28Pfj2lpjqdBAfNA&m=bPykOb4vQn9au86TWRhP7WX1887vhC0vf-5qXXnVJ4Q&s=I3j2DwE-gNG6j66xYD8Mb8VHL6TjoXXyTaTZW_ktWh8&e=> b/datapath-windows/automake.mk<https://urldefense.proofpoint.com/v2/url?u=http-3A__automake.mk&d=CwMFaQ&c=Sqcl0Ez6M0X8aeM67LKIiDJAXVeAw-YihVMNtXt-uEs&r=rcjwdr28Pfj2lpjqdBAfNA&m=bPykOb4vQn9au86TWRhP7WX1887vhC0vf-5qXXnVJ4Q&s=I3j2DwE-gNG6j66xYD8Mb8VHL6TjoXXyTaTZW_ktWh8&e=>
index c9af806..53fb5c5 100644
--- a/datapath-windows/automake.mk<https://urldefense.proofpoint.com/v2/url?u=http-3A__automake.mk&d=CwMFaQ&c=Sqcl0Ez6M0X8aeM67LKIiDJAXVeAw-YihVMNtXt-uEs&r=rcjwdr28Pfj2lpjqdBAfNA&m=bPykOb4vQn9au86TWRhP7WX1887vhC0vf-5qXXnVJ4Q&s=I3j2DwE-gNG6j66xYD8Mb8VHL6TjoXXyTaTZW_ktWh8&e=>
+++ b/datapath-windows/automake.mk<https://urldefense.proofpoint.com/v2/url?u=http-3A__automake.mk&d=CwMFaQ&c=Sqcl0Ez6M0X8aeM67LKIiDJAXVeAw-YihVMNtXt-uEs&r=rcjwdr28Pfj2lpjqdBAfNA&m=bPykOb4vQn9au86TWRhP7WX1887vhC0vf-5qXXnVJ4Q&s=I3j2DwE-gNG6j66xYD8Mb8VHL6TjoXXyTaTZW_ktWh8&e=>
@@ -68,6 +68,8 @@ EXTRA_DIST += \
        datapath-windows/ovsext/Vport.h \
        datapath-windows/ovsext/Vxlan.c \
        datapath-windows/ovsext/Vxlan.h \
+       datapath-windows/ovsext/Geneve.c \
+       datapath-windows/ovsext/Geneve.h \
        datapath-windows/ovsext/ovsext.inf \
        datapath-windows/ovsext/ovsext.rc \
        datapath-windows/ovsext/ovsext.vcxproj \
diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c
index 7ac6bb7..722a2a8 100644
--- a/datapath-windows/ovsext/Actions.c
+++ b/datapath-windows/ovsext/Actions.c
@@ -33,6 +33,7 @@
 #include "User.h"
 #include "Vport.h"
 #include "Vxlan.h"
+#include "Geneve.h"

 #ifdef OVS_DBG_MOD
 #undef OVS_DBG_MOD
@@ -48,6 +49,8 @@ typedef struct _OVS_ACTION_STATS {
     UINT64 txVxlan;
     UINT64 rxStt;
     UINT64 txStt;
+    UINT64 rxGeneve;
+    UINT64 txGeneve;
     UINT64 flowMiss;
     UINT64 flowUserspace;
     UINT64 txTcp;
@@ -237,6 +240,9 @@ OvsDetectTunnelRxPkt(OvsForwardingContext *ovsFwdCtx,
             case OVS_VPORT_TYPE_VXLAN:
                 ovsActionStats.rxVxlan++;
                 break;
+            case OVS_VPORT_TYPE_GENEVE:
+                ovsActionStats.rxGeneve++;
+                break;
             case OVS_VPORT_TYPE_GRE:
                 ovsActionStats.rxGre++;
                 break;
@@ -333,6 +339,9 @@ OvsDetectTunnelPkt(OvsForwardingContext *ovsFwdCtx,
             case OVS_VPORT_TYPE_STT:
                 ovsActionStats.txStt++;
                 break;
+            case OVS_VPORT_TYPE_GENEVE:
+               ovsActionStats.txGeneve++;
+               break;
             }
             ovsFwdCtx->tunnelTxNic = dstVport;
         }
@@ -689,6 +698,11 @@ OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
                              &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
                              &ovsFwdCtx->layers, &newNbl);
         break;
+    case OVS_VPORT_TYPE_GENEVE:
+        status = OvsEncapGeneve(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl,
+                                &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext,
+                                &ovsFwdCtx->layers, &newNbl);
+        break;
     default:
         ASSERT(! "Tx: Unhandled tunnel type");
     }
@@ -767,6 +781,10 @@ OvsTunnelPortRx(OvsForwardingContext *ovsFwdCtx)
             dropReason = L"OVS-STT segment is cached";
         }
         break;
+    case OVS_VPORT_TYPE_GENEVE:
+        status = OvsDecapGeneve(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
+                                &ovsFwdCtx->tunKey, &newNbl);
+        break;
     default:
         OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n",
                       tunnelRxVport->ovsType);
@@ -1233,57 +1251,6 @@ OvsActionMplsPush(OvsForwardingContext *ovsFwdCtx,
 }

 /*
- * --------------------------------------------------------------------------
- * OvsTunnelAttrToIPv4TunnelKey --
- *      Convert tunnel attribute to OvsIPv4TunnelKey.
- * --------------------------------------------------------------------------
- */
-static __inline NDIS_STATUS
-OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr,
-                             OvsIPv4TunnelKey *tunKey)
-{
-   PNL_ATTR a;
-   INT rem;
-
-   tunKey->attr[0] = 0;
-   tunKey->attr[1] = 0;
-   tunKey->attr[2] = 0;
-   ASSERT(NlAttrType(attr) == OVS_KEY_ATTR_TUNNEL);
-
-   NL_ATTR_FOR_EACH_UNSAFE (a, rem, NlAttrData(attr),
-                            NlAttrGetSize(attr)) {
-      switch (NlAttrType(a)) {
-      case OVS_TUNNEL_KEY_ATTR_ID:
-         tunKey->tunnelId = NlAttrGetBe64(a);
-         tunKey->flags |= OVS_TNL_F_KEY;
-         break;
-      case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
-         tunKey->src = NlAttrGetBe32(a);
-         break;
-      case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
-         tunKey->dst = NlAttrGetBe32(a);
-         break;
-      case OVS_TUNNEL_KEY_ATTR_TOS:
-         tunKey->tos = NlAttrGetU8(a);
-         break;
-      case OVS_TUNNEL_KEY_ATTR_TTL:
-         tunKey->ttl = NlAttrGetU8(a);
-         break;
-      case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
-         tunKey->flags |= OVS_TNL_F_DONT_FRAGMENT;
-         break;
-      case OVS_TUNNEL_KEY_ATTR_CSUM:
-         tunKey->flags |= OVS_TNL_F_CSUM;
-         break;
-      default:
-         ASSERT(0);
-      }
-   }
-
-   return NDIS_STATUS_SUCCESS;
-}
-
-/*
  *----------------------------------------------------------------------------
  * OvsUpdateEthHeader --
  *      Updates the ethernet header in ovsFwdCtx.curNbl inline based on the
@@ -1511,7 +1478,8 @@ OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx,
     case OVS_KEY_ATTR_TUNNEL:
     {
         OvsIPv4TunnelKey tunKey;
-        status = OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR)a, &tunKey);
+        NTSTATUS convertStatus = OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR)a, &tunKey);
+        status = SUCCEEDED(convertStatus) ? NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE;
         ASSERT(status == NDIS_STATUS_SUCCESS);
         tunKey.flow_hash = (uint16)(hash ? *hash : OvsHashFlow(key));
         tunKey.dst_port = key->ipKey.l4.tpDst;
diff --git a/datapath-windows/ovsext/Debug.h b/datapath-windows/ovsext/Debug.h
index e5ed963..935f858 100644
--- a/datapath-windows/ovsext/Debug.h
+++ b/datapath-windows/ovsext/Debug.h
@@ -41,6 +41,7 @@
 #define OVS_DBG_TUNFLT   BIT32(21)
 #define OVS_DBG_STT      BIT32(22)
 #define OVS_DBG_CONTRK   BIT32(23)
+#define OVS_DBG_GENEVE   BIT32(24)

 #define OVS_DBG_RESERVED BIT32(31)
 //Please add above OVS_DBG_RESERVED.
diff --git a/datapath-windows/ovsext/DpInternal.h b/datapath-windows/ovsext/DpInternal.h
index 07bc180..42b5ec9 100644
--- a/datapath-windows/ovsext/DpInternal.h
+++ b/datapath-windows/ovsext/DpInternal.h
@@ -128,10 +128,18 @@ typedef struct L2Key {
 } L2Key; /* Size of 24 byte. */

 /* Number of packet attributes required to store OVS tunnel key. */
-#define NUM_PKT_ATTR_REQUIRED 3
+#define NUM_PKT_ATTR_REQUIRED 35
+#define TUN_OPT_MAX_LEN 255

 typedef union OvsIPv4TunnelKey {
+    /* Options should always be the first member of tunnel key.
+     * They are stored at the end of the array if they are less than the
+     * maximum size. This allows us to get the benefits of variable length
+     * matching for small options.
+     */
     struct {
+        UINT8 tunOpts[TUN_OPT_MAX_LEN];          /* Tunnel options. */
+        UINT8 tunOptLen;             /* Tunnel option length in byte. */
         ovs_be32 dst;
         ovs_be32 src;
         ovs_be64 tunnelId;
@@ -147,7 +155,22 @@ typedef union OvsIPv4TunnelKey {
         };
     };
     uint64_t attr[NUM_PKT_ATTR_REQUIRED];
-} OvsIPv4TunnelKey; /* Size of 24 byte. */
+} OvsIPv4TunnelKey; /* Size of 280 byte. */
+
+__inline uint8_t TunnelKeyGetOptionsOffset(const OvsIPv4TunnelKey *key)
+{
+    return TUN_OPT_MAX_LEN - key->tunOptLen;
+}
+
+__inline uint8_t* TunnelKeyGetOptions(OvsIPv4TunnelKey *key)
+{
+    return key->tunOpts + TunnelKeyGetOptionsOffset(key);
+}
+
+__inline uint16_t TunnelKeyGetRealSize(OvsIPv4TunnelKey *key)
+{
+    return sizeof(OvsIPv4TunnelKey) - TunnelKeyGetOptionsOffset(key);
+}

 typedef struct MplsKey {
     ovs_be32 lse;                /* MPLS topmost label stack entry. */
@@ -155,7 +178,7 @@ typedef struct MplsKey {
 } MplsKey; /* Size of 8 bytes. */

 typedef __declspec(align(8)) struct OvsFlowKey {
-    OvsIPv4TunnelKey tunKey;     /* 24 bytes */
+    OvsIPv4TunnelKey tunKey;     /* 280 bytes */
     L2Key l2;                    /* 24 bytes */
     union {
         /* These headers are mutually exclusive. */
diff --git a/datapath-windows/ovsext/Flow.c b/datapath-windows/ovsext/Flow.c
index 595518f..bc0bb37 100644
--- a/datapath-windows/ovsext/Flow.c
+++ b/datapath-windows/ovsext/Flow.c
@@ -21,6 +21,7 @@
 #include "Flow.h"
 #include "PacketParser.h"
 #include "Datapath.h"
+#include "Geneve.h"

 #ifdef OVS_DBG_MOD
 #undef OVS_DBG_MOD
@@ -85,7 +86,7 @@ static NTSTATUS OvsDoDumpFlows(OvsFlowDumpInput *dumpInput,
                                UINT32 *replyLen);
 static NTSTATUS OvsProbeSupportedFeature(POVS_MESSAGE msgIn,
                                          PNL_ATTR keyAttr);
-
+static UINT16 OvsGetFlowL2Offset(const OvsIPv4TunnelKey *tunKey);

 #define OVS_FLOW_TABLE_SIZE 2048
 #define OVS_FLOW_TABLE_MASK (OVS_FLOW_TABLE_SIZE -1)
@@ -1029,6 +1030,14 @@ MapFlowTunKeyToNlKey(PNL_BUFFER nlBuf,
         goto done;
     }

+    if (tunKey->tunOptLen > 0 &&
+        !NlMsgPutTailUnspec(nlBuf, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,
+                            (PCHAR)TunnelKeyGetOptions(tunKey),
+                            tunKey->tunOptLen)) {
+        rc = STATUS_UNSUCCESSFUL;
+        goto done;
+    }
+
 done:
     NlMsgEndNested(nlBuf, offset);
 error_nested_start:
@@ -1638,6 +1647,120 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,

 /*
  *----------------------------------------------------------------------------
+ *  OvsTunnelAttrToGeneveOptions --
+ *    Converts OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS attribute to tunKey->tunOpts.
+ *----------------------------------------------------------------------------
+ */
+static __inline NTSTATUS
+OvsTunnelAttrToGeneveOptions(PNL_ATTR attr,
+                             OvsIPv4TunnelKey *tunKey)
+{
+    UINT32 optLen = NlAttrGetSize(attr);
+    GeneveOptionHdr *option;
+    BOOLEAN isCritical = FALSE;
+    if (optLen > TUN_OPT_MAX_LEN) {
+        OVS_LOG_ERROR("Geneve option length err (len %d, max %Iu).",
+                      optLen, TUN_OPT_MAX_LEN);
+        return STATUS_INFO_LENGTH_MISMATCH;
+    } else if (optLen % 4 != 0) {
+        OVS_LOG_ERROR("Geneve opt len %d is not a multiple of 4.", optLen);
+        return STATUS_INFO_LENGTH_MISMATCH;
+    }
+    tunKey->tunOptLen = (UINT8)optLen;
+    option = (GeneveOptionHdr *)NlAttrData(attr);
+    while (optLen > 0) {
+        UINT32 len;
+        if (optLen < sizeof(*option)) {
+            return STATUS_INFO_LENGTH_MISMATCH;
+        }
+        len = sizeof(*option) + option->length * 4;
+        if (len > optLen) {
+            return STATUS_INFO_LENGTH_MISMATCH;
+        }
+        if (option->type & GENEVE_CRIT_OPT_TYPE) {
+            isCritical = TRUE;
+        }
+        option = (GeneveOptionHdr *)((UINT8 *)option + len);
+        optLen -= len;
+    }
+    memcpy(TunnelKeyGetOptions(tunKey), option, optLen);
+    if (isCritical) {
+        tunKey->flags |= OVS_TNL_F_CRT_OPT;
+    }
+    return STATUS_SUCCESS;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ *  OvsTunnelAttrToIPv4TunnelKey --
+ *    Converts OVS_KEY_ATTR_TUNNEL attribute to tunKey.
+ *----------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr,
+                             OvsIPv4TunnelKey *tunKey)
+{
+    PNL_ATTR a;
+    INT rem;
+    INT hasOpt = 0;
+    NTSTATUS status;
+
+    memset(tunKey, 0, OVS_WIN_TUNNEL_KEY_SIZE);
+    ASSERT(NlAttrType(attr) == OVS_KEY_ATTR_TUNNEL);
+
+    NL_ATTR_FOR_EACH_UNSAFE(a, rem, NlAttrData(attr),
+        NlAttrGetSize(attr)) {
+        switch (NlAttrType(a)) {
+        case OVS_TUNNEL_KEY_ATTR_ID:
+            tunKey->tunnelId = NlAttrGetBe64(a);
+            tunKey->flags |= OVS_TNL_F_KEY;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
+            tunKey->src = NlAttrGetBe32(a);
+            break;
+        case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
+            tunKey->dst = NlAttrGetBe32(a);
+            break;
+        case OVS_TUNNEL_KEY_ATTR_TOS:
+            tunKey->tos = NlAttrGetU8(a);
+            break;
+        case OVS_TUNNEL_KEY_ATTR_TTL:
+            tunKey->ttl = NlAttrGetU8(a);
+            break;
+        case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
+            tunKey->flags |= OVS_TNL_F_DONT_FRAGMENT;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_CSUM:
+            tunKey->flags |= OVS_TNL_F_CSUM;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_OAM:
+            tunKey->flags |= OVS_TNL_F_OAM;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
+            if (hasOpt) {
+                /* Duplicate options attribute is not allowed. */
+                return NDIS_STATUS_FAILURE;
+            }
+            status = OvsTunnelAttrToGeneveOptions(a, tunKey);
+            if (!SUCCEEDED(status)) {
+                return status;
+            }
+            tunKey->flags |= OVS_TNL_F_GENEVE_OPT;
+            hasOpt = 1;
+            break;
+        default:
+            // XXX: Support OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS
+            return STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+/*
+ *----------------------------------------------------------------------------
  *  MapTunAttrToFlowPut --
  *    Converts FLOW_TUNNEL_KEY attribute to OvsFlowKey->tunKey.
  *----------------------------------------------------------------------------
@@ -1647,8 +1770,10 @@ MapTunAttrToFlowPut(PNL_ATTR *keyAttrs,
                     PNL_ATTR *tunAttrs,
                     OvsFlowKey *destKey)
 {
+    memset(&destKey->tunKey, 0, OVS_WIN_TUNNEL_KEY_SIZE);
     if (keyAttrs[OVS_KEY_ATTR_TUNNEL]) {
-
+        /* XXX: This blocks performs same functionality as
+           OvsTunnelAttrToIPv4TunnelKey. Consider refactoring the code.*/
         if (tunAttrs[OVS_TUNNEL_KEY_ATTR_ID]) {
             destKey->tunKey.tunnelId =
                 NlAttrGetU64(tunAttrs[OVS_TUNNEL_KEY_ATTR_ID]);
@@ -1683,13 +1808,21 @@ MapTunAttrToFlowPut(PNL_ATTR *keyAttrs,
                 NlAttrGetU8(tunAttrs[OVS_TUNNEL_KEY_ATTR_TTL]);
         }

-        destKey->tunKey.pad = 0;
-        destKey->l2.offset = 0;
+        if (tunAttrs[OVS_TUNNEL_KEY_ATTR_OAM]) {
+        destKey->tunKey.flags |= OVS_TNL_F_OAM;
+        }
+
+        if (tunAttrs[OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS]) {
+        NTSTATUS status = OvsTunnelAttrToGeneveOptions(
+                          tunAttrs[OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS],
+                          &destKey->tunKey);
+        if (SUCCEEDED(status)) {
+            destKey->tunKey.flags |= OVS_TNL_F_GENEVE_OPT;
+        }
+        }
+        destKey->l2.offset = OvsGetFlowL2Offset(&destKey->tunKey);
     } else {
-        destKey->tunKey.attr[0] = 0;
-        destKey->tunKey.attr[1] = 0;
-        destKey->tunKey.attr[2] = 0;
-        destKey->l2.offset = sizeof destKey->tunKey;
+        destKey->l2.offset = OvsGetFlowL2Offset(NULL);
     }
 }

@@ -1853,6 +1986,19 @@ OvsGetFlowMetadata(OvsFlowKey *key,
     return status;
 }

+UINT16
+OvsGetFlowL2Offset(const OvsIPv4TunnelKey *tunKey)
+{
+    if (tunKey != NULL) {
+        // Align with int64 boundary
+        if (tunKey->tunOptLen == 0) {
+            return (TUN_OPT_MAX_LEN + 1) / 8 * 8;
+        }
+        return TunnelKeyGetOptionsOffset(tunKey) / 8 * 8;
+    } else {
+        return OVS_WIN_TUNNEL_KEY_SIZE;
+    }
+}

 /*
 *----------------------------------------------------------------------------
@@ -2057,16 +2203,17 @@ OvsExtractFlow(const NET_BUFFER_LIST *packet,

     if (tunKey) {
         ASSERT(tunKey->dst != 0);
-        RtlMoveMemory(&flow->tunKey, tunKey, sizeof flow->tunKey);
-        flow->l2.offset = 0;
+        UINT8 optOffset = TunnelKeyGetOptionsOffset(tunKey);
+        RtlMoveMemory(((UINT8 *)&flow->tunKey) + optOffset,
+                      ((UINT8 *)tunKey) + optOffset,
+                      TunnelKeyGetRealSize(tunKey));
     } else {
         flow->tunKey.dst = 0;
-        flow->l2.offset = OVS_WIN_TUNNEL_KEY_SIZE;
     }
-
+    flow->l2.offset = OvsGetFlowL2Offset(tunKey);
     flow->l2.inPort = inPort;

-    if ( OvsPacketLenNBL(packet) < ETH_HEADER_LEN_DIX) {
+    if (OvsPacketLenNBL(packet) < ETH_HEADER_LEN_DIX) {
         flow->l2.keyLen = OVS_WIN_TUNNEL_KEY_SIZE + 8 - flow->l2.offset;
         return NDIS_STATUS_SUCCESS;
     }
@@ -2390,8 +2537,8 @@ OvsLookupFlow(OVS_DATAPATH *datapath,
     UINT16 size = key->l2.keyLen;
     UINT8 *start;

-    ASSERT(key->tunKey.dst || offset == sizeof (OvsIPv4TunnelKey));
-    ASSERT(!key->tunKey.dst || offset == 0);
+    ASSERT(key->tunKey.dst || offset == sizeof(OvsIPv4TunnelKey));
+    ASSERT(!key->tunKey.dst || offset == OvsGetFlowL2Offset(&key->tunKey));

     start = (UINT8 *)key + offset;

@@ -2447,7 +2594,7 @@ OvsHashFlow(const OvsFlowKey *key)
     UINT16 size = key->l2.keyLen;
     UINT8 *start;

-    ASSERT(key->tunKey.dst || offset == sizeof (OvsIPv4TunnelKey));
+    ASSERT(key->tunKey.dst || offset == sizeof(OvsIPv4TunnelKey));
     ASSERT(!key->tunKey.dst || offset == 0);
     start = (UINT8 *)key + offset;
     return OvsJhashBytes(start, size, 0);
diff --git a/datapath-windows/ovsext/Flow.h b/datapath-windows/ovsext/Flow.h
index d39db45..0744d30 100644
--- a/datapath-windows/ovsext/Flow.h
+++ b/datapath-windows/ovsext/Flow.h
@@ -87,10 +87,17 @@ VOID MapTunAttrToFlowPut(PNL_ATTR *keyAttrs, PNL_ATTR *tunAttrs,
                          OvsFlowKey *destKey);
 UINT32 OvsFlowKeyAttrSize(void);
 UINT32 OvsTunKeyAttrSize(void);
+NTSTATUS OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr, OvsIPv4TunnelKey *tunKey);

 /* Flags for tunneling */
 #define OVS_TNL_F_DONT_FRAGMENT         (1 << 0)
 #define OVS_TNL_F_CSUM                  (1 << 1)
 #define OVS_TNL_F_KEY                   (1 << 2)
+#define OVS_TNL_F_OAM                   (1 << 3)
+#define OVS_TNL_F_CRT_OPT               (1 << 4)
+#define OVS_TNL_F_GENEVE_OPT            (1 << 5)
+#define OVS_TNL_F_VXLAN_OPT             (1 << 6)
+
+#define OVS_TNL_HAS_OPTIONS             (OVS_TNL_F_GENEVE_OPT | OVS_TNL_F_VXLAN_OPT)

 #endif /* __FLOW_H_ */
diff --git a/datapath-windows/ovsext/Geneve.c b/datapath-windows/ovsext/Geneve.c
new file mode 100644
index 0000000..5712e4d
--- /dev/null
+++ b/datapath-windows/ovsext/Geneve.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2016 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0<https://urldefense.proofpoint.com/v2/url?u=http-3A__www.apache.org_licenses_LICENSE-2D2.0&d=CwMFaQ&c=Sqcl0Ez6M0X8aeM67LKIiDJAXVeAw-YihVMNtXt-uEs&r=rcjwdr28Pfj2lpjqdBAfNA&m=bPykOb4vQn9au86TWRhP7WX1887vhC0vf-5qXXnVJ4Q&s=PhatvLxse4f0KutEy0uF1MfJfw0YxeykZVSUBwbCbWI&e=>
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "precomp.h"
+
+#include "Atomic.h"
+#include "Debug.h"
+#include "Flow.h"
+#include "IpHelper.h"
+#include "Jhash.h"
+#include "NetProto.h"
+#include "Offload.h"
+#include "PacketIO.h"
+#include "PacketParser.h"
+#include "Geneve.h"
+#include "Switch.h"
+#include "User.h"
+#include "Util.h"
+#include "Vport.h"
+
+#ifdef OVS_DBG_MOD
+#undef OVS_DBG_MOD
+#endif
+#define OVS_DBG_MOD OVS_DBG_GENEVE
+
+
+NTSTATUS OvsInitGeneveTunnel(POVS_VPORT_ENTRY vport,
+                             UINT16 udpDestPort)
+{
+    POVS_GENEVE_VPORT genevePort;
+
+    genevePort = (POVS_GENEVE_VPORT)
+        OvsAllocateMemoryWithTag(sizeof(*genevePort), OVS_GENEVE_POOL_TAG);
+    if (!genevePort) {
+        OVS_LOG_ERROR("Insufficient memory, can't allocate GENEVE_VPORT");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlZeroMemory(genevePort, sizeof(*genevePort));
+    genevePort->dstPort = udpDestPort;
+    vport->priv = (PVOID) genevePort;
+    return STATUS_SUCCESS;
+}
+
+VOID
+OvsCleanupGeneveTunnel(POVS_VPORT_ENTRY vport)
+{
+    if (vport->ovsType != OVS_VPORT_TYPE_GENEVE ||
+        vport->priv == NULL) {
+        return;
+    }
+
+    OvsFreeMemoryWithTag(vport->priv, OVS_GENEVE_POOL_TAG);
+    vport->priv = NULL;
+}
+
+NDIS_STATUS OvsEncapGeneve(POVS_VPORT_ENTRY vport,
+                           PNET_BUFFER_LIST curNbl,
+                           OvsIPv4TunnelKey *tunKey,
+                           POVS_SWITCH_CONTEXT switchContext,
+                           POVS_PACKET_HDR_INFO layers,
+                           PNET_BUFFER_LIST *newNbl)
+{
+    NTSTATUS status;
+    OVS_FWD_INFO fwdInfo;
+    PNET_BUFFER curNb;
+    PMDL curMdl;
+    PUINT8 bufferStart;
+    EthHdr *ethHdr;
+    IPHdr *ipHdr;
+    UDPHdr *udpHdr;
+    GeneveHdr *geneveHdr;
+    GeneveOptionHdr *optHdr;
+    POVS_GENEVE_VPORT vportGeneve;
+    UINT32 headRoom = OvsGetGeneveTunHdrMinSize() + tunKey->tunOptLen;
+    UINT32 packetLength;
+    ULONG mss = 0;
+    NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo;
+
+    status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo);
+    if (status != STATUS_SUCCESS) {
+        OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL);
+        // return NDIS_STATUS_PENDING;
+        /*
+         * XXX: Don't know if the completionList will make any sense when
+         * accessed in the callback. Make sure the caveats are known.
+         *
+         * XXX: This code will work once we are able to grab locks in the
+         * callback.
+         */
+        return NDIS_STATUS_FAILURE;
+    }
+
+    curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
+    packetLength = NET_BUFFER_DATA_LENGTH(curNb);
+
+    if (layers->isTcp) {
+        mss = OVSGetTcpMSS(curNbl);
+
+        OVS_LOG_TRACE("MSS %u packet len %u", mss,
+                      packetLength);
+        if (mss) {
+            OVS_LOG_TRACE("l4Offset %d", layers->l4Offset);
+            *newNbl = OvsTcpSegmentNBL(switchContext, curNbl, layers,
+                                       mss, headRoom);
+            if (*newNbl == NULL) {
+                OVS_LOG_ERROR("Unable to segment NBL");
+                return NDIS_STATUS_FAILURE;
+            }
+            /* Clear out LSO flags after this point */
+            NET_BUFFER_LIST_INFO(*newNbl, TcpLargeSendNetBufferListInfo) = 0;
+        }
+    }
+
+    vportGeneve = (POVS_GENEVE_VPORT) GetOvsVportPriv(vport);
+    ASSERT(vportGeneve != NULL);
+
+    /* If we didn't split the packet above, make a copy now */
+    if (*newNbl == NULL) {
+        *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom,
+                                    FALSE /*NBL info*/);
+        if (*newNbl == NULL) {
+            OVS_LOG_ERROR("Unable to copy NBL");
+            return NDIS_STATUS_FAILURE;
+        }
+        csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl,
+                                              TcpIpChecksumNetBufferListInfo);
+        status = OvsApplySWChecksumOnNB(layers, *newNbl, &csumInfo);
+
+        if (status != NDIS_STATUS_SUCCESS) {
+            goto ret_error;
+        }
+    }
+
+    curNbl = *newNbl;
+    for (curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curNb != NULL;
+         curNb = curNb->Next) {
+        status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL);
+        if (status != NDIS_STATUS_SUCCESS) {
+            goto ret_error;
+        }
+
+        curMdl = NET_BUFFER_CURRENT_MDL(curNb);
+        bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl,
+                                                           LowPagePriority);
+        if (!bufferStart) {
+            status = NDIS_STATUS_RESOURCES;
+            goto ret_error;
+        }
+
+        bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
+        if (NET_BUFFER_NEXT_NB(curNb)) {
+            OVS_LOG_TRACE("nb length %u next %u",
+                          NET_BUFFER_DATA_LENGTH(curNb),
+                          NET_BUFFER_DATA_LENGTH(curNb->Next));
+        }
+
+        /* L2 header */
+        ethHdr = (EthHdr *)bufferStart;
+        ASSERT(((PCHAR)&fwdInfo.dstMacAddr + sizeof fwdInfo.dstMacAddr) ==
+               (PCHAR)&fwdInfo.srcMacAddr);
+        NdisMoveMemory(ethHdr->Destination, fwdInfo.dstMacAddr,
+                       sizeof ethHdr->Destination + sizeof ethHdr->Source);
+        ethHdr->Type = htons(ETH_TYPE_IPV4);
+
+        /* IP header */
+        ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr);
+
+        ipHdr->ihl = sizeof *ipHdr / 4;
+        ipHdr->version = IPPROTO_IPV4;
+        ipHdr->tos = tunKey->tos;
+        ipHdr->tot_len = htons(NET_BUFFER_DATA_LENGTH(curNb) - sizeof *ethHdr);
+        ipHdr->id = (uint16)atomic_add64(&vportGeneve->ipId,
+                                         NET_BUFFER_DATA_LENGTH(curNb));
+        ipHdr->frag_off = (tunKey->flags & OVS_TNL_F_DONT_FRAGMENT) ?
+                          IP_DF_NBO : 0;
+        ipHdr->ttl = tunKey->ttl ? tunKey->ttl : GENEVE_DEFAULT_TTL;
+        ipHdr->protocol = IPPROTO_UDP;
+        ASSERT(tunKey->dst == fwdInfo.dstIpAddr);
+        ASSERT(tunKey->src == fwdInfo.srcIpAddr || tunKey->src == 0);
+        ipHdr->saddr = fwdInfo.srcIpAddr;
+        ipHdr->daddr = fwdInfo.dstIpAddr;
+        ipHdr->check = 0;
+
+        /* UDP header */
+        udpHdr = (UDPHdr *)((PCHAR)ipHdr + sizeof *ipHdr);
+        udpHdr->source = htons(tunKey->flow_hash | MAXINT16);
+        udpHdr->dest = htons(vportGeneve->dstPort);
+        udpHdr->len = htons(NET_BUFFER_DATA_LENGTH(curNb) - headRoom +
+                            sizeof *udpHdr + sizeof *geneveHdr +
+                            tunKey->tunOptLen);
+        if (tunKey->flags & OVS_TNL_F_CSUM) {
+            UINT16 udpChksumLen = (UINT16) NET_BUFFER_DATA_LENGTH(curNb) -
+                                   sizeof *ipHdr - sizeof *ethHdr;
+            udpHdr->check = IPPseudoChecksum(&ipHdr->saddr, &ipHdr->daddr,
+                                             IPPROTO_UDP, udpChksumLen);
+        } else {
+            udpHdr->check = 0;
+        }
+        /* Geneve header */
+        geneveHdr = (GeneveHdr *)((PCHAR)udpHdr + sizeof *udpHdr);
+        geneveHdr->version = GENEVE_VER;
+        geneveHdr->optLen = tunKey->tunOptLen / 4;
+        geneveHdr->oam = !!(tunKey->flags & OVS_TNL_F_OAM);
+        geneveHdr->critical = !!(tunKey->flags & OVS_TNL_F_CRT_OPT);
+        geneveHdr->reserved1 = 0;
+        geneveHdr->protocol = ETH_P_TEB_NBO;
+        geneveHdr->vni = GENEVE_TUNNELID_TO_VNI(tunKey->tunnelId);
+        geneveHdr->reserved2 = 0;
+
+        /* Geneve header options */
+        optHdr = (GeneveOptionHdr *)(geneveHdr + 1);
+        memcpy(optHdr, TunnelKeyGetOptions(tunKey), tunKey->tunOptLen);
+
+        csumInfo.Value = 0;
+        csumInfo.Transmit.IpHeaderChecksum = 1;
+        csumInfo.Transmit.IsIPv4 = 1;
+        if (tunKey->flags & OVS_TNL_F_CSUM) {
+            csumInfo.Transmit.UdpChecksum = 1;
+        }
+        NET_BUFFER_LIST_INFO(curNbl,
+                             TcpIpChecksumNetBufferListInfo) = csumInfo.Value;
+    }
+    return STATUS_SUCCESS;
+
+ret_error:
+    OvsCompleteNBL(switchContext, *newNbl, TRUE);
+    *newNbl = NULL;
+    return status;
+}
+
+NDIS_STATUS OvsDecapGeneve(POVS_SWITCH_CONTEXT switchContext,
+                           PNET_BUFFER_LIST curNbl,
+                           OvsIPv4TunnelKey *tunKey,
+                           PNET_BUFFER_LIST *newNbl)
+{
+    PNET_BUFFER curNb;
+    PMDL curMdl;
+    EthHdr *ethHdr;
+    IPHdr *ipHdr;
+    UDPHdr *udpHdr;
+    GeneveHdr *geneveHdr;
+    UINT32 tunnelSize;
+    UINT32 packetLength;
+    PUINT8 bufferStart;
+    PVOID optStart;
+    NDIS_STATUS status;
+
+    /* Check the length of the UDP payload */
+    curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
+    tunnelSize = OvsGetGeneveTunHdrMinSize();
+    packetLength = NET_BUFFER_DATA_LENGTH(curNb);
+    if (packetLength <= tunnelSize) {
+        return NDIS_STATUS_INVALID_LENGTH;
+    }
+
+    /*
+     * Create a copy of the NBL so that we have all the headers in one MDL.
+     */
+    *newNbl = OvsPartialCopyNBL(switchContext, curNbl,
+                                tunnelSize, 0,
+                                TRUE /*copy NBL info */);
+
+    if (*newNbl == NULL) {
+        return NDIS_STATUS_RESOURCES;
+    }
+
+    /* XXX: Handle VLAN header. */
+    curNbl = *newNbl;
+    curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
+    curMdl = NET_BUFFER_CURRENT_MDL(curNb);
+    bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority)
+                  + NET_BUFFER_CURRENT_MDL_OFFSET(curNb);
+    if (!bufferStart) {
+        status = NDIS_STATUS_RESOURCES;
+        goto dropNbl;
+    }
+
+    ethHdr = (EthHdr *)bufferStart;
+    /* XXX: Handle IP options. */
+    ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr);
+    tunKey->src = ipHdr->saddr;
+    tunKey->dst = ipHdr->daddr;
+    tunKey->tos = ipHdr->tos;
+    tunKey->ttl = ipHdr->ttl;
+    tunKey->pad = 0;
+    udpHdr = (UDPHdr *)((PCHAR)ipHdr + sizeof *ipHdr);
+
+    /* Validate if NIC has indicated checksum failure. */
+    status = OvsValidateUDPChecksum(curNbl, udpHdr->check == 0);
+    if (status != NDIS_STATUS_SUCCESS) {
+        goto dropNbl;
+    }
+
+    /* Calculate and verify UDP checksum if NIC didn't do it. */
+    if (udpHdr->check != 0) {
+        status = OvsCalculateUDPChecksum(curNbl, curNb, ipHdr, udpHdr,
+                                         packetLength);
+        tunKey->flags |= OVS_TNL_F_CSUM;
+        if (status != NDIS_STATUS_SUCCESS) {
+            goto dropNbl;
+        }
+    }
+
+    geneveHdr = (GeneveHdr *)((PCHAR)udpHdr + sizeof *udpHdr);
+    if (geneveHdr->protocol != ETH_P_TEB_NBO) {
+        status = STATUS_NDIS_INVALID_PACKET;
+        goto dropNbl;
+    }
+    tunKey->flags = OVS_TNL_F_KEY;
+    if (geneveHdr->oam) {
+        tunKey->flags |= OVS_TNL_F_OAM;
+    }
+    tunKey->tunnelId = GENEVE_VNI_TO_TUNNELID(geneveHdr->vni);
+    tunKey->tunOptLen = (uint8)geneveHdr->optLen * 4;
+    if (tunKey->tunOptLen > TUN_OPT_MAX_LEN ||
+        packetLength < tunnelSize + tunKey->tunOptLen) {
+        status = NDIS_STATUS_INVALID_LENGTH;
+        goto dropNbl;
+    }
+    /* Clear out the receive flag for the inner packet. */
+    NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = 0;
+
+    NdisAdvanceNetBufferDataStart(curNb, tunnelSize, FALSE, NULL);
+    if (tunKey->tunOptLen > 0) {
+        optStart = NdisGetDataBuffer(curNb, tunKey->tunOptLen,
+                                     TunnelKeyGetOptions(tunKey), 1, 0);
+
+        /* If data is contiguous in the buffer, NdisGetDataBuffer will not copy
+           data to the storage. Manual copy is needed. */
+        if (optStart != TunnelKeyGetOptions(tunKey)) {
+            memcpy(TunnelKeyGetOptions(tunKey), optStart, tunKey->tunOptLen);
+        }
+        NdisAdvanceNetBufferDataStart(curNb, tunKey->tunOptLen, FALSE, NULL);
+    }
+
+    return NDIS_STATUS_SUCCESS;
+
+dropNbl:
+    OvsCompleteNBL(switchContext, *newNbl, TRUE);
+    *newNbl = NULL;
+    return status;
+}
diff --git a/datapath-windows/ovsext/Geneve.h b/datapath-windows/ovsext/Geneve.h
new file mode 100644
index 0000000..057f80a
--- /dev/null
+++ b/datapath-windows/ovsext/Geneve.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2016 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0<https://urldefense.proofpoint.com/v2/url?u=http-3A__www.apache.org_licenses_LICENSE-2D2.0&d=CwMFaQ&c=Sqcl0Ez6M0X8aeM67LKIiDJAXVeAw-YihVMNtXt-uEs&r=rcjwdr28Pfj2lpjqdBAfNA&m=bPykOb4vQn9au86TWRhP7WX1887vhC0vf-5qXXnVJ4Q&s=PhatvLxse4f0KutEy0uF1MfJfw0YxeykZVSUBwbCbWI&e=>
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __GENEVE_H_
+#define __GENEVE_H_ 1
+
+#include "NetProto.h"
+typedef struct _OVS_GENEVE_VPORT {
+    UINT16 dstPort;
+    UINT64 filterID;
+    UINT64 ipId;
+    /*
+     * To be filled
+     */
+} OVS_GENEVE_VPORT, *POVS_GENEVE_VPORT;
+
+/* Geneve Header:
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |Ver|  Opt Len  |O|C|    Rsvd.  |          Protocol Type        |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |        Virtual Network Identifier (VNI)       |    Reserved   |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |                    Variable Length Options                    |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Option Header:
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |          Option Class         |      Type     |R|R|R| Length  |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |                      Variable Option Data                     |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+typedef struct GeneveHdr {
+    /* Length of options fields in int32 excluding the common header */
+    UINT32   optLen : 6;
+    /* Version. */
+    UINT32   version:2;
+    /* Reserved. */
+    UINT32   reserved1 : 6;
+    /* Critical options present */
+    UINT32   critical : 1;
+    /* This packet contains a control message instead of a data payload */
+    UINT32   oam:1;
+    /* Protocol Type. */
+    UINT32   protocol:16;
+    /* VNI */
+    UINT32   vni:24;
+    /* Reserved. */
+    UINT32   reserved2:8;
+} GeneveHdr;
+
+typedef struct GeneveOptionHdr {
+    /* Namespace for the 'type' field. */
+    UINT32   optionClass:16;
+    /* Format of data contained in the option. */
+    UINT32   type:8;
+    /* Reserved. */
+    UINT32   reserved:3;
+    /* Length of option in int32 excluding the option header. */
+    UINT32   length:5;
+} GeneveOptionHdr;
+
+#define GENEVE_CRIT_OPT_TYPE (1 << 7)
+
+NTSTATUS OvsInitGeneveTunnel(POVS_VPORT_ENTRY vport,
+                             UINT16 udpDestPort);
+
+VOID OvsCleanupGeneveTunnel(POVS_VPORT_ENTRY vport);
+
+
+NDIS_STATUS OvsEncapGeneve(POVS_VPORT_ENTRY vport,
+                           PNET_BUFFER_LIST curNbl,
+                           OvsIPv4TunnelKey *tunKey,
+                           POVS_SWITCH_CONTEXT switchContext,
+                           POVS_PACKET_HDR_INFO layers,
+                           PNET_BUFFER_LIST *newNbl);
+
+NDIS_STATUS OvsDecapGeneve(POVS_SWITCH_CONTEXT switchContext,
+                           PNET_BUFFER_LIST curNbl,
+                           OvsIPv4TunnelKey *tunKey,
+                           PNET_BUFFER_LIST *newNbl);
+
+static __inline UINT32
+OvsGetGeneveTunHdrMinSize(VOID)
+{
+    /* XXX: Can L2 include VLAN at all? */
+    return sizeof (EthHdr) + sizeof (IPHdr) + sizeof (UDPHdr) +
+           sizeof (GeneveHdr);
+}
+
+static __inline UINT32
+OvsGetGeneveTunHdrMaxSize(VOID)
+{
+    /* XXX: Can L2 include VLAN at all? */
+    return OvsGetGeneveTunHdrMinSize() + TUN_OPT_MAX_LEN;
+}
+
+#define GENEVE_UDP_PORT 6081
+#define GENEVE_UDP_PORT_NBO 0xC117
+#define GENEVE_VER 0
+#define GENEVE_DEFAULT_TTL 64
+#define GENEVE_ID_IS_VALID(geneveID) (0 < (geneveID) && (vxlanID) <= 0xffffff)
+#define GENEVE_TUNNELID_TO_VNI(_tID)   (UINT32)(((UINT64)(_tID)) >> 40)
+#define GENEVE_VNI_TO_TUNNELID(_vni) (((UINT64)(_vni)) << 40)
+#define ETH_P_TEB_NBO       0x5865          /* Trans Ether Bridging */
+
+#endif /* __GENEVE_H_ */
diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h
index bcd38dd..e666e74 100644
--- a/datapath-windows/ovsext/Util.h
+++ b/datapath-windows/ovsext/Util.h
@@ -38,6 +38,7 @@
 #define OVS_TUNFLT_POOL_TAG             'WSVO'
 #define OVS_RECIRC_POOL_TAG             'CSVO'
 #define OVS_CT_POOL_TAG                 'CTVO'
+#define OVS_GENEVE_POOL_TAG             'GNVO'

 VOID *OvsAllocateMemory(size_t size);
 VOID *OvsAllocateMemoryWithTag(size_t size, ULONG tag);
diff --git a/datapath-windows/ovsext/Vport.c b/datapath-windows/ovsext/Vport.c
index b69360e..1462453 100644
--- a/datapath-windows/ovsext/Vport.c
+++ b/datapath-windows/ovsext/Vport.c
@@ -27,6 +27,7 @@
 #include "User.h"
 #include "Vport.h"
 #include "Vxlan.h"
+#include "Geneve.h"

 #ifdef OVS_DBG_MOD
 #undef OVS_DBG_MOD
@@ -1075,6 +1076,9 @@ OvsInitTunnelVport(PVOID userContext,
     case OVS_VPORT_TYPE_STT:
         status = OvsInitSttTunnel(vport, dstPort);
         break;
+    case OVS_VPORT_TYPE_GENEVE:
+        status = OvsInitGeneveTunnel(vport, dstPort);
+        break;
     default:
         ASSERT(0);
     }
@@ -1218,6 +1222,7 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
     case OVS_VPORT_TYPE_GRE:
     case OVS_VPORT_TYPE_VXLAN:
     case OVS_VPORT_TYPE_STT:
+    case OVS_VPORT_TYPE_GENEVE:
     {
         UINT16 dstPort = GetPortFromPriv(vport);
         hash = OvsJhashBytes(&dstPort,
@@ -1301,6 +1306,9 @@ OvsRemoveAndDeleteVport(PVOID usrParamsContext,
             return status;
         }
     }
+    case OVS_VPORT_TYPE_GENEVE:
+        OvsCleanupGeneveTunnel(vport);
+        break;
     case OVS_VPORT_TYPE_STT:
         OvsCleanupSttTunnel(vport);
         break;
@@ -1362,9 +1370,7 @@ OvsRemoveAndDeleteVport(PVOID usrParamsContext,
         InitializeListHead(&vport->ovsNameLink);
         RemoveEntryList(&vport->portNoLink);
         InitializeListHead(&vport->portNoLink);
-        if (OVS_VPORT_TYPE_VXLAN == vport->ovsType ||
-            OVS_VPORT_TYPE_STT == vport->ovsType   ||
-            OVS_VPORT_TYPE_GRE == vport->ovsType) {
+        if (OvsIsTunnelVportType(vport->ovsType)) {
             RemoveEntryList(&vport->tunnelVportLink);
             InitializeListHead(&vport->tunnelVportLink);
         }
@@ -2255,7 +2261,7 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,

         if (OvsIsTunnelVportType(portType)) {
             UINT16 transportPortDest = 0;
-            UINT8 nwProto;
+            UINT8 nwProto = IPPROTO_NONE;
             POVS_VPORT_ENTRY dupVport;

             switch (portType) {
@@ -2266,6 +2272,9 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                 transportPortDest = VXLAN_UDP_PORT;
                 nwProto = IPPROTO_UDP;
                 break;
+            case OVS_VPORT_TYPE_GENEVE:
+                transportPortDest = GENEVE_UDP_PORT;
+                break;
             case OVS_VPORT_TYPE_STT:
                 transportPortDest = STT_TCP_PORT;
                 nwProto = IPPROTO_TCP;
@@ -2393,6 +2402,9 @@ Cleanup:
                     case OVS_VPORT_TYPE_STT:
                         OvsCleanupSttTunnel(vport);
                         break;
+                    case OVS_VPORT_TYPE_GENEVE:
+                        OvsCleanupGeneveTunnel(vport);
+                        break;
                     default:
                         ASSERT(!"Invalid tunnel port type");
                     }
diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj
index 0356ddf..02fa60c 100644
--- a/datapath-windows/ovsext/ovsext.vcxproj
+++ b/datapath-windows/ovsext/ovsext.vcxproj
@@ -81,6 +81,7 @@
     <ClInclude Include="Ethernet.h" />
     <ClInclude Include="Event.h" />
     <ClInclude Include="Flow.h" />
+    <ClInclude Include="Geneve.h" />
     <ClInclude Include="Gre.h" />
     <ClInclude Include="IpHelper.h" />
     <ClInclude Include="Jhash.h" />
@@ -182,6 +183,7 @@
     <ClCompile Include="Driver.c" />
     <ClCompile Include="Event.c" />
     <ClCompile Include="Flow.c" />
+    <ClCompile Include="Geneve.c" />
     <ClCompile Include="Gre.c" />
     <ClCompile Include="IpHelper.c" />
     <ClCompile Include="Jhash.c" />
