The kernel datapath supports only port 4789 for VXLAN tunnel creation.
Added support in order to allow for the VXLAN tunnel port to be
configurable to any port number set by the userspace.

The patch also checks to see if an existing WFP filter, for the
necessary UDP tunnel port, is already created before adding a new one.
This is a double check, because currently the userspace also verifies
this, but it is necessary to avoid future issues.

Custom VXLAN tunnel port requires the addition of a new WFP filter
with the new UDP tunnel port. The creation of a new WFP filter is
triggered in OvsInitVxlanTunnel function and the removal of the WFP
filter in OvsCleanupVxlanTunnel function.
But the latter functions are running at IRQL = DISPATCH_LEVEL, due
to the NDIS RW lock acquisition, and all WFP calls must be running at
IRQL = PASSIVE_LEVEL. This is why I have created a system thread which
records all filter addition/removal requests into a list for later
processing by the system thread. The ThreadStart routine processes all
received requests at IRQL = PASSIVE_LEVEL, which is the required IRQL
for the necessary WFP calls for adding/removal of the WFP filters.

The WFP filter for the default VXLAN port 4789 is not added anymore at
filter attach. All WFP filters for the tunnel ports are added when the
tunnel ports are initialized and are removed at cleanup.

It is necessary that OvsTunnelFilterUninitialize function is called
after OvsClearAllSwitchVports in order to allow for the added WFP
filters to be removed. OvsTunnelFilterUninitialize function closes the
global engine handle used by most of the WFP calls, including filter
removal.

Signed-off-by: Sorin Vinturis <[email protected]>
Reported-by: Alin Gabriel Serdean <[email protected]>
Reported-at: https://github.com/openvswitch/ovs-issues/issues/66
---
 datapath-windows/ovsext/Switch.c       |   2 +-
 datapath-windows/ovsext/Tunnel.h       |   6 -
 datapath-windows/ovsext/TunnelFilter.c | 484 ++++++++++++++++++++++++++++-----
 datapath-windows/ovsext/TunnelIntf.h   |   4 +
 datapath-windows/ovsext/Vport.c        |  13 +-
 datapath-windows/ovsext/Vxlan.c        |  53 +++-
 datapath-windows/ovsext/Vxlan.h        |   2 +-
 7 files changed, 473 insertions(+), 91 deletions(-)

diff --git a/datapath-windows/ovsext/Switch.c b/datapath-windows/ovsext/Switch.c
index a228d8e..cf5e3c4 100644
--- a/datapath-windows/ovsext/Switch.c
+++ b/datapath-windows/ovsext/Switch.c
@@ -261,8 +261,8 @@ OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
     if (switchContext)
     {
         dpNo = switchContext->dpNo;
-        OvsTunnelFilterUninitialize(gOvsExtDriverObject);
         OvsClearAllSwitchVports(switchContext);
+        OvsTunnelFilterUninitialize(gOvsExtDriverObject);
         OvsUninitSwitchContext(switchContext);
         OvsFreeMemory(switchContext);
     }
diff --git a/datapath-windows/ovsext/Tunnel.h b/datapath-windows/ovsext/Tunnel.h
index 2978bb3..2c45e35 100644
--- a/datapath-windows/ovsext/Tunnel.h
+++ b/datapath-windows/ovsext/Tunnel.h
@@ -32,12 +32,6 @@ typedef struct OVS_TUNNEL_PENDED_PACKET_
    FWPS_CLASSIFY_OUT *classifyOut;
 } OVS_TUNNEL_PENDED_PACKET;
 
-/* Shared global data. */
-
-extern UINT16 configNewDestPort;
-
-extern UINT32 gCalloutIdV4;
-
 //
 // Shared function prototypes
 //
diff --git a/datapath-windows/ovsext/TunnelFilter.c 
b/datapath-windows/ovsext/TunnelFilter.c
index e0adc37..b4289e6 100644
--- a/datapath-windows/ovsext/TunnelFilter.c
+++ b/datapath-windows/ovsext/TunnelFilter.c
@@ -63,9 +63,6 @@
 /* The session name isn't required but it's useful for diagnostics. */
 #define OVS_TUNNEL_SESSION_NAME         L"OVS tunnel session"
 
-/* Configurable parameters (addresses and ports are in host order) */
-UINT16   configNewDestPort = VXLAN_UDP_PORT;
-
 /*
  * Callout and sublayer GUIDs
  */
@@ -105,14 +102,57 @@ DEFINE_GUID(
     0xa5, 0x36, 0x1e, 0xed, 0xb9, 0xe9, 0xba, 0x6a
     );
 
+KSTART_ROUTINE  OvsTunnelFilterThreadProc;
+NTSTATUS        OvsTunnelFilterThreadInit();
+VOID            OvsTunnelFilterThreadStop();
+VOID            OvsTunnelFilterThreadCleanup();
+
 /*
- * Callout driver global variables
+ * Callout driver type definitions
  */
-PDEVICE_OBJECT gDeviceObject;
-
-HANDLE gEngineHandle = NULL;
-UINT32 gCalloutIdV4;
+typedef enum _OVS_TUNFLT_OPERATION {
+    OVS_TUN_FILTER_CREATE = 0,
+    OVS_TUN_FILTER_DELETE
+} OVS_TUNFLT_OPERATION;
+
+typedef struct _OVS_TUNFLT_REQUEST {
+    LIST_ENTRY              entry;
+    /* Tunnel filter destination port. */
+    UINT16                  port;
+    /* Tunnel filter identification. */
+    UINT64                  ID;
+    /* Requested operation to be performed. */
+    OVS_TUNFLT_OPERATION    operation;
+    /* Context used to return filter ID to the caller. */
+    PVOID                   context;
+} OVS_TUNFLT_REQUEST, *POVS_TUNFLT_REQUEST;
+
+typedef struct _OVS_TUNFLT_REQUEST_LIST {
+    /* SpinLock for syncronizing access to the requests list. */
+    NDIS_SPIN_LOCK  spinlock;
+    /* Head of the requests list. */
+    LIST_ENTRY      head;
+    /* Number of OVS_TUNFLT_REQUEST entries in the list. */
+    UINT64          numEntries;
+} OVS_TUNFLT_REQUEST_LIST, *POVS_TUNFLT_REQUEST_LIST;
+
+typedef struct _OVS_TUNFLT_THREAD_CONTEXT {
+    /* Reference of the thread object. */
+    PVOID   threadObject;
+    /* Event signaling that there are requests to process. */
+    KEVENT  requestEvent;
+    /* Event for stopping thread execution. */
+    KEVENT  stopEvent;
+} OVS_TUNFLT_THREAD_CONTEXT, *POVS_TUNFLT_THREAD_CONTEXT;
 
+/*
+ * Callout driver global variables
+ */
+PDEVICE_OBJECT              gDeviceObject = NULL;
+HANDLE                      gEngineHandle = NULL;
+UINT32                      gCalloutIdV4 = 0;
+OVS_TUNFLT_REQUEST_LIST     gTunnelRequestList = { 0 };
+OVS_TUNFLT_THREAD_CONTEXT   gTunnelThreadCtx = { 0 };
 
 /* Callout driver implementation */
 
@@ -239,7 +279,8 @@ OvsTunnelAddFilter(PWSTR filterName,
                    UINT64 context,
                    const GUID *filterKey,
                    const GUID *layerKey,
-                   const GUID *calloutKey)
+                   const GUID *calloutKey,
+                   UINT64 *filterID)
 {
     NTSTATUS status = STATUS_SUCCESS;
     FWPM_FILTER filter = {0};
@@ -249,7 +290,9 @@ OvsTunnelAddFilter(PWSTR filterName,
     UNREFERENCED_PARAMETER(remotePort);
     UNREFERENCED_PARAMETER(direction);
 
-    filter.filterKey = *filterKey;
+    if (filterKey) {
+        filter.filterKey = *filterKey;
+    }
     filter.layerKey = *layerKey;
     filter.displayData.name = (wchar_t*)filterName;
     filter.displayData.description = (wchar_t*)filterDesc;
@@ -282,54 +325,8 @@ OvsTunnelAddFilter(PWSTR filterName,
     status = FwpmFilterAdd(gEngineHandle,
                            &filter,
                            NULL,
-                           NULL);
-
-    return status;
-}
-
-NTSTATUS
-OvsTunnelRemoveFilter(const GUID *filterKey,
-                      const GUID *sublayerKey)
-{
-    NTSTATUS status = STATUS_SUCCESS;
-    BOOLEAN inTransaction = FALSE;
-
-    do {
-        status = FwpmTransactionBegin(gEngineHandle, 0);
-        if (!NT_SUCCESS(status)) {
-            break;
-        }
+                           filterID);
 
-        inTransaction = TRUE;
-
-        /*
-         * We have to delete the filter first since it references the
-         * sublayer. If we tried to delete the sublayer first, it would fail
-         * with FWP_ERR_IN_USE.
-         */
-        status = FwpmFilterDeleteByKey(gEngineHandle,
-                                       filterKey);
-        if (!NT_SUCCESS(status)) {
-            break;
-        }
-
-        status = FwpmSubLayerDeleteByKey(gEngineHandle,
-                                         sublayerKey);
-        if (!NT_SUCCESS(status)) {
-            break;
-        }
-
-        status = FwpmTransactionCommit(gEngineHandle);
-        if (!NT_SUCCESS(status)){
-            break;
-        }
-
-        inTransaction = FALSE;
-    } while (inTransaction);
-
-    if (inTransaction) {
-        FwpmTransactionAbort(gEngineHandle);
-    }
     return status;
 }
 
@@ -388,15 +385,6 @@ OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
         goto Exit;
     }
 
-    status = OvsTunnelAddFilter(L"Datagram-Data OVS Filter (Inbound)",
-                                L"address/port for UDP",
-                                configNewDestPort,
-                                FWP_DIRECTION_INBOUND,
-                                0,
-                                &OVS_TUNNEL_FILTER_KEY,
-                                layerKey,
-                                calloutKey);
-
 Exit:
 
     if (!NT_SUCCESS(status)){
@@ -487,8 +475,8 @@ Exit:
 VOID
 OvsTunnelUnregisterCallouts(VOID)
 {
-    OvsTunnelRemoveFilter(&OVS_TUNNEL_FILTER_KEY,
-                          &OVS_TUNNEL_SUBLAYER);
+    FwpmSubLayerDeleteByKey(gEngineHandle,
+                            &OVS_TUNNEL_SUBLAYER);
     FwpsCalloutUnregisterById(gCalloutIdV4);
     FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
     OvsTunnelEngineClose(&gEngineHandle);
@@ -499,6 +487,7 @@ OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
 {
     UNREFERENCED_PARAMETER(driverObject);
 
+    OvsTunnelFilterThreadStop();
     OvsTunnelUnregisterCallouts();
     IoDeleteDevice(gDeviceObject);
 }
@@ -525,6 +514,11 @@ OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
         goto Exit;
     }
 
+    status = OvsTunnelFilterThreadInit();
+    if (!NT_SUCCESS(status)){
+        goto Exit;
+    }
+
     status = OvsTunnelRegisterCallouts(gDeviceObject);
 
 Exit:
@@ -541,3 +535,357 @@ Exit:
 
     return status;
 }
+
+NTSTATUS
+OvsTunnelAddFilterEx(UINT32 filterPort,
+                     UINT64 *filterID)
+{
+    return OvsTunnelAddFilter(L"Datagram-Data OVS Filter (Inbound)",
+                              L"address/port for UDP",
+                              (USHORT)filterPort,
+                              FWP_DIRECTION_INBOUND,
+                              0,
+                              NULL,
+                              &FWPM_LAYER_DATAGRAM_DATA_V4,
+                              &OVS_TUNNEL_CALLOUT_V4,
+                              filterID);
+}
+
+NTSTATUS
+OvsTunnelRemoveFilterEx(UINT64 filterID)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    BOOLEAN  error = TRUE;
+
+    do {
+        if (0 == filterID) {
+            OVS_LOG_INFO("No tunnel filter to remove.");
+            break;
+        }
+
+        status = FwpmFilterDeleteById(gEngineHandle, filterID);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR("Failed to remove tunnel filter with ID: %x.",
+                status);
+            break;
+        }
+
+        error = FALSE;
+    } while (error);
+
+    return status;
+}
+
+NTSTATUS
+OvsTunnelFilterExecuteAction(POVS_TUNFLT_REQUEST request)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    switch (request->operation)
+    {
+    case OVS_TUN_FILTER_CREATE:
+        status = OvsTunnelAddFilterEx(request->port,
+                                      (UINT64*)request->context);
+        break;
+    case OVS_TUN_FILTER_DELETE:
+        status = OvsTunnelRemoveFilterEx(request->ID);
+        break;
+    default:
+        break;
+    }
+
+    return status;
+}
+
+POVS_TUNFLT_REQUEST
+OvsTunnelFilterRequestPop()
+{
+    POVS_TUNFLT_REQUEST request = NULL;
+
+    NdisAcquireSpinLock(&gTunnelRequestList.spinlock);
+
+    if (!IsListEmpty(&gTunnelRequestList.head)) {
+        request = (POVS_TUNFLT_REQUEST)
+            RemoveHeadList(&gTunnelRequestList.head);
+
+        gTunnelRequestList.numEntries--;
+    }
+
+    NdisReleaseSpinLock(&gTunnelRequestList.spinlock);
+
+    return request;
+}
+
+VOID
+OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST request)
+{
+    NdisAcquireSpinLock(&gTunnelRequestList.spinlock);
+
+    InsertTailList(&gTunnelRequestList.head,
+                   &(request->entry));
+
+    gTunnelRequestList.numEntries++;
+
+    NdisReleaseSpinLock(&gTunnelRequestList.spinlock);
+}
+
+VOID
+OvsTunnelFilterRequestListCleanup()
+{
+    POVS_TUNFLT_REQUEST request = NULL;
+
+    while (NULL != (request = OvsTunnelFilterRequestPop())) {
+        OvsFreeMemory(request);
+        request = NULL;
+    }
+}
+
+NTSTATUS
+OvsTunnelFilterRequestListProcess()
+{
+    NTSTATUS            status = STATUS_SUCCESS;
+    POVS_TUNFLT_REQUEST request = NULL;
+    BOOLEAN             inTransaction = FALSE;
+
+    do
+    {
+        status = FwpmTransactionBegin(gEngineHandle, 0);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR("Failed to start transaction, status: %x.", status);
+            break;
+        }
+        inTransaction = TRUE;
+
+        while (NULL != (request = OvsTunnelFilterRequestPop())) {
+            status = OvsTunnelFilterExecuteAction(request);
+
+            OvsFreeMemory(request);
+
+            if (!NT_SUCCESS(status)) {
+                break;
+            }
+        }
+        if (!NT_SUCCESS(status)) {
+            break;
+        }
+
+        status = FwpmTransactionCommit(gEngineHandle);
+        if (!NT_SUCCESS(status)){
+            OVS_LOG_ERROR("Failed to commit transaction, status: %x.", status);
+            break;
+        }
+
+        inTransaction = FALSE;
+    } while (inTransaction);
+
+    if (inTransaction) {
+        FwpmTransactionAbort(gEngineHandle);
+    }
+
+    return status;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *  System thread routine that handles tunnel filter create/delete requests.
+ *----------------------------------------------------------------------------
+ */
+_Use_decl_annotations_
+VOID
+OvsTunnelFilterThreadProc(PVOID context)
+{
+    NTSTATUS                   status = STATUS_SUCCESS;
+    POVS_TUNFLT_THREAD_CONTEXT threadCtx =
+        (POVS_TUNFLT_THREAD_CONTEXT)context;
+    BOOLEAN                    exit = FALSE;
+    PKEVENT                    eventArray[2] = { 0 };
+    ULONG                      count = 0;
+
+    OVS_LOG_INFO("Starting OVS Tunnel system thread.");
+
+    eventArray[0] = &threadCtx->stopEvent;
+    eventArray[1] = &threadCtx->requestEvent;
+    count = ARRAY_SIZE(eventArray);
+
+    do
+    {
+        status = KeWaitForMultipleObjects(count,
+                                          (PVOID)eventArray,
+                                          WaitAny,
+                                          Executive,
+                                          KernelMode,
+                                          FALSE,
+                                          NULL,
+                                          NULL);
+        switch (status)
+        {
+        case STATUS_WAIT_1:
+        {
+            status = OvsTunnelFilterRequestListProcess();
+            if (!NT_SUCCESS(status)) {
+                exit = TRUE;
+            }
+            break;
+        }
+        default:
+            exit = TRUE;
+            break;
+        }
+    } while (!exit);
+
+    OvsTunnelFilterThreadCleanup();
+
+    OVS_LOG_INFO("Terminating OVS Tunnel system thread.");
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
+};
+
+NTSTATUS
+OvsTunnelFilterThreadInit()
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    HANDLE      threadHandle = NULL;
+    BOOLEAN     error = TRUE;
+
+    do {
+        InitializeListHead(&gTunnelRequestList.head);
+        NdisAllocateSpinLock(&gTunnelRequestList.spinlock);
+        gTunnelRequestList.numEntries = 0;
+
+        KeInitializeEvent(&gTunnelThreadCtx.stopEvent,
+                          NotificationEvent,
+                          FALSE);
+        KeInitializeEvent(&gTunnelThreadCtx.requestEvent,
+                          SynchronizationEvent,
+                          FALSE);
+
+        status = PsCreateSystemThread(&threadHandle,
+                                      SYNCHRONIZE,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      OvsTunnelFilterThreadProc,
+                                      &gTunnelThreadCtx);
+        if (status) {
+            OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
+                          status);
+            break;
+        }
+
+        ObReferenceObjectByHandle(threadHandle,
+                                  SYNCHRONIZE,
+                                  NULL,
+                                  KernelMode,
+                                  &gTunnelThreadCtx.threadObject,
+                                  NULL);
+        ZwClose(threadHandle);
+        threadHandle = NULL;
+
+        error = FALSE;
+    } while (error);
+
+    if (error) {
+        NdisFreeSpinLock(&gTunnelRequestList.spinlock);
+    }
+
+    return status;
+}
+
+VOID
+OvsTunnelFilterThreadCleanup()
+{
+    OvsTunnelFilterRequestListCleanup();
+    NdisFreeSpinLock(&gTunnelRequestList.spinlock);
+}
+
+VOID
+OvsTunnelFilterThreadStop()
+{
+    /* Signal stop thread event. */
+    KeSetEvent(&gTunnelThreadCtx.stopEvent, IO_NO_INCREMENT, FALSE);
+
+    /* Wait for the tunnel thread to finish. */
+    KeWaitForSingleObject(gTunnelThreadCtx.threadObject,
+                          Executive,
+                          KernelMode,
+                          FALSE,
+                          NULL);
+
+    ObDereferenceObject(gTunnelThreadCtx.threadObject);
+}
+
+VOID
+OvsTunnelFilterQueueRequest(UINT16 remotePort,
+                            UINT64 *filterID,
+                            OVS_TUNFLT_OPERATION operation)
+{
+    POVS_TUNFLT_REQUEST request = NULL;
+    BOOLEAN             error = TRUE;
+
+    do {
+        if (NULL == filterID) {
+            OVS_LOG_ERROR("Invalid request.");
+            break;
+        }
+
+        request = (POVS_TUNFLT_REQUEST)OvsAllocateMemory(sizeof(*request));
+        if (NULL == request) {
+            OVS_LOG_ERROR("Failed to allocate list item.");
+            break;
+        }
+
+        request->port = remotePort;
+        request->operation = operation;
+        request->ID = *filterID;
+        request->context = (PVOID)filterID;
+
+        OvsTunnelFilterRequestPush(request);
+
+        KeSetEvent(&gTunnelThreadCtx.requestEvent, IO_NO_INCREMENT, FALSE);
+
+        error = FALSE;
+    } while (error);
+
+    if (error) {
+        if (request) {
+            OvsFreeMemory(request);
+            request = NULL;
+        }
+    }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  This function adds a new WFP filter for the received port and returns the
+ *  ID of the created WFP filter.
+ *
+ *  Note:
+ *  All necessary calls to the WFP filtering engine must be running at IRQL =
+ *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
+ *  we register an OVS_TUN_FILTER_CREATE request that will be processed by
+ *  the thread routine at IRQL = PASSIVE_LEVEL.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsTunelFilterCreate(UINT16 filterPort,
+                     UINT64 *filterID)
+{
+    OvsTunnelFilterQueueRequest(filterPort, filterID, OVS_TUN_FILTER_CREATE);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  This function removes a WFP filter using the received filter ID.
+ *
+ *  Note:
+ *  All necessary calls to the WFP filtering engine must be running at IRQL =
+ *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
+ *  we register an OVS_TUN_FILTER_DELETE request that will be processed by
+ *  the thread routine at IRQL = PASSIVE_LEVEL.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsTunelFilterDelete(UINT64 filterID)
+{
+    OvsTunnelFilterQueueRequest(0, &filterID, OVS_TUN_FILTER_DELETE);
+}
\ No newline at end of file
diff --git a/datapath-windows/ovsext/TunnelIntf.h 
b/datapath-windows/ovsext/TunnelIntf.h
index b2bba30..d304f49 100644
--- a/datapath-windows/ovsext/TunnelIntf.h
+++ b/datapath-windows/ovsext/TunnelIntf.h
@@ -30,4 +30,8 @@ VOID OvsTunnelAddSystemProvider(HANDLE handle);
 
 VOID OvsTunnelRemoveSystemProvider(HANDLE handle);
 
+VOID OvsTunelFilterCreate(UINT16 filterPort, UINT64 *filterID);
+
+VOID OvsTunelFilterDelete(UINT64 filterID);
+
 #endif /* __TUNNEL_INTF_H_ */
diff --git a/datapath-windows/ovsext/Vport.c b/datapath-windows/ovsext/Vport.c
index c9dfaea..968c112 100644
--- a/datapath-windows/ovsext/Vport.c
+++ b/datapath-windows/ovsext/Vport.c
@@ -1011,7 +1011,7 @@ InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext,
 
     switch(vport->ovsType) {
     case OVS_VPORT_TYPE_VXLAN:
-        ASSERT(switchContext->vxlanVport == NULL);
+        //ASSERT(switchContext->vxlanVport == NULL);
         switchContext->vxlanVport = vport;
         switchContext->numNonHvVports++;
         break;
@@ -1908,7 +1908,7 @@ Cleanup:
 
 /*
  * --------------------------------------------------------------------------
- *  Command Handler for 'OVS_VPORT_CMD_NEW'.
+ *  Command Handler for 'OVS_VPORT_CMD_GET'.
  *
  *  The function handles the initial call to setup the dump state, as well as
  *  subsequent calls to continue dumping data.
@@ -2033,8 +2033,8 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT 
usrParamsCtx,
     } else {
         ASSERT(OvsIsTunnelVportType(portType) ||
                (portType == OVS_VPORT_TYPE_INTERNAL && isBridgeInternal));
-        ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
-               !OvsIsTunnelVportType(portType));
+        //ASSERT(OvsGetTunnelVport(gOvsSwitchContext, portType) == NULL ||
+        //       !OvsIsTunnelVportType(portType));
 
         vport = (POVS_VPORT_ENTRY)OvsAllocateVport();
         if (vport == NULL) {
@@ -2044,7 +2044,10 @@ OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT 
usrParamsCtx,
         vportAllocated = TRUE;
 
         if (OvsIsTunnelVportType(portType)) {
-            status = OvsInitTunnelVport(vport, portType, VXLAN_UDP_PORT);
+            PNL_ATTR attr = 
NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS],
+                                             OVS_TUNNEL_ATTR_DST_PORT);
+            UINT16 udpPortDest = NlAttrGetU16(attr);
+            status = OvsInitTunnelVport(vport, portType, udpPortDest);
             nlError = NlMapStatusToNlErr(status);
         } else {
             OvsInitBridgeInternalVport(vport);
diff --git a/datapath-windows/ovsext/Vxlan.c b/datapath-windows/ovsext/Vxlan.c
index 1ce5af2..8981bf5 100644
--- a/datapath-windows/ovsext/Vxlan.c
+++ b/datapath-windows/ovsext/Vxlan.c
@@ -49,6 +49,39 @@
 /* Move to a header file */
 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
 
+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) &&
+                    (0 != vxlanPort->filterID)) {
+                    /*
+                     * VXLAN destination port matching is not enough to decide
+                     * if the WFP filter was created or not, because the newly
+                     * allocated VXLAN vport is already added to the
+                     * portNoHashArray in InitOvsVportCommon function. If the
+                     * filterID of the VXLAN vport is zero it means that the
+                     * WFP filter is not created yet.
+                     */
+                    return TRUE;
+                }
+            }
+        }
+    }
+
+    return FALSE;
+}
+
 /*
  * 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.
@@ -57,7 +90,7 @@ NTSTATUS
 OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
                    UINT16 udpDestPort)
 {
-    POVS_VXLAN_VPORT vxlanPort;
+    POVS_VXLAN_VPORT vxlanPort = NULL;
 
     vxlanPort = OvsAllocateMemory(sizeof (*vxlanPort));
     if (vxlanPort == NULL) {
@@ -66,14 +99,12 @@ OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
 
     RtlZeroMemory(vxlanPort, sizeof(*vxlanPort));
     vxlanPort->dstPort = udpDestPort;
-    /*
-     * 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;
 
+    if (!OvsIsTunnelFilterCreated(gOvsSwitchContext, udpDestPort)) {
+        OvsTunelFilterCreate(udpDestPort, &vxlanPort->filterID);
+    }
+
     return STATUS_SUCCESS;
 }
 
@@ -81,11 +112,16 @@ OvsInitVxlanTunnel(POVS_VPORT_ENTRY vport,
 VOID
 OvsCleanupVxlanTunnel(POVS_VPORT_ENTRY vport)
 {
+    POVS_VXLAN_VPORT vxlanPort = NULL;
+
     if (vport->ovsType != OVS_VPORT_TYPE_VXLAN ||
         vport->priv == NULL) {
         return;
     }
 
+    vxlanPort = (POVS_VXLAN_VPORT)vport->priv;
+    OvsTunelFilterDelete(vxlanPort->filterID);
+
     OvsFreeMemory(vport->priv);
     vport->priv = NULL;
 }
@@ -474,9 +510,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,
diff --git a/datapath-windows/ovsext/Vxlan.h b/datapath-windows/ovsext/Vxlan.h
index d84796d..ade6b0c 100644
--- a/datapath-windows/ovsext/Vxlan.h
+++ b/datapath-windows/ovsext/Vxlan.h
@@ -24,6 +24,7 @@ typedef struct _OVS_VXLAN_VPORT {
     UINT64 outPkts;
     UINT64 slowInPkts;
     UINT64 slowOutPkts;
+    UINT64 filterID;
     /*
      * To be filled
      */
@@ -75,7 +76,6 @@ OvsGetVxlanTunHdrSize(VOID)
            sizeof (VXLANHdr);
 }
 
-#define VXLAN_UDP_PORT 4789
 #define VXLAN_UDP_PORT_NBO 0xB512
 
 #endif /* __VXLAN_H_ */
-- 
1.9.0.msysgit.0
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev

Reply via email to