datapath-windows: Fixed spelling errors in OVS
[cascardo/ovs.git] / datapath-windows / ovsext / TunnelFilter.c
index 4b879c0..7865451 100644 (file)
 /* Infinite timeout */
 #define INFINITE                        0xFFFFFFFF
 
-/*
- * The provider name should always match the provider string from the install
- * file.
- */
+/* The provider name should always match the provider string from the install
+ * file. */
 #define OVS_TUNNEL_PROVIDER_NAME        L"Open vSwitch"
 
-/*
- * The provider description should always contain the OVS service description
- * string from the install file.
- */
+/* The provider description should always contain the OVS service description
+ * string from the install file. */
 #define OVS_TUNNEL_PROVIDER_DESC        L"Open vSwitch Extension tunnel provider"
 
 /* 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;
+/* Maximum number of tunnel threads to be created. */
+#define OVS_TUNFLT_MAX_THREADS          8
 
 /*
  * Callout and sublayer GUIDs
  */
-// b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8
+
+/* b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8 */
 DEFINE_GUID(
     OVS_TUNNEL_CALLOUT_V4,
     0xb16b0a6e,
@@ -106,40 +103,120 @@ DEFINE_GUID(
     );
 
 /*
- * Callout driver global variables
+ * Callout driver type definitions
  */
-PDEVICE_OBJECT gDeviceObject;
+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;
+    /* XXX: We also need to specify the tunnel L4 protocol, because there are
+     * different protocols that can use the same destination port.*/
+    union {
+        /* Tunnel filter identification used for filter deletion. */
+        UINT64                  delID;
+        /* Pointer used to return filter ID to the caller on filter creation. */
+        PUINT64                 addID;
+    }filterID;
+    /* Requested operation to be performed. */
+    OVS_TUNFLT_OPERATION    operation;
+    /* Current I/O request to be completed when requested
+     * operation is finished. */
+    PIRP                    irp;
+    /* Callback function called before completing the IRP. */
+    PFNTunnelVportPendingOp callback;
+    /* Context passed to the callback function. */
+    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 requests in the list. This variable is used by
+     * InterlockedCompareExchange function and needs to be aligned
+     * at 32-bit boundaries. */
+    UINT32         numEntries;
+} OVS_TUNFLT_REQUEST_LIST, *POVS_TUNFLT_REQUEST_LIST;
+
+typedef struct _OVS_TUNFLT_THREAD_CONTEXT {
+    /* Thread identification. */
+    UINT                    threadID;
+    /* Thread's engine session handle. */
+    HANDLE                  engineSession;
+    /* Reference of the thread object. */
+    PVOID                   threadObject;
+    /* Requests queue list. */
+    OVS_TUNFLT_REQUEST_LIST listRequests;
+    /* 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;
+
+KSTART_ROUTINE  OvsTunnelFilterThreadProc;
+
+static NTSTATUS OvsTunnelFilterStartThreads();
+static NTSTATUS OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
+static VOID     OvsTunnelFilterStopThreads();
+static VOID     OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
+                                          BOOLEAN signalEvent);
+static NTSTATUS OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
+static VOID     OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
 
-HANDLE gEngineHandle = NULL;
-HANDLE gBfeSubscriptionHandle = NULL;
-UINT32 gCalloutIdV4;
+/*
+ * Callout driver global variables
+ */
 
+/* Pointer to the device object that must be create before we can register our
+ * callout to the base filtering engine. */
+static PDEVICE_OBJECT            gDeviceObject = NULL;
+/* Handle to an open session to the filter engine that is used for adding
+ * tunnel's callout. */
+static HANDLE                    gEngineHandle = NULL;
+/* A pointer to the received handle that is associated with the registration of
+ * the OvsTunnelProviderBfeCallback callback. */
+static HANDLE                    gTunnelProviderBfeHandle = NULL;
+/* A pointer to the received handle that is associated with the registration of
+ * the OvsTunnelInitBfeCallback callback. */
+static HANDLE                    gTunnelInitBfeHandle = NULL;
+/* Runtime identifier for tunnel's callout which is retrieved at tunnel
+ * initialization phase when the callout is registered. This ID is then used
+ * for removing the callout object from the system at tunnel
+ * uninitialization phase. */
+static UINT32                    gCalloutIdV4 = 0;
+/* Array used for storing tunnel thread's private data. */
+static OVS_TUNFLT_THREAD_CONTEXT gTunnelThreadCtx[OVS_TUNFLT_MAX_THREADS] = { 0 };
 
-/* Callout driver implementation */
+/*
+ * Callout driver implementation.
+ */
 
 NTSTATUS
-OvsTunnelEngineOpen(HANDLE *handle)
+OvsTunnelEngineOpen(HANDLE *engineSession)
 {
     NTSTATUS status = STATUS_SUCCESS;
     FWPM_SESSION session = { 0 };
 
-    /* The session name isn't required but may be useful for diagnostics. */
-    session.displayData.name = OVS_TUNNEL_SESSION_NAME;
     /*
     * Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT
     * errors while waiting to acquire the transaction lock.
     */
     session.txnWaitTimeoutInMSec = INFINITE;
-    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
 
     /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
     status = FwpmEngineOpen(NULL,
                             RPC_C_AUTHN_DEFAULT,
                             NULL,
                             &session,
-                            handle);
+                            engineSession);
     if (!NT_SUCCESS(status)) {
-        OVS_LOG_ERROR("Fail to open filtering engine session, status: %x.",
+        OVS_LOG_ERROR("Failed to open filtering engine session, status: %x.",
                       status);
     }
 
@@ -147,23 +224,23 @@ OvsTunnelEngineOpen(HANDLE *handle)
 }
 
 VOID
-OvsTunnelEngineClose(HANDLE *handle)
+OvsTunnelEngineClose(HANDLE *engineSession)
 {
-    if (*handle) {
-        FwpmEngineClose(*handle);
-        *handle = NULL;
+    if (*engineSession) {
+        FwpmEngineClose(*engineSession);
+        *engineSession = NULL;
     }
 }
 
 VOID
-OvsTunnelAddSystemProvider(HANDLE handle)
+OvsTunnelAddSystemProvider(HANDLE engineSession)
 {
     NTSTATUS status = STATUS_SUCCESS;
     BOOLEAN inTransaction = FALSE;
     FWPM_PROVIDER provider = { 0 };
 
     do {
-        status = FwpmTransactionBegin(handle, 0);
+        status = FwpmTransactionBegin(engineSession, 0);
         if (!NT_SUCCESS(status)) {
             break;
         }
@@ -179,7 +256,7 @@ OvsTunnelAddSystemProvider(HANDLE handle)
          */
         provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
 
-        status = FwpmProviderAdd(handle,
+        status = FwpmProviderAdd(engineSession,
                                  &provider,
                                  NULL);
         if (!NT_SUCCESS(status)) {
@@ -190,7 +267,7 @@ OvsTunnelAddSystemProvider(HANDLE handle)
             }
         }
 
-        status = FwpmTransactionCommit(handle);
+        status = FwpmTransactionCommit(engineSession);
         if (!NT_SUCCESS(status)) {
             break;
         }
@@ -199,30 +276,30 @@ OvsTunnelAddSystemProvider(HANDLE handle)
     } while (inTransaction);
 
     if (inTransaction){
-        FwpmTransactionAbort(handle);
+        FwpmTransactionAbort(engineSession);
     }
 }
 
 VOID
-OvsTunnelRemoveSystemProvider(HANDLE handle)
+OvsTunnelRemoveSystemProvider(HANDLE engineSession)
 {
     NTSTATUS status = STATUS_SUCCESS;
     BOOLEAN inTransaction = FALSE;
 
     do {
-        status = FwpmTransactionBegin(handle, 0);
+        status = FwpmTransactionBegin(engineSession, 0);
         if (!NT_SUCCESS(status)) {
             break;
         }
         inTransaction = TRUE;
 
-        status = FwpmProviderDeleteByKey(handle,
+        status = FwpmProviderDeleteByKey(engineSession,
                                          &OVS_TUNNEL_PROVIDER_KEY);
         if (!NT_SUCCESS(status)) {
             break;
         }
 
-        status = FwpmTransactionCommit(handle);
+        status = FwpmTransactionCommit(engineSession);
         if (!NT_SUCCESS(status)) {
             break;
         }
@@ -231,29 +308,30 @@ OvsTunnelRemoveSystemProvider(HANDLE handle)
     } while (inTransaction);
 
     if (inTransaction){
-        FwpmTransactionAbort(handle);
+        FwpmTransactionAbort(engineSession);
     }
 }
 
 NTSTATUS
-OvsTunnelAddFilter(PWSTR filterName,
+OvsTunnelAddFilter(HANDLE engineSession,
+                   PWSTR filterName,
                    const PWSTR filterDesc,
                    USHORT remotePort,
                    FWP_DIRECTION direction,
                    UINT64 context,
                    const GUID *filterKey,
                    const GUID *layerKey,
-                   const GUID *calloutKey)
+                   const GUID *calloutKey,
+                   UINT64 *filterID)
 {
     NTSTATUS status = STATUS_SUCCESS;
     FWPM_FILTER filter = {0};
     FWPM_FILTER_CONDITION filterConditions[3] = {0};
     UINT conditionIndex;
 
-    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;
@@ -283,64 +361,18 @@ OvsTunnelAddFilter(PWSTR filterName,
 
     filter.numFilterConditions = conditionIndex;
 
-    status = FwpmFilterAdd(gEngineHandle,
+    status = FwpmFilterAdd(engineSession,
                            &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;
-        }
-
-        inTransaction = TRUE;
+                           filterID);
 
-        /*
-         * 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;
 }
 
 /*
  * --------------------------------------------------------------------------
- * This function registers callouts and filters that intercept UDP traffic at
- * WFP FWPM_LAYER_DATAGRAM_DATA_V4
+ * This function registers callouts for intercepting UDP traffic at WFP
+ * FWPM_LAYER_DATAGRAM_DATA_V4 layer.
  * --------------------------------------------------------------------------
  */
 NTSTATUS
@@ -367,10 +399,7 @@ OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
     sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
 #endif
 
-    status = FwpsCalloutRegister(deviceObject,
-                                 &sCallout,
-                                 calloutId);
-
+    status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId);
     if (!NT_SUCCESS(status)) {
         goto Exit;
     }
@@ -383,24 +412,16 @@ OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
     mCallout.displayData = displayData;
     mCallout.applicableLayer = *layerKey;
 
-    status = FwpmCalloutAdd(gEngineHandle,
-                            &mCallout,
-                            NULL,
-                            NULL);
-
+    status = FwpmCalloutAdd(gEngineHandle, &mCallout, NULL, NULL);
     if (!NT_SUCCESS(status)) {
-        goto Exit;
+        if (STATUS_FWP_ALREADY_EXISTS != status) {
+            OVS_LOG_ERROR("Failed to add WFP callout, status: %x.",
+                          status);
+            goto Exit;
+        }
+        status = STATUS_SUCCESS;
     }
 
-    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)){
@@ -415,24 +436,16 @@ Exit:
 
 /*
  * --------------------------------------------------------------------------
- * This function registers dynamic callouts and filters that intercept UDP
- * Callouts and filters will be removed during De-Initialize.
+ * This function registers non-dynamic callouts for intercepting UDP traffic.
+ * Callouts will be removed during un-initializing phase.
  * --------------------------------------------------------------------------
  */
 NTSTATUS
 OvsTunnelRegisterCallouts(VOID *deviceObject)
 {
-    NTSTATUS status = STATUS_SUCCESS;
-    FWPM_SUBLAYER OvsTunnelSubLayer;
-
-    BOOLEAN engineOpened = FALSE;
-    BOOLEAN inTransaction = FALSE;
-
-    status = OvsTunnelEngineOpen(&gEngineHandle);
-    if (!NT_SUCCESS(status)) {
-        goto Exit;
-    }
-    engineOpened = TRUE;
+    NTSTATUS        status = STATUS_SUCCESS;
+    BOOLEAN         inTransaction = FALSE;
+    FWPM_SUBLAYER   OvsTunnelSubLayer;
 
     status = FwpmTransactionBegin(gEngineHandle, 0);
     if (!NT_SUCCESS(status)) {
@@ -448,15 +461,14 @@ OvsTunnelRegisterCallouts(VOID *deviceObject)
         L"Sub-Layer for use by Datagram-Data OVS callouts";
     OvsTunnelSubLayer.flags = 0;
     OvsTunnelSubLayer.weight = FWP_EMPTY; /* auto-weight */
-    /*
-     * Link all objects to the tunnel provider. When multiple providers are
-     * installed on a computer, this makes it easy to determine who added what.
-     */
-    OvsTunnelSubLayer.providerKey = (GUID*) &OVS_TUNNEL_PROVIDER_KEY;
 
     status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL);
     if (!NT_SUCCESS(status)) {
-        goto Exit;
+        if (STATUS_FWP_ALREADY_EXISTS != status) {
+            OVS_LOG_ERROR("Failed to add WFP sublayer, status: %x.",
+                          status);
+            goto Exit;
+        }
     }
 
     /* In order to use this callout a socket must be opened. */
@@ -480,22 +492,17 @@ Exit:
         if (inTransaction) {
             FwpmTransactionAbort(gEngineHandle);
         }
-        if (engineOpened) {
-            OvsTunnelEngineClose(&gEngineHandle);
-        }
     }
 
     return status;
 }
 
 VOID
-OvsTunnelUnregisterCallouts(VOID)
+OvsTunnelUnregisterCallouts()
 {
-    OvsTunnelRemoveFilter(&OVS_TUNNEL_FILTER_KEY,
-                          &OVS_TUNNEL_SUBLAYER);
     FwpsCalloutUnregisterById(gCalloutIdV4);
+    FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER);
     FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
-    OvsTunnelEngineClose(&gEngineHandle);
 }
 
 VOID
@@ -503,16 +510,22 @@ OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
 {
     UNREFERENCED_PARAMETER(driverObject);
 
+    OvsTunnelFilterStopThreads();
+
     OvsTunnelUnregisterCallouts();
-    IoDeleteDevice(gDeviceObject);
+    OvsTunnelEngineClose(&gEngineHandle);
+
+    if (gDeviceObject) {
+        IoDeleteDevice(gDeviceObject);
+    }
 }
 
 
 NTSTATUS
 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
 {
-    NTSTATUS status = STATUS_SUCCESS;
-    UNICODE_STRING deviceName;
+    NTSTATUS        status = STATUS_SUCCESS;
+    UNICODE_STRING  deviceName;
 
     RtlInitUnicodeString(&deviceName,
                          L"\\Device\\OvsTunnelFilter");
@@ -525,57 +538,79 @@ OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
                             FALSE,
                             &gDeviceObject);
 
+    if (!NT_SUCCESS(status)){
+        OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.",
+                      status);
+        goto Exit;
+    }
+
+    status = OvsTunnelFilterStartThreads();
+    if (!NT_SUCCESS(status)){
+        goto Exit;
+    }
+
+    status = OvsTunnelEngineOpen(&gEngineHandle);
     if (!NT_SUCCESS(status)){
         goto Exit;
     }
 
     status = OvsTunnelRegisterCallouts(gDeviceObject);
+    if (!NT_SUCCESS(status)) {
+        OVS_LOG_ERROR("Failed to register callout, status: %x.",
+                      status);
+    }
 
 Exit:
 
     if (!NT_SUCCESS(status)){
-        if (gEngineHandle != NULL) {
-            OvsTunnelUnregisterCallouts();
-        }
-
-        if (gDeviceObject) {
-            IoDeleteDevice(gDeviceObject);
-        }
+        OvsTunnelFilterUninitialize(driverObject);
     }
 
     return status;
 }
 
+/*
+ * --------------------------------------------------------------------------
+ * This function adds OVS system provider to the system if the BFE (Base
+ * Filtering Engine) is running.
+ * --------------------------------------------------------------------------
+ */
 VOID NTAPI
-OvsBfeStateChangeCallback(PVOID context,
-                          FWPM_SERVICE_STATE bfeState)
+OvsTunnelProviderBfeCallback(PVOID context,
+                             FWPM_SERVICE_STATE bfeState)
 {
-    HANDLE handle = NULL;
+    HANDLE engineSession = NULL;
 
     DBG_UNREFERENCED_PARAMETER(context);
 
     if (FWPM_SERVICE_RUNNING == bfeState) {
-        OvsTunnelEngineOpen(&handle);
-        if (handle) {
-            OvsTunnelAddSystemProvider(handle);
+        OvsTunnelEngineOpen(&engineSession);
+        if (engineSession) {
+            OvsTunnelAddSystemProvider(engineSession);
         }
-        OvsTunnelEngineClose(&handle);
+        OvsTunnelEngineClose(&engineSession);
     }
 }
 
+/*
+ * --------------------------------------------------------------------------
+ * This function registers the OvsTunnelProviderBfeCallback callback that is
+ * called whenever there is a change to the state of base filtering engine.
+ * --------------------------------------------------------------------------
+ */
 NTSTATUS
-OvsSubscribeBfeStateChanges(PVOID deviceObject)
+OvsSubscribeTunnelProviderBfeStateChanges(PVOID deviceObject)
 {
     NTSTATUS status = STATUS_SUCCESS;
 
-    if (!gBfeSubscriptionHandle) {
+    if (!gTunnelProviderBfeHandle) {
         status = FwpmBfeStateSubscribeChanges(deviceObject,
-                                              OvsBfeStateChangeCallback,
+                                              OvsTunnelProviderBfeCallback,
                                               NULL,
-                                              &gBfeSubscriptionHandle);
+                                              &gTunnelProviderBfeHandle);
         if (!NT_SUCCESS(status)) {
             OVS_LOG_ERROR(
-                "Failed to open subscribe BFE state change callback, status: %x.",
+                "Failed to subscribe BFE tunnel provider callback, status: %x.",
                 status);
         }
     }
@@ -583,50 +618,890 @@ OvsSubscribeBfeStateChanges(PVOID deviceObject)
     return status;
 }
 
+/*
+ * --------------------------------------------------------------------------
+ * This function unregisters the OvsTunnelProviderBfeCallback callback that
+ * was previously registered by OvsSubscribeTunnelProviderBfeStateChanges
+ * function.
+ * --------------------------------------------------------------------------
+ */
 VOID
-OvsUnsubscribeBfeStateChanges()
+OvsUnsubscribeTunnelProviderBfeStateChanges()
 {
     NTSTATUS status = STATUS_SUCCESS;
 
-    if (gBfeSubscriptionHandle) {
-        status = FwpmBfeStateUnsubscribeChanges(gBfeSubscriptionHandle);
+    if (gTunnelProviderBfeHandle) {
+        status = FwpmBfeStateUnsubscribeChanges(gTunnelProviderBfeHandle);
         if (!NT_SUCCESS(status)) {
             OVS_LOG_ERROR(
-                "Failed to open unsubscribe BFE state change callback, status: %x.",
+                "Failed to unsubscribe BFE tunnel provider callback, status: %x.",
                 status);
         }
-        gBfeSubscriptionHandle = NULL;
+        gTunnelProviderBfeHandle = NULL;
     }
 }
 
-VOID OvsRegisterSystemProvider(PVOID deviceObject)
+/*
+ * --------------------------------------------------------------------------
+ * This function registers the OVS system provider if the BFE (Base Filtering
+ * Engine) is running.
+ * Otherwise, it will register the OvsTunnelProviderBfeCallback callback.
+
+ * Note: Before calling FwpmBfeStateGet, the callout driver must call the
+ * FwpmBfeStateSubscribeChanges function to register the callback function
+ * to be called whenever the state of the filter engine changes.
+ *
+ * Register WFP system provider call hierarchy:
+ * <DriverEntry>
+ *     <OvsCreateDeviceObject>
+ *         <OvsRegisterSystemProvider>
+ *             <OvsSubscribeTunnelProviderBfeStateChanges>
+ *                 --> registers OvsTunnelProviderBfeCallback callback
+ *                     <OvsTunnelProviderBfeCallback>
+ *                         --> if BFE is running:
+ *                             <OvsTunnelAddSystemProvider>
+ *             --> if BFE is running:
+ *                 <OvsTunnelAddSystemProvider>
+ *                 <OvsUnsubscribeTunnelProviderBfeStateChanges>
+ *                     --> unregisters OvsTunnelProviderBfeCallback callback
+ *
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsRegisterSystemProvider(PVOID deviceObject)
 {
     NTSTATUS status = STATUS_SUCCESS;
-    HANDLE handle = NULL;
+    HANDLE engineSession = NULL;
 
-    status = OvsSubscribeBfeStateChanges(deviceObject);
+    status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
     if (NT_SUCCESS(status)) {
         if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
-            OvsTunnelEngineOpen(&handle);
-            if (handle) {
-                OvsTunnelAddSystemProvider(handle);
+            OvsTunnelEngineOpen(&engineSession);
+            if (engineSession) {
+                OvsTunnelAddSystemProvider(engineSession);
             }
-            OvsTunnelEngineClose(&handle);
+            OvsTunnelEngineClose(&engineSession);
 
-            OvsUnsubscribeBfeStateChanges();
+            OvsUnsubscribeTunnelProviderBfeStateChanges();
         }
     }
 }
 
-VOID OvsUnregisterSystemProvider()
+/*
+ * --------------------------------------------------------------------------
+ * This function removes the OVS system provider and unregisters the
+ * OvsTunnelProviderBfeCallback callback from BFE (Base Filtering Engine).
+ *
+ * Unregister WFP system provider call hierarchy:
+ * <OvsExtUnload>
+ *     <OvsDeleteDeviceObject>
+ *         <OvsUnregisterSystemProvider>
+ *             <OvsTunnelRemoveSystemProvider>
+ *             <OvsUnsubscribeTunnelProviderBfeStateChanges>
+ *                 --> unregisters OvsTunnelProviderBfeCallback callback
+ *
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsUnregisterSystemProvider()
+{
+    HANDLE engineSession = NULL;
+
+    OvsTunnelEngineOpen(&engineSession);
+    if (engineSession) {
+        OvsTunnelRemoveSystemProvider(engineSession);
+    }
+    OvsTunnelEngineClose(&engineSession);
+
+    OvsUnsubscribeTunnelProviderBfeStateChanges();
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function initializes the tunnel filter if the BFE is running.
+ * --------------------------------------------------------------------------
+ */
+VOID NTAPI
+OvsTunnelInitBfeCallback(PVOID context,
+                         FWPM_SERVICE_STATE bfeState)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) context;
+
+    if (FWPM_SERVICE_RUNNING == bfeState) {
+        status = OvsTunnelFilterInitialize(driverObject);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR(
+                "Failed to initialize tunnel filter, status: %x.",
+                status);
+        }
+    }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function registers the OvsTunnelInitBfeCallback callback that is
+ * called whenever there is a change to the state of base filtering engine.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsSubscribeTunnelInitBfeStateChanges(PDRIVER_OBJECT driverObject,
+                                      PVOID deviceObject)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    if (!gTunnelInitBfeHandle) {
+        status = FwpmBfeStateSubscribeChanges(deviceObject,
+                                              OvsTunnelInitBfeCallback,
+                                              driverObject,
+                                              &gTunnelInitBfeHandle);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR(
+                "Failed to subscribe BFE tunnel init callback, status: %x.",
+                status);
+        }
+    }
+
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function unregisters the OvsTunnelInitBfeCallback callback that
+ * was previously registered by OvsSubscribeTunnelInitBfeStateChanges
+ * function.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsUnsubscribeTunnelInitBfeStateChanges()
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    if (gTunnelInitBfeHandle) {
+        status = FwpmBfeStateUnsubscribeChanges(gTunnelInitBfeHandle);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR(
+                "Failed to unsubscribe BFE tunnel init callback, status: %x.",
+                status);
+        }
+        gTunnelInitBfeHandle = NULL;
+    }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function initializes the OVS tunnel filter if the BFE (Base Filtering
+ * Engine) is running.
+ * Otherwise, it will register the OvsTunnelInitBfeCallback callback.
+
+ * Note: Before calling FwpmBfeStateGet, the callout driver must call the
+ * FwpmBfeStateSubscribeChanges function to register the callback function
+ * to be called whenever the state of the filter engine changes.
+ *
+ * Initialize OVS tunnel filter call hierarchy:
+ * <OvsExtAttach>
+ *     <OvsCreateSwitch>
+ *         <OvsInitTunnelFilter>
+ *             <OvsSubscribeTunnelInitBfeStateChanges>
+ *                 --> registers OvsTunnelInitBfeCallback callback
+ *                     <OvsTunnelInitBfeCallback>
+ *                         --> if BFE is running:
+ *                             <OvsTunnelFilterInitialize>
+ *                                 <IoCreateDevice>
+ *                                 <OvsTunnelFilterStartThreads>
+ *                                 <OvsTunnelRegisterCallouts>
+ *             --> if BFE is running:
+ *                 <OvsTunnelFilterInitialize>
+ *                     <IoCreateDevice>
+ *                     <OvsTunnelFilterStartThreads>
+ *                     <OvsTunnelRegisterCallouts>
+ *                 <OvsUnsubscribeTunnelInitBfeStateChanges>
+ *                     --> unregisters OvsTunnelInitBfeCallback callback
+ *
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject)
 {
-    HANDLE handle = NULL;
+    NTSTATUS status = STATUS_SUCCESS;
 
-    OvsTunnelEngineOpen(&handle);
-    if (handle) {
-        OvsTunnelRemoveSystemProvider(handle);
+    status = OvsSubscribeTunnelInitBfeStateChanges(driverObject, deviceObject);
+    if (NT_SUCCESS(status)) {
+        if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
+            status = OvsTunnelFilterInitialize(driverObject);
+            if (!NT_SUCCESS(status)) {
+                /* XXX: We need to decide what actions to take in case of
+                 * failure to initialize tunnel filter. */
+                ASSERT(status == NDIS_STATUS_SUCCESS);
+                OVS_LOG_ERROR(
+                    "Failed to initialize tunnel filter, status: %x.",
+                    status);
+            }
+            OvsUnsubscribeTunnelInitBfeStateChanges();
+        }
     }
-    OvsTunnelEngineClose(&handle);
 
-    OvsUnsubscribeBfeStateChanges();
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function uninitializes the OVS tunnel filter and unregisters the
+ * OvsTunnelInitBfeCallback callback from BFE.
+ *
+ * Uninitialize OVS tunnel filter call hierarchy:
+ * <OvsExtDetach>
+ *     <OvsDeleteSwitch>
+ *         <OvsUninitTunnelFilter>
+ *             <OvsTunnelFilterUninitialize>
+ *                 <OvsTunnelFilterStopThreads>
+ *                 <OvsTunnelUnregisterCallouts>
+ *                 <IoDeleteDevice>
+ *             <OvsUnsubscribeTunnelInitBfeStateChanges>
+ *                 --> unregisters OvsTunnelInitBfeCallback callback
+ *
+ * --------------------------------------------------------------------------
+ */
+VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
+{
+    OvsTunnelFilterUninitialize(driverObject);
+    OvsUnsubscribeTunnelInitBfeStateChanges();
+}
+
+NTSTATUS
+OvsTunnelAddFilterEx(HANDLE engineSession,
+                     UINT32 filterPort,
+                     UINT64 *filterID)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    status = OvsTunnelAddFilter(engineSession,
+                                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);
+    if (!NT_SUCCESS(status)) {
+        OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.",
+                      filterPort, status);
+    } else {
+        OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.",
+                     filterPort, *filterID);
+    }
+
+    return status;
+}
+
+NTSTATUS
+OvsTunnelRemoveFilterEx(HANDLE engineSession,
+                        UINT64 filterID)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    BOOLEAN  error = TRUE;
+
+    do {
+        if (filterID == 0) {
+            OVS_LOG_INFO("No tunnel filter to remove.");
+            break;
+        }
+
+        status = FwpmFilterDeleteById(engineSession, filterID);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR("Failed to remove tunnel with filter ID: %d,\
+                           status: %x.", filterID, status);
+            break;
+        }
+        OVS_LOG_INFO("Filter removed, filter ID: %d.",
+                     filterID);
+
+        error = FALSE;
+    } while (error);
+
+    return status;
+}
+
+NTSTATUS
+OvsTunnelFilterExecuteAction(HANDLE engineSession,
+                             POVS_TUNFLT_REQUEST request)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    switch (request->operation)
+    {
+    case OVS_TUN_FILTER_CREATE:
+        status = OvsTunnelAddFilterEx(engineSession,
+                                      request->port,
+                                      request->filterID.addID);
+        break;
+    case OVS_TUN_FILTER_DELETE:
+        status = OvsTunnelRemoveFilterEx(engineSession,
+                                         request->filterID.delID);
+        break;
+    default:
+        status = STATUS_NOT_SUPPORTED;
+        break;
+    }
+
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function pops the whole request entries from the queue and returns the
+ * number of entries through the 'count' parameter. The operation is
+ * synchronized using request list spinlock.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsTunnelFilterRequestPopList(POVS_TUNFLT_REQUEST_LIST listRequests,
+                              PLIST_ENTRY head,
+                              UINT32 *count)
+{
+    NdisAcquireSpinLock(&listRequests->spinlock);
+
+    if (!IsListEmpty(&listRequests->head)) {
+        PLIST_ENTRY PrevEntry;
+        PLIST_ENTRY NextEntry;
+
+        NextEntry = listRequests->head.Flink;
+        PrevEntry = listRequests->head.Blink;
+
+        head->Flink = NextEntry;
+        NextEntry->Blink = head;
+
+        head->Blink = PrevEntry;
+        PrevEntry->Flink = head;
+
+        *count = listRequests->numEntries;
+
+        InitializeListHead(&listRequests->head);
+        listRequests->numEntries = 0;
+    }
+
+    NdisReleaseSpinLock(&listRequests->spinlock);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function pushes the received request to the list while holding the
+ * request list spinlock.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests,
+                           POVS_TUNFLT_REQUEST request)
+{
+    NdisAcquireSpinLock(&listRequests->spinlock);
+
+    InsertTailList(&listRequests->head, &(request->entry));
+    listRequests->numEntries++;
+
+    NdisReleaseSpinLock(&listRequests->spinlock);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function pushes the received request to the corresponding thread
+ * request queue. The arrival of the new request is signaled to the thread,
+ * in order to start processing it.
+ *
+ * For a uniform distribution of requests to thread queues, a thread index is
+ * calculated based on the received destination port.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request)
+{
+    UINT32 threadIndex;
+
+    threadIndex = request->port % OVS_TUNFLT_MAX_THREADS;
+
+    OvsTunnelFilterRequestPush(
+        &gTunnelThreadCtx[threadIndex].listRequests,
+        request);
+
+    KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent,
+               IO_NO_INCREMENT,
+               FALSE);
+}
+
+VOID
+OvsTunnelFilterCompleteRequest(PIRP irp,
+                               PFNTunnelVportPendingOp callback,
+                               PVOID context,
+                               NTSTATUS status)
+{
+    UINT32 replyLen = 0;
+
+    if (callback) {
+        callback(context, status, &replyLen);
+        /* Release the context passed to the callback function. */
+        OvsFreeMemory(context);
+    }
+
+    if (irp) {
+        OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
+    }
+}
+
+VOID
+OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
+{
+    POVS_TUNFLT_REQUEST request = NULL;
+    PLIST_ENTRY         link = NULL;
+    PLIST_ENTRY         next = NULL;
+    LIST_ENTRY          head;
+    NTSTATUS            status = STATUS_SUCCESS;
+    UINT32              count = 0;
+    BOOLEAN             inTransaction = FALSE;
+    BOOLEAN             error = TRUE;
+
+    do
+    {
+        if (!InterlockedCompareExchange(
+            (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) {
+            OVS_LOG_INFO("Nothing to do... request list is empty.");
+            break;
+        }
+
+        status = FwpmTransactionBegin(threadCtx->engineSession, 0);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR("Failed to start transaction, status: %x.",
+                          status);
+            break;
+        }
+        inTransaction = TRUE;
+
+        InitializeListHead(&head);
+        OvsTunnelFilterRequestPopList(&threadCtx->listRequests, &head, &count);
+
+        LIST_FORALL_SAFE(&head, link, next) {
+            request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry);
+
+            status = OvsTunnelFilterExecuteAction(threadCtx->engineSession,
+                                                  request);
+            if (!NT_SUCCESS(status)) {
+                RemoveEntryList(&request->entry);
+                count--;
+
+                /* Complete the IRP with the failure status. */
+                OvsTunnelFilterCompleteRequest(request->irp,
+                                               request->callback,
+                                               request->context,
+                                               status);
+                OvsFreeMemory(request);
+                request = NULL;
+            } else {
+                error = FALSE;
+            }
+        }
+
+        if (error) {
+            /* No successful requests were made, so there is no point to commit
+             * the transaction. */
+            break;
+        }
+
+        status = FwpmTransactionCommit(threadCtx->engineSession);
+        if (!NT_SUCCESS(status)){
+            OVS_LOG_ERROR("Failed to commit transaction, status: %x.",
+                          status);
+            break;
+        }
+
+        inTransaction = FALSE;
+    } while (inTransaction);
+
+    if (inTransaction) {
+        FwpmTransactionAbort(threadCtx->engineSession);
+        OVS_LOG_ERROR("Failed to execute request, status: %x.\
+                       Transaction aborted.", status);
+    }
+
+    /* Complete the requests successfully executed with the transaction commit
+     * status. */
+    while (count) {
+        request = (POVS_TUNFLT_REQUEST)RemoveHeadList(&head);
+        count--;
+
+        OvsTunnelFilterCompleteRequest(request->irp,
+                                       request->callback,
+                                       request->context,
+                                       status);
+        OvsFreeMemory(request);
+        request = NULL;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * System thread routine that processes thread's requests queue. The thread
+ * routine initializes thread's necessary data and waits on two events,
+ * requestEvent and stopEvent. Whenever a request is pushed to the thread's
+ * queue, the requestEvent is signaled and the thread routine starts processing
+ * the arrived requests. When stopEvent is signaled, all subsequent requests
+ * are completed with STATUS_CANCELED, without being added to the thread's
+ * queue, and the routine finishes processing all existing requests from the
+ * queue before uninitializing the thread and exiting.
+ *----------------------------------------------------------------------------
+ */
+_Use_decl_annotations_
+VOID
+OvsTunnelFilterThreadProc(PVOID context)
+{
+    NTSTATUS                   status = STATUS_SUCCESS;
+    POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context;
+    PKEVENT                    eventArray[2] = { 0 };
+    ULONG                      count = 0;
+    BOOLEAN                    exit = FALSE;
+    BOOLEAN                    error = TRUE;
+
+    OVS_LOG_INFO("Starting OVS Tunnel system thread %d.",
+                 threadCtx->threadID);
+
+    eventArray[0] = &threadCtx->stopEvent;
+    eventArray[1] = &threadCtx->requestEvent;
+    count = ARRAY_SIZE(eventArray);
+
+    do {
+        status = OvsTunnelFilterThreadInit(threadCtx);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.",
+                threadCtx->threadID);
+            break;
+        }
+
+        do {
+            status = KeWaitForMultipleObjects(count,
+                                              (PVOID)eventArray,
+                                              WaitAny,
+                                              Executive,
+                                              KernelMode,
+                                              FALSE,
+                                              NULL,
+                                              NULL);
+            switch (status) {
+                case STATUS_WAIT_1:
+                    /* Start processing requests. */
+                    OvsTunnelFilterRequestListProcess(threadCtx);
+                    break;
+                default:
+                    /* Finish processing the received requests and exit. */
+                    OvsTunnelFilterRequestListProcess(threadCtx);
+                    exit = TRUE;
+                    break;
+            }
+        } while (!exit);
+
+        OvsTunnelFilterThreadUninit(threadCtx);
+
+        error = FALSE;
+    } while (error);
+
+    OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.",
+                 threadCtx->threadID);
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
+};
+
+static NTSTATUS
+OvsTunnelFilterStartThreads()
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
+        gTunnelThreadCtx[index].threadID = index;
+
+        status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index);
+            break;
+        }
+    }
+
+    return status;
+}
+
+static NTSTATUS
+OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
+{
+    NTSTATUS    status = STATUS_SUCCESS;
+    HANDLE      threadHandle = NULL;
+    BOOLEAN     error = TRUE;
+
+    do {
+        status = PsCreateSystemThread(&threadHandle,
+                                      SYNCHRONIZE,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      OvsTunnelFilterThreadProc,
+                                      threadCtx);
+        if (!NT_SUCCESS(status)) {
+            OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
+                          status);
+            break;
+        }
+
+        ObReferenceObjectByHandle(threadHandle,
+                                  SYNCHRONIZE,
+                                  NULL,
+                                  KernelMode,
+                                  &threadCtx->threadObject,
+                                  NULL);
+        ZwClose(threadHandle);
+        threadHandle = NULL;
+
+        error = FALSE;
+    } while (error);
+
+    return status;
+}
+
+static VOID
+OvsTunnelFilterStopThreads()
+{
+    /* Signal all threads to stop and ignore all subsequent requests. */
+    for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
+        OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], TRUE);
+    }
+
+    /* Wait for all threads to finish processing the requests. */
+    for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
+        OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], FALSE);
+    }
+}
+
+static VOID
+OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
+                          BOOLEAN signalEvent)
+{
+    if (signalEvent) {
+        /* Signal stop thread event. */
+        OVS_LOG_INFO("Received stop event for OVS Tunnel system thread %d.",
+                     threadCtx->threadID);
+        KeSetEvent(&threadCtx->stopEvent, IO_NO_INCREMENT, FALSE);
+    } else {
+        /* Wait for the tunnel thread to finish. */
+        KeWaitForSingleObject(threadCtx->threadObject,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+
+        ObDereferenceObject(threadCtx->threadObject);
+    }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function initializes thread's necessary data. Each thread has its own
+ * session object to the BFE that is used for processing the requests from
+ * the thread's queue.
+ * --------------------------------------------------------------------------
+ */
+static NTSTATUS
+OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    BOOLEAN error = TRUE;
+
+    do {
+        /* Create thread's engine session object. */
+        status = OvsTunnelEngineOpen(&threadCtx->engineSession);
+        if (!NT_SUCCESS(status)) {
+            break;
+        }
+
+        NdisAllocateSpinLock(&threadCtx->listRequests.spinlock);
+
+        InitializeListHead(&threadCtx->listRequests.head);
+
+        KeInitializeEvent(&threadCtx->stopEvent,
+            NotificationEvent,
+            FALSE);
+
+        KeInitializeEvent(&threadCtx->requestEvent,
+            SynchronizationEvent,
+            FALSE);
+
+        error = FALSE;
+    } while (error);
+
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function uninitializes thread's private data. Thread's engine session
+ * handle is closed and set to NULL.
+ * --------------------------------------------------------------------------
+ */
+static VOID
+OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
+{
+    if (threadCtx->engineSession) {
+        /* Close thread's FWPM session. */
+        OvsTunnelEngineClose(&threadCtx->engineSession);
+
+        NdisFreeSpinLock(&threadCtx->listRequests.spinlock);
+    }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * This function creates a new tunnel filter request and push it to a thread
+ * queue. If the thread stop event is signaled, the request is completed with
+ * STATUS_CANCELLED without pushing it to any queue.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsTunnelFilterQueueRequest(PIRP irp,
+                            UINT16 remotePort,
+                            UINT64 *filterID,
+                            OVS_TUNFLT_OPERATION operation,
+                            PFNTunnelVportPendingOp callback,
+                            PVOID tunnelContext)
+{
+    POVS_TUNFLT_REQUEST request = NULL;
+    NTSTATUS            status = STATUS_PENDING;
+    BOOLEAN             error = TRUE;
+    UINT64              timeout = 0;
+
+    do {
+        /* Verify if the stop event was signaled. */
+        if (STATUS_SUCCESS == KeWaitForSingleObject(
+                                  &gTunnelThreadCtx[0].stopEvent,
+                                  Executive,
+                                  KernelMode,
+                                  FALSE,
+                                  (LARGE_INTEGER *)&timeout)) {
+            /* The stop event is signaled. Completed the IRP with
+             * STATUS_CANCELLED. */
+            status = STATUS_CANCELLED;
+            break;
+        }
+
+        if (NULL == filterID) {
+            OVS_LOG_ERROR("Invalid request.");
+            status = STATUS_INVALID_PARAMETER;
+            break;
+        }
+
+        request = (POVS_TUNFLT_REQUEST) OvsAllocateMemory(sizeof(*request));
+        if (NULL == request) {
+            OVS_LOG_ERROR("Failed to allocate list item.");
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            break;
+        }
+
+        request->port = remotePort;
+        request->operation = operation;
+        switch (operation) {
+            case OVS_TUN_FILTER_CREATE:
+                request->filterID.addID = filterID;
+                break;
+            case OVS_TUN_FILTER_DELETE:
+                request->filterID.delID = *filterID;
+                break;
+        }
+        request->irp = irp;
+        request->callback = callback;
+        request->context = tunnelContext;
+
+        OvsTunnelFilterThreadPush(request);
+
+        error = FALSE;
+    } while (error);
+
+    if (error) {
+        if (request) {
+            OvsFreeMemory(request);
+            request = NULL;
+        }
+    }
+
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  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 tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
+ *
+ * OVS VXLAN port add call hierarchy:
+ * <OvsNewVportCmdHandler>
+ *     <OvsInitTunnelVport>
+ *         <OvsInitVxlanTunnel>
+ *             <OvsTunnelFilterCreate>
+ *                 <OvsTunnelFilterQueueRequest>
+ *                     --> if thread STOP event is signalled:
+ *                         --> Complete request with STATUS_CANCELLED
+ *                         --> EXIT
+ *                     <OvsTunnelFilterThreadPush>
+ *                         --> add the request to one of tunnel thread queues
+ *
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsTunnelFilterCreate(PIRP irp,
+                      UINT16 filterPort,
+                      UINT64 *filterID,
+                      PFNTunnelVportPendingOp callback,
+                      PVOID tunnelContext)
+{
+    return OvsTunnelFilterQueueRequest(irp,
+                                       filterPort,
+                                       filterID,
+                                       OVS_TUN_FILTER_CREATE,
+                                       callback,
+                                       tunnelContext);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ *  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 tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
+ *
+ * OVS VXLAN port delete call hierarchy:
+ * <OvsDeleteVportCmdHandler>
+ *     <OvsRemoveAndDeleteVport>
+ *         <OvsCleanupVxlanTunnel>
+ *             <OvsTunnelFilterDelete>
+ *                 <OvsTunnelFilterQueueRequest>
+ *                     --> if thread STOP event is signalled:
+ *                         --> Complete request with STATUS_CANCELLED
+ *                         --> EXIT
+ *                     <OvsTunnelFilterThreadPush>
+ *                         --> add the request to one of tunnel thread queues
+ *
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsTunnelFilterDelete(PIRP irp,
+                      UINT64 filterID,
+                      PFNTunnelVportPendingOp callback,
+                      PVOID tunnelContext)
+{
+    return OvsTunnelFilterQueueRequest(irp,
+                                       0,
+                                       &filterID,
+                                       OVS_TUN_FILTER_DELETE,
+                                       callback,
+                                       tunnelContext);
 }