datapath-windows: Solved BSOD when cleaning up the VXLAN tunnel
[cascardo/ovs.git] / datapath-windows / ovsext / Vxlan.c
index 3a1291c..b84c1d0 100644 (file)
@@ -26,7 +26,6 @@
 #include "PacketIO.h"
 #include "Flow.h"
 #include "PacketParser.h"
-#include "Checksum.h"
 
 #pragma warning( push )
 #pragma warning( disable:4127 )
 /* Move to a header file */
 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
 
+/*
+ *----------------------------------------------------------------------------
+ * This function verifies if the VXLAN tunnel already exists, in order to
+ * avoid sending a duplicate request to the WFP base filtering engine.
+ *----------------------------------------------------------------------------
+ */
+static BOOLEAN
+OvsIsTunnelFilterCreated(POVS_SWITCH_CONTEXT switchContext,
+                         UINT16 udpPortDest)
+{
+    for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) {
+        PLIST_ENTRY head, link, next;
+
+        head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]);
+        LIST_FORALL_SAFE(head, link, next) {
+            POVS_VPORT_ENTRY vport = NULL;
+            POVS_VXLAN_VPORT vxlanPort = NULL;
+            vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink);
+            vxlanPort = (POVS_VXLAN_VPORT)vport->priv;
+            if (vxlanPort) {
+                if ((udpPortDest == vxlanPort->dstPort)) {
+                    /* The VXLAN tunnel was already created. */
+                    return TRUE;
+                }
+            }
+        }
+    }
+
+    return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * This function allocates and initializes the OVS_VXLAN_VPORT. The function
+ * also creates a WFP tunnel filter for the necessary destination port. The
+ * tunnel filter create request is passed to the tunnel filter threads that
+ * will complete the request at a later time when IRQL is lowered to
+ * PASSIVE_LEVEL.
+ *
+ * udpDestPort: the vxlan is set as payload to a udp frame. If the destination
+ * port of an udp frame is udpDestPort, we understand it to be vxlan.
+ *----------------------------------------------------------------------------
+ */
 NTSTATUS
-OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
-                   POVS_VPORT_ADD_REQUEST addReq)
+OvsInitVxlanTunnel(PIRP irp,
+                   POVS_VPORT_ENTRY vport,
+                   UINT16 udpDestPort,
+                   PFNTunnelVportPendingOp callback,
+                   PVOID tunnelContext)
 {
-    POVS_VXLAN_VPORT vxlanPort;
     NTSTATUS status = STATUS_SUCCESS;
+    POVS_VXLAN_VPORT vxlanPort = NULL;
 
-    ASSERT(addReq->type == OVSWIN_VPORT_TYPE_VXLAN);
-
-    vxlanPort = OvsAllocateMemory(sizeof (*vxlanPort));
+    vxlanPort = OvsAllocateMemoryWithTag(sizeof (*vxlanPort),
+                                         OVS_VXLAN_POOL_TAG);
     if (vxlanPort == NULL) {
-        status =  STATUS_INSUFFICIENT_RESOURCES;
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlZeroMemory(vxlanPort, sizeof(*vxlanPort));
+    vxlanPort->dstPort = udpDestPort;
+    vport->priv = (PVOID)vxlanPort;
+
+    if (!OvsIsTunnelFilterCreated(gOvsSwitchContext, udpDestPort)) {
+        status = OvsTunelFilterCreate(irp,
+                                      udpDestPort,
+                                      &vxlanPort->filterID,
+                                      callback,
+                                      tunnelContext);
     } else {
-        RtlZeroMemory(vxlanPort, sizeof (*vxlanPort));
-        vxlanPort->dstPort = addReq->dstPort;
-        /*
-         * since we are installing the WFP filter before the port is created
-         * We need to check if it is the same number
-         * XXX should be removed later
-         */
-        ASSERT(vxlanPort->dstPort == VXLAN_UDP_PORT);
-        vport->priv = (PVOID)vxlanPort;
+        status = STATUS_OBJECT_NAME_EXISTS;
     }
+
     return status;
 }
 
-
-VOID
-OvsCleanupVxlanTunnel(POVS_VPORT_ENTRY vport)
+/*
+ *----------------------------------------------------------------------------
+ * This function releases the OVS_VXLAN_VPORT. The function also deletes the
+ * WFP tunnel filter previously created. The tunnel filter delete request is
+ * passed to the tunnel filter threads that will complete the request at a
+ * later time when IRQL is lowered to PASSIVE_LEVEL.
+ *----------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsCleanupVxlanTunnel(PIRP irp,
+                      POVS_VPORT_ENTRY vport,
+                      PFNTunnelVportPendingOp callback,
+                      PVOID tunnelContext)
 {
-    if (vport->ovsType != OVSWIN_VPORT_TYPE_VXLAN ||
+    NTSTATUS status = STATUS_SUCCESS;
+    POVS_VXLAN_VPORT vxlanPort = NULL;
+
+    if (vport->ovsType != OVS_VPORT_TYPE_VXLAN ||
         vport->priv == NULL) {
-        return;
+        return STATUS_SUCCESS;
     }
 
-    OvsFreeMemory(vport->priv);
-    vport->priv = NULL;
+    vxlanPort = (POVS_VXLAN_VPORT)vport->priv;
+
+    if (vxlanPort->filterID != 0) {
+        status = OvsTunelFilterDelete(irp,
+                                      vxlanPort->filterID,
+                                      callback,
+                                      tunnelContext);
+    } else {
+        OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG);
+        vport->priv = NULL;
+    }
+
+    return status;
 }
 
 
@@ -97,7 +170,8 @@ OvsCleanupVxlanTunnel(POVS_VPORT_ENTRY vport)
  *----------------------------------------------------------------------------
  */
 static __inline NDIS_STATUS
-OvsDoEncapVxlan(PNET_BUFFER_LIST curNbl,
+OvsDoEncapVxlan(POVS_VPORT_ENTRY vport,
+                PNET_BUFFER_LIST curNbl,
                 OvsIPv4TunnelKey *tunKey,
                 POVS_FWD_INFO fwdInfo,
                 POVS_PACKET_HDR_INFO layers,
@@ -112,6 +186,7 @@ OvsDoEncapVxlan(PNET_BUFFER_LIST curNbl,
     IPHdr *ipHdr;
     UDPHdr *udpHdr;
     VXLANHdr *vxlanHdr;
+    POVS_VXLAN_VPORT vportVxlan;
     UINT32 headRoom = OvsGetVxlanTunHdrSize();
     UINT32 packetLength;
 
@@ -138,6 +213,10 @@ OvsDoEncapVxlan(PNET_BUFFER_LIST curNbl,
             }
         }
     }
+
+    vportVxlan = (POVS_VXLAN_VPORT) GetOvsVportPriv(vport);
+    ASSERT(vportVxlan);
+
     /* If we didn't split the packet above, make a copy now */
     if (*newNbl == NULL) {
         *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom,
@@ -171,10 +250,10 @@ OvsDoEncapVxlan(PNET_BUFFER_LIST curNbl,
 
         /* L2 header */
         ethHdr = (EthHdr *)bufferStart;
-        NdisMoveMemory(ethHdr->Destination, fwdInfo->dstMacAddr,
-                       sizeof ethHdr->Destination + sizeof ethHdr->Source);
         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);
 
         // XXX: question: there are fields in the OvsIPv4TunnelKey for ttl and such,
@@ -201,7 +280,7 @@ OvsDoEncapVxlan(PNET_BUFFER_LIST curNbl,
         /* UDP header */
         udpHdr = (UDPHdr *)((PCHAR)ipHdr + sizeof *ipHdr);
         udpHdr->source = htons(tunKey->flow_hash | 32768);
-        udpHdr->dest = VXLAN_UDP_PORT_NBO;
+        udpHdr->dest = htons(vportVxlan->dstPort);
         udpHdr->len = htons(NET_BUFFER_DATA_LENGTH(curNb) - headRoom +
                             sizeof *udpHdr + sizeof *vxlanHdr);
         udpHdr->check = 0;
@@ -235,16 +314,15 @@ ret_error:
  *----------------------------------------------------------------------------
  */
 NDIS_STATUS
-OvsEncapVxlan(PNET_BUFFER_LIST curNbl,
+OvsEncapVxlan(POVS_VPORT_ENTRY vport,
+              PNET_BUFFER_LIST curNbl,
               OvsIPv4TunnelKey *tunKey,
               POVS_SWITCH_CONTEXT switchContext,
-              VOID *completionList,
               POVS_PACKET_HDR_INFO layers,
               PNET_BUFFER_LIST *newNbl)
 {
     NTSTATUS status;
     OVS_FWD_INFO fwdInfo;
-    UNREFERENCED_PARAMETER(completionList);
 
     status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo);
     if (status != STATUS_SUCCESS) {
@@ -260,48 +338,10 @@ OvsEncapVxlan(PNET_BUFFER_LIST curNbl,
         return NDIS_STATUS_FAILURE;
     }
 
-    return OvsDoEncapVxlan(curNbl, tunKey, &fwdInfo, layers,
+    return OvsDoEncapVxlan(vport, curNbl, tunKey, &fwdInfo, layers,
                            switchContext, newNbl);
 }
 
-
-/*
- *----------------------------------------------------------------------------
- * OvsIpHlprCbVxlan --
- *     Callback function for IP helper.
- *     XXX: not used currently
- *----------------------------------------------------------------------------
- */
-static VOID
-OvsIpHlprCbVxlan(PNET_BUFFER_LIST curNbl,
-                 UINT32 inPort,
-                 OvsIPv4TunnelKey *tunKey,
-                 PVOID cbData1,
-                 PVOID cbData2,
-                 NTSTATUS result,
-                 POVS_FWD_INFO fwdInfo)
-{
-    OVS_PACKET_HDR_INFO layers;
-    OvsFlowKey key;
-    NDIS_STATUS status;
-    UNREFERENCED_PARAMETER(inPort);
-
-    status = OvsExtractFlow(curNbl, inPort, &key, &layers, NULL);
-    if (result == STATUS_SUCCESS) {
-        status = OvsDoEncapVxlan(curNbl, tunKey, fwdInfo, &layers,
-                (POVS_SWITCH_CONTEXT)cbData1, NULL);
-    } else {
-        status = NDIS_STATUS_FAILURE;
-    }
-
-    if (status != NDIS_STATUS_SUCCESS) {
-        // XXX: Free up the NBL;
-        return;
-    }
-
-    OvsLookupFlowOutput((POVS_SWITCH_CONTEXT)cbData1, cbData2, curNbl);
-}
-
 /*
  *----------------------------------------------------------------------------
  * OvsCalculateUDPChecksum
@@ -347,15 +387,15 @@ OvsCalculateUDPChecksum(PNET_BUFFER_LIST curNbl,
 
 /*
  *----------------------------------------------------------------------------
- * OvsDoDecapVxlan
+ * OvsDecapVxlan
  *     Decapsulates to tunnel header in 'curNbl' and puts into 'tunKey'.
  *----------------------------------------------------------------------------
  */
 NDIS_STATUS
-OvsDoDecapVxlan(POVS_SWITCH_CONTEXT switchContext,
-                PNET_BUFFER_LIST curNbl,
-                OvsIPv4TunnelKey *tunKey,
-                PNET_BUFFER_LIST *newNbl)
+OvsDecapVxlan(POVS_SWITCH_CONTEXT switchContext,
+              PNET_BUFFER_LIST curNbl,
+              OvsIPv4TunnelKey *tunKey,
+              PNET_BUFFER_LIST *newNbl)
 {
     PNET_BUFFER curNb;
     PMDL curMdl;
@@ -473,9 +513,6 @@ OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet,
             break;
         }
 
-        /* XXX Should be tested against the dynamic port # in the VXLAN vport */
-        ASSERT(udp->dest == RtlUshortByteSwap(VXLAN_UDP_PORT));
-
         VxlanHeader = (VXLANHdr *)OvsGetPacketBytes(packet,
                                                     sizeof(*VxlanHeader),
                                                     layers.l7Offset,