Merge "master" into "ovn".
[cascardo/ovs.git] / datapath-windows / ovsext / TunnelFilter.c
1 /*
2  * Copyright (c) 2014 VMware, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "precomp.h"
18
19 #pragma warning(push)
20 #pragma warning(disable:4201)       // unnamed struct/union
21 #ifdef OVS_DBG_MOD
22 #undef OVS_DBG_MOD
23 #endif
24 #define OVS_DBG_MOD OVS_DBG_TUNFLT
25 #include "Debug.h"
26
27
28 #include <fwpsk.h>
29
30 #pragma warning(pop)
31
32 #include <fwpmk.h>
33 #include <ws2ipdef.h>
34 #include <in6addr.h>
35 #include <ip2string.h>
36
37 #include "Tunnel.h"
38 #include "Switch.h"
39 #include "Vport.h"
40 #include "Event.h"
41 #include "User.h"
42 #include "Vxlan.h"
43
44
45 #define INITGUID
46 #include <guiddef.h>
47
48 /* Infinite timeout */
49 #define INFINITE                        0xFFFFFFFF
50
51 /* The provider name should always match the provider string from the install
52  * file. */
53 #define OVS_TUNNEL_PROVIDER_NAME        L"Open vSwitch"
54
55 /* The provider description should always contain the OVS service description
56  * string from the install file. */
57 #define OVS_TUNNEL_PROVIDER_DESC        L"Open vSwitch Extension tunnel provider"
58
59 /* The session name isn't required but it's useful for diagnostics. */
60 #define OVS_TUNNEL_SESSION_NAME         L"OVS tunnel session"
61
62 /* Maximum number of tunnel threads to be created. */
63 #define OVS_TUNFLT_MAX_THREADS          8
64
65 /*
66  * Callout and sublayer GUIDs
67  */
68
69 /* b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8 */
70 DEFINE_GUID(
71     OVS_TUNNEL_CALLOUT_V4,
72     0xb16b0a6e,
73     0x2b2a,
74     0x41a3,
75     0x8b, 0x39, 0xbd, 0x3f, 0xfc, 0x85, 0x5f, 0xf8
76     );
77
78 /* 0104fd7e-c825-414e-94c9-f0d525bbc169 */
79 DEFINE_GUID(
80     OVS_TUNNEL_SUBLAYER,
81     0x0104fd7e,
82     0xc825,
83     0x414e,
84     0x94, 0xc9, 0xf0, 0xd5, 0x25, 0xbb, 0xc1, 0x69
85     );
86
87 /* 6fc957d7-14e7-47c7-812b-4668be994ba1 */
88 DEFINE_GUID(
89     OVS_TUNNEL_PROVIDER_KEY,
90     0x6fc957d7,
91     0x14e7,
92     0x47c7,
93     0x81, 0x2b, 0x46, 0x68, 0xbe, 0x99, 0x4b, 0xa1
94     );
95
96 /* bfd4814c-9650-4de3-a536-1eedb9e9ba6a */
97 DEFINE_GUID(
98     OVS_TUNNEL_FILTER_KEY,
99     0xbfd4814c,
100     0x9650,
101     0x4de3,
102     0xa5, 0x36, 0x1e, 0xed, 0xb9, 0xe9, 0xba, 0x6a
103     );
104
105 /*
106  * Callout driver type definitions
107  */
108 typedef enum _OVS_TUNFLT_OPERATION {
109     OVS_TUN_FILTER_CREATE = 0,
110     OVS_TUN_FILTER_DELETE
111 } OVS_TUNFLT_OPERATION;
112
113 typedef struct _OVS_TUNFLT_REQUEST {
114     LIST_ENTRY              entry;
115     /* Tunnel filter destination port. */
116     UINT16                  port;
117     /* XXX: We also need to specify the tunnel L4 protocol, because there are
118      * different protocols that can use the same destination port.*/
119     union {
120         /* Tunnel filter identification used for filter deletion. */
121         UINT64                  delID;
122         /* Pointer used to return filter ID to the caller on filter creation. */
123         PUINT64                 addID;
124     }filterID;
125     /* Requested operation to be performed. */
126     OVS_TUNFLT_OPERATION    operation;
127     /* Current I/O request to be completed when requested
128      * operation is finished. */
129     PIRP                    irp;
130     /* Callback function called before completing the IRP. */
131     PFNTunnelVportPendingOp callback;
132     /* Context passed to the callback function. */
133     PVOID                   context;
134 } OVS_TUNFLT_REQUEST, *POVS_TUNFLT_REQUEST;
135
136 typedef struct _OVS_TUNFLT_REQUEST_LIST {
137     /* SpinLock for syncronizing access to the requests list. */
138     NDIS_SPIN_LOCK spinlock;
139     /* Head of the requests list. */
140     LIST_ENTRY     head;
141     /* Number of requests in the list. This variable is used by
142      * InterlockedCompareExchange function and needs to be aligned
143      * at 32-bit boundaries. */
144     UINT32         numEntries;
145 } OVS_TUNFLT_REQUEST_LIST, *POVS_TUNFLT_REQUEST_LIST;
146
147 typedef struct _OVS_TUNFLT_THREAD_CONTEXT {
148     /* Thread identification. */
149     UINT                    threadID;
150     /* Thread's engine session handle. */
151     HANDLE                  engineSession;
152     /* Reference of the thread object. */
153     PVOID                   threadObject;
154     /* Requests queue list. */
155     OVS_TUNFLT_REQUEST_LIST listRequests;
156     /* Event signaling that there are requests to process. */
157     KEVENT                  requestEvent;
158     /* Event for stopping thread execution. */
159     KEVENT                  stopEvent;
160 } OVS_TUNFLT_THREAD_CONTEXT, *POVS_TUNFLT_THREAD_CONTEXT;
161
162 KSTART_ROUTINE  OvsTunnelFilterThreadProc;
163
164 static NTSTATUS OvsTunnelFilterStartThreads();
165 static NTSTATUS OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
166 static VOID     OvsTunnelFilterStopThreads();
167 static VOID     OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
168                                           BOOLEAN signalEvent);
169 static NTSTATUS OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
170 static VOID     OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
171
172 /*
173  * Callout driver global variables
174  */
175
176 /* Pointer to the device object that must be create before we can register our
177  * callout to the base filtering engine. */
178 static PDEVICE_OBJECT            gDeviceObject = NULL;
179 /* Handle to an open session to the filter engine that is used for adding
180  * tunnel's callout. */
181 static HANDLE                    gEngineHandle = NULL;
182 /* A pointer to the received handle that is associated with the registration of
183  * the OvsTunnelProviderBfeCallback callback. */
184 static HANDLE                    gTunnelProviderBfeHandle = NULL;
185 /* A pointer to the received handle that is associated with the registration of
186  * the OvsTunnelInitBfeCallback callback. */
187 static HANDLE                    gTunnelInitBfeHandle = NULL;
188 /* Runtime identifier for tunnel's callout which is retrieved at tunnel
189  * initialization phase when the callout is registered. This ID is then used
190  * for removing the callout object from the system at tunnel
191  * uninitialization phase. */
192 static UINT32                    gCalloutIdV4 = 0;
193 /* Array used for storing tunnel thread's private data. */
194 static OVS_TUNFLT_THREAD_CONTEXT gTunnelThreadCtx[OVS_TUNFLT_MAX_THREADS] = { 0 };
195
196 /*
197  * Callout driver implementation.
198  */
199
200 NTSTATUS
201 OvsTunnelEngineOpen(HANDLE *engineSession)
202 {
203     NTSTATUS status = STATUS_SUCCESS;
204     FWPM_SESSION session = { 0 };
205
206     /*
207     * Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT
208     * errors while waiting to acquire the transaction lock.
209     */
210     session.txnWaitTimeoutInMSec = INFINITE;
211
212     /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
213     status = FwpmEngineOpen(NULL,
214                             RPC_C_AUTHN_DEFAULT,
215                             NULL,
216                             &session,
217                             engineSession);
218     if (!NT_SUCCESS(status)) {
219         OVS_LOG_ERROR("Failed to open filtering engine session, status: %x.",
220                       status);
221     }
222
223     return status;
224 }
225
226 VOID
227 OvsTunnelEngineClose(HANDLE *engineSession)
228 {
229     if (*engineSession) {
230         FwpmEngineClose(*engineSession);
231         *engineSession = NULL;
232     }
233 }
234
235 VOID
236 OvsTunnelAddSystemProvider(HANDLE engineSession)
237 {
238     NTSTATUS status = STATUS_SUCCESS;
239     BOOLEAN inTransaction = FALSE;
240     FWPM_PROVIDER provider = { 0 };
241
242     do {
243         status = FwpmTransactionBegin(engineSession, 0);
244         if (!NT_SUCCESS(status)) {
245             break;
246         }
247         inTransaction = TRUE;
248
249         memset(&provider, 0, sizeof(provider));
250         provider.providerKey = OVS_TUNNEL_PROVIDER_KEY;
251         provider.displayData.name = OVS_TUNNEL_PROVIDER_NAME;
252         provider.displayData.description = OVS_TUNNEL_PROVIDER_DESC;
253         /*
254          * Since we always want the provider to be present, it's easiest to add
255          * it as persistent object during driver load.
256          */
257         provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
258
259         status = FwpmProviderAdd(engineSession,
260                                  &provider,
261                                  NULL);
262         if (!NT_SUCCESS(status)) {
263             if (STATUS_FWP_ALREADY_EXISTS != status) {
264                 OVS_LOG_ERROR("Failed to add WFP provider, status: %x.",
265                               status);
266                 break;
267             }
268         }
269
270         status = FwpmTransactionCommit(engineSession);
271         if (!NT_SUCCESS(status)) {
272             break;
273         }
274
275         inTransaction = FALSE;
276     } while (inTransaction);
277
278     if (inTransaction){
279         FwpmTransactionAbort(engineSession);
280     }
281 }
282
283 VOID
284 OvsTunnelRemoveSystemProvider(HANDLE engineSession)
285 {
286     NTSTATUS status = STATUS_SUCCESS;
287     BOOLEAN inTransaction = FALSE;
288
289     do {
290         status = FwpmTransactionBegin(engineSession, 0);
291         if (!NT_SUCCESS(status)) {
292             break;
293         }
294         inTransaction = TRUE;
295
296         status = FwpmProviderDeleteByKey(engineSession,
297                                          &OVS_TUNNEL_PROVIDER_KEY);
298         if (!NT_SUCCESS(status)) {
299             break;
300         }
301
302         status = FwpmTransactionCommit(engineSession);
303         if (!NT_SUCCESS(status)) {
304             break;
305         }
306
307         inTransaction = FALSE;
308     } while (inTransaction);
309
310     if (inTransaction){
311         FwpmTransactionAbort(engineSession);
312     }
313 }
314
315 NTSTATUS
316 OvsTunnelAddFilter(HANDLE engineSession,
317                    PWSTR filterName,
318                    const PWSTR filterDesc,
319                    USHORT remotePort,
320                    FWP_DIRECTION direction,
321                    UINT64 context,
322                    const GUID *filterKey,
323                    const GUID *layerKey,
324                    const GUID *calloutKey,
325                    UINT64 *filterID)
326 {
327     NTSTATUS status = STATUS_SUCCESS;
328     FWPM_FILTER filter = {0};
329     FWPM_FILTER_CONDITION filterConditions[3] = {0};
330     UINT conditionIndex;
331
332     if (filterKey) {
333         filter.filterKey = *filterKey;
334     }
335     filter.layerKey = *layerKey;
336     filter.displayData.name = (wchar_t*)filterName;
337     filter.displayData.description = (wchar_t*)filterDesc;
338
339     filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
340     filter.action.calloutKey = *calloutKey;
341     filter.filterCondition = filterConditions;
342     filter.subLayerKey = OVS_TUNNEL_SUBLAYER;
343     filter.weight.type = FWP_EMPTY; // auto-weight.
344     filter.rawContext = context;
345
346     conditionIndex = 0;
347
348     filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_DIRECTION;
349     filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
350     filterConditions[conditionIndex].conditionValue.type = FWP_UINT32;
351     filterConditions[conditionIndex].conditionValue.uint32 = direction;
352
353     conditionIndex++;
354
355     filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
356     filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
357     filterConditions[conditionIndex].conditionValue.type = FWP_UINT16;
358     filterConditions[conditionIndex].conditionValue.uint16 = remotePort;
359
360     conditionIndex++;
361
362     filter.numFilterConditions = conditionIndex;
363
364     status = FwpmFilterAdd(engineSession,
365                            &filter,
366                            NULL,
367                            filterID);
368
369     return status;
370 }
371
372 /*
373  * --------------------------------------------------------------------------
374  * This function registers callouts for intercepting UDP traffic at WFP
375  * FWPM_LAYER_DATAGRAM_DATA_V4 layer.
376  * --------------------------------------------------------------------------
377  */
378 NTSTATUS
379 OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
380                                       const GUID *calloutKey,
381                                       VOID *deviceObject,
382                                       UINT32 *calloutId)
383 {
384     NTSTATUS status = STATUS_SUCCESS;
385
386     FWPS_CALLOUT sCallout = {0};
387     FWPM_CALLOUT mCallout = {0};
388
389     FWPM_DISPLAY_DATA displayData = {0};
390
391     BOOLEAN calloutRegistered = FALSE;
392
393     sCallout.calloutKey = *calloutKey;
394     sCallout.classifyFn = OvsTunnelClassify;
395     sCallout.notifyFn = OvsTunnelNotify;
396 #if FLOW_CONTEXT
397     /* Currently we don't associate a context with the flow */
398     sCallout.flowDeleteFn = OvsTunnelFlowDelete;
399     sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
400 #endif
401
402     status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId);
403     if (!NT_SUCCESS(status)) {
404         goto Exit;
405     }
406     calloutRegistered = TRUE;
407
408     displayData.name = L"Datagram-Data OVS Callout";
409     displayData.description = L"Proxies destination address/port for UDP";
410
411     mCallout.calloutKey = *calloutKey;
412     mCallout.displayData = displayData;
413     mCallout.applicableLayer = *layerKey;
414
415     status = FwpmCalloutAdd(gEngineHandle, &mCallout, NULL, NULL);
416     if (!NT_SUCCESS(status)) {
417         goto Exit;
418     }
419
420 Exit:
421
422     if (!NT_SUCCESS(status)){
423         if (calloutRegistered) {
424             FwpsCalloutUnregisterById(*calloutId);
425             *calloutId = 0;
426         }
427     }
428
429     return status;
430 }
431
432 /*
433  * --------------------------------------------------------------------------
434  * This function registers non-dynamic callouts for intercepting UDP traffic.
435  * Callouts will be removed during un-initializing phase.
436  * --------------------------------------------------------------------------
437  */
438 NTSTATUS
439 OvsTunnelRegisterCallouts(VOID *deviceObject)
440 {
441     NTSTATUS        status = STATUS_SUCCESS;
442     BOOLEAN         inTransaction = FALSE;
443     FWPM_SUBLAYER   OvsTunnelSubLayer;
444
445     status = FwpmTransactionBegin(gEngineHandle, 0);
446     if (!NT_SUCCESS(status)) {
447         goto Exit;
448     }
449     inTransaction = TRUE;
450
451     RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER));
452
453     OvsTunnelSubLayer.subLayerKey = OVS_TUNNEL_SUBLAYER;
454     OvsTunnelSubLayer.displayData.name = L"Datagram-Data OVS Sub-Layer";
455     OvsTunnelSubLayer.displayData.description =
456         L"Sub-Layer for use by Datagram-Data OVS callouts";
457     OvsTunnelSubLayer.flags = 0;
458     OvsTunnelSubLayer.weight = FWP_EMPTY; /* auto-weight */
459
460     status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL);
461     if (!NT_SUCCESS(status)) {
462         goto Exit;
463     }
464
465     /* In order to use this callout a socket must be opened. */
466     status = OvsTunnelRegisterDatagramDataCallouts(&FWPM_LAYER_DATAGRAM_DATA_V4,
467                                                    &OVS_TUNNEL_CALLOUT_V4,
468                                                    deviceObject,
469                                                    &gCalloutIdV4);
470     if (!NT_SUCCESS(status)) {
471         goto Exit;
472     }
473
474     status = FwpmTransactionCommit(gEngineHandle);
475     if (!NT_SUCCESS(status)){
476         goto Exit;
477     }
478     inTransaction = FALSE;
479
480 Exit:
481
482     if (!NT_SUCCESS(status)) {
483         if (inTransaction) {
484             FwpmTransactionAbort(gEngineHandle);
485         }
486     }
487
488     return status;
489 }
490
491 VOID
492 OvsTunnelUnregisterCallouts()
493 {
494     FwpsCalloutUnregisterById(gCalloutIdV4);
495     FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER);
496     FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
497 }
498
499 VOID
500 OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
501 {
502     UNREFERENCED_PARAMETER(driverObject);
503
504     OvsTunnelFilterStopThreads();
505
506     OvsTunnelUnregisterCallouts();
507     OvsTunnelEngineClose(&gEngineHandle);
508
509     if (gDeviceObject) {
510         IoDeleteDevice(gDeviceObject);
511     }
512 }
513
514
515 NTSTATUS
516 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
517 {
518     NTSTATUS        status = STATUS_SUCCESS;
519     UNICODE_STRING  deviceName;
520
521     RtlInitUnicodeString(&deviceName,
522                          L"\\Device\\OvsTunnelFilter");
523
524     status = IoCreateDevice(driverObject,
525                             0,
526                             &deviceName,
527                             FILE_DEVICE_NETWORK,
528                             0,
529                             FALSE,
530                             &gDeviceObject);
531
532     if (!NT_SUCCESS(status)){
533         OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.",
534                       status);
535         goto Exit;
536     }
537
538     status = OvsTunnelFilterStartThreads();
539     if (!NT_SUCCESS(status)){
540         goto Exit;
541     }
542
543     status = OvsTunnelEngineOpen(&gEngineHandle);
544     if (!NT_SUCCESS(status)){
545         goto Exit;
546     }
547
548     status = OvsTunnelRegisterCallouts(gDeviceObject);
549     if (!NT_SUCCESS(status)) {
550         OVS_LOG_ERROR("Failed to register callout, status: %x.",
551                       status);
552     }
553
554 Exit:
555
556     if (!NT_SUCCESS(status)){
557         OvsTunnelFilterUninitialize(driverObject);
558     }
559
560     return status;
561 }
562
563 /*
564  * --------------------------------------------------------------------------
565  * This function adds OVS system provider to the system if the BFE (Base
566  * Filtering Engine) is running.
567  * --------------------------------------------------------------------------
568  */
569 VOID NTAPI
570 OvsTunnelProviderBfeCallback(PVOID context,
571                              FWPM_SERVICE_STATE bfeState)
572 {
573     HANDLE engineSession = NULL;
574
575     DBG_UNREFERENCED_PARAMETER(context);
576
577     if (FWPM_SERVICE_RUNNING == bfeState) {
578         OvsTunnelEngineOpen(&engineSession);
579         if (engineSession) {
580             OvsTunnelAddSystemProvider(engineSession);
581         }
582         OvsTunnelEngineClose(&engineSession);
583     }
584 }
585
586 /*
587  * --------------------------------------------------------------------------
588  * This function registers the OvsTunnelProviderBfeCallback callback that is
589  * called whenever there is a change to the state of base filtering engine.
590  * --------------------------------------------------------------------------
591  */
592 NTSTATUS
593 OvsSubscribeTunnelProviderBfeStateChanges(PVOID deviceObject)
594 {
595     NTSTATUS status = STATUS_SUCCESS;
596
597     if (!gTunnelProviderBfeHandle) {
598         status = FwpmBfeStateSubscribeChanges(deviceObject,
599                                               OvsTunnelProviderBfeCallback,
600                                               NULL,
601                                               &gTunnelProviderBfeHandle);
602         if (!NT_SUCCESS(status)) {
603             OVS_LOG_ERROR(
604                 "Failed to subscribe BFE tunnel provider callback, status: %x.",
605                 status);
606         }
607     }
608
609     return status;
610 }
611
612 /*
613  * --------------------------------------------------------------------------
614  * This function unregisters the OvsTunnelProviderBfeCallback callback that
615  * was previously registered by OvsSubscribeTunnelProviderBfeStateChanges
616  * function.
617  * --------------------------------------------------------------------------
618  */
619 VOID
620 OvsUnsubscribeTunnelProviderBfeStateChanges()
621 {
622     NTSTATUS status = STATUS_SUCCESS;
623
624     if (gTunnelProviderBfeHandle) {
625         status = FwpmBfeStateUnsubscribeChanges(gTunnelProviderBfeHandle);
626         if (!NT_SUCCESS(status)) {
627             OVS_LOG_ERROR(
628                 "Failed to unsubscribe BFE tunnel provider callback, status: %x.",
629                 status);
630         }
631         gTunnelProviderBfeHandle = NULL;
632     }
633 }
634
635 /*
636  * --------------------------------------------------------------------------
637  * This function registers the OVS system provider if the BFE (Base Filtering
638  * Engine) is running.
639  * Otherwise, it will register the OvsTunnelProviderBfeCallback callback.
640
641  * Note: Before calling FwpmBfeStateGet, the callout driver must call the
642  * FwpmBfeStateSubscribeChanges function to register the callback function
643  * to be called whenever the state of the filter engine changes.
644  *
645  * Register WFP system provider call hierarchy:
646  * <DriverEntry>
647  *     <OvsCreateDeviceObject>
648  *         <OvsRegisterSystemProvider>
649  *             <OvsSubscribeTunnelProviderBfeStateChanges>
650  *                 --> registers OvsTunnelProviderBfeCallback callback
651  *                     <OvsTunnelProviderBfeCallback>
652  *                         --> if BFE is running:
653  *                             <OvsTunnelAddSystemProvider>
654  *             --> if BFE is running:
655  *                 <OvsTunnelAddSystemProvider>
656  *                 <OvsUnsubscribeTunnelProviderBfeStateChanges>
657  *                     --> unregisters OvsTunnelProviderBfeCallback callback
658  *
659  * --------------------------------------------------------------------------
660  */
661 VOID
662 OvsRegisterSystemProvider(PVOID deviceObject)
663 {
664     NTSTATUS status = STATUS_SUCCESS;
665     HANDLE engineSession = NULL;
666
667     status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
668     if (NT_SUCCESS(status)) {
669         if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
670             OvsTunnelEngineOpen(&engineSession);
671             if (engineSession) {
672                 OvsTunnelAddSystemProvider(engineSession);
673             }
674             OvsTunnelEngineClose(&engineSession);
675
676             OvsUnsubscribeTunnelProviderBfeStateChanges();
677         }
678     }
679 }
680
681 /*
682  * --------------------------------------------------------------------------
683  * This function removes the OVS system provider and unregisters the
684  * OvsTunnelProviderBfeCallback callback from BFE (Base Filtering Engine).
685  *
686  * Unregister WFP system provider call hierarchy:
687  * <OvsExtUnload>
688  *     <OvsDeleteDeviceObject>
689  *         <OvsUnregisterSystemProvider>
690  *             <OvsTunnelRemoveSystemProvider>
691  *             <OvsUnsubscribeTunnelProviderBfeStateChanges>
692  *                 --> unregisters OvsTunnelProviderBfeCallback callback
693  *
694  * --------------------------------------------------------------------------
695  */
696 VOID
697 OvsUnregisterSystemProvider()
698 {
699     HANDLE engineSession = NULL;
700
701     OvsTunnelEngineOpen(&engineSession);
702     if (engineSession) {
703         OvsTunnelRemoveSystemProvider(engineSession);
704     }
705     OvsTunnelEngineClose(&engineSession);
706
707     OvsUnsubscribeTunnelProviderBfeStateChanges();
708 }
709
710 /*
711  * --------------------------------------------------------------------------
712  * This function initializes the tunnel filter if the BFE is running.
713  * --------------------------------------------------------------------------
714  */
715 VOID NTAPI
716 OvsTunnelInitBfeCallback(PVOID context,
717                          FWPM_SERVICE_STATE bfeState)
718 {
719     NTSTATUS status = STATUS_SUCCESS;
720     PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) context;
721
722     if (FWPM_SERVICE_RUNNING == bfeState) {
723         status = OvsTunnelFilterInitialize(driverObject);
724         if (!NT_SUCCESS(status)) {
725             OVS_LOG_ERROR(
726                 "Failed to initialize tunnel filter, status: %x.",
727                 status);
728         }
729     }
730 }
731
732 /*
733  * --------------------------------------------------------------------------
734  * This function registers the OvsTunnelInitBfeCallback callback that is
735  * called whenever there is a change to the state of base filtering engine.
736  * --------------------------------------------------------------------------
737  */
738 NTSTATUS
739 OvsSubscribeTunnelInitBfeStateChanges(PDRIVER_OBJECT driverObject,
740                                       PVOID deviceObject)
741 {
742     NTSTATUS status = STATUS_SUCCESS;
743
744     if (!gTunnelInitBfeHandle) {
745         status = FwpmBfeStateSubscribeChanges(deviceObject,
746                                               OvsTunnelInitBfeCallback,
747                                               driverObject,
748                                               &gTunnelInitBfeHandle);
749         if (!NT_SUCCESS(status)) {
750             OVS_LOG_ERROR(
751                 "Failed to subscribe BFE tunnel init callback, status: %x.",
752                 status);
753         }
754     }
755
756     return status;
757 }
758
759 /*
760  * --------------------------------------------------------------------------
761  * This function unregisters the OvsTunnelInitBfeCallback callback that
762  * was previously registered by OvsSubscribeTunnelInitBfeStateChanges
763  * function.
764  * --------------------------------------------------------------------------
765  */
766 VOID
767 OvsUnsubscribeTunnelInitBfeStateChanges()
768 {
769     NTSTATUS status = STATUS_SUCCESS;
770
771     if (gTunnelInitBfeHandle) {
772         status = FwpmBfeStateUnsubscribeChanges(gTunnelInitBfeHandle);
773         if (!NT_SUCCESS(status)) {
774             OVS_LOG_ERROR(
775                 "Failed to unsubscribe BFE tunnel init callback, status: %x.",
776                 status);
777         }
778         gTunnelInitBfeHandle = NULL;
779     }
780 }
781
782 /*
783  * --------------------------------------------------------------------------
784  * This function initializes the OVS tunnel filter if the BFE (Base Filtering
785  * Engine) is running.
786  * Otherwise, it will register the OvsTunnelInitBfeCallback callback.
787
788  * Note: Before calling FwpmBfeStateGet, the callout driver must call the
789  * FwpmBfeStateSubscribeChanges function to register the callback function
790  * to be called whenever the state of the filter engine changes.
791  *
792  * Initialize OVS tunnel filter call hierarchy:
793  * <OvsExtAttach>
794  *     <OvsCreateSwitch>
795  *         <OvsInitTunnelFilter>
796  *             <OvsSubscribeTunnelInitBfeStateChanges>
797  *                 --> registers OvsTunnelInitBfeCallback callback
798  *                     <OvsTunnelInitBfeCallback>
799  *                         --> if BFE is running:
800  *                             <OvsTunnelFilterInitialize>
801  *                                 <IoCreateDevice>
802  *                                 <OvsTunnelFilterStartThreads>
803  *                                 <OvsTunnelRegisterCallouts>
804  *             --> if BFE is running:
805  *                 <OvsTunnelFilterInitialize>
806  *                     <IoCreateDevice>
807  *                     <OvsTunnelFilterStartThreads>
808  *                     <OvsTunnelRegisterCallouts>
809  *                 <OvsUnsubscribeTunnelInitBfeStateChanges>
810  *                     --> unregisters OvsTunnelInitBfeCallback callback
811  *
812  * --------------------------------------------------------------------------
813  */
814 NTSTATUS
815 OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject)
816 {
817     NTSTATUS status = STATUS_SUCCESS;
818
819     status = OvsSubscribeTunnelInitBfeStateChanges(driverObject, deviceObject);
820     if (NT_SUCCESS(status)) {
821         if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
822             status = OvsTunnelFilterInitialize(driverObject);
823             if (!NT_SUCCESS(status)) {
824                 /* XXX: We need to decide what actions to take in case of
825                  * failure to initialize tunnel filter. */
826                 ASSERT(status == NDIS_STATUS_SUCCESS);
827                 OVS_LOG_ERROR(
828                     "Failed to initialize tunnel filter, status: %x.",
829                     status);
830             }
831             OvsUnsubscribeTunnelInitBfeStateChanges();
832         }
833     }
834
835     return status;
836 }
837
838 /*
839  * --------------------------------------------------------------------------
840  * This function uninitializes the OVS tunnel filter and unregisters the
841  * OvsTunnelInitBfeCallback callback from BFE.
842  *
843  * Uninitialize OVS tunnel filter call hierarchy:
844  * <OvsExtDetach>
845  *     <OvsDeleteSwitch>
846  *         <OvsUninitTunnelFilter>
847  *             <OvsTunnelFilterUninitialize>
848  *                 <OvsTunnelFilterStopThreads>
849  *                 <OvsTunnelUnregisterCallouts>
850  *                 <IoDeleteDevice>
851  *             <OvsUnsubscribeTunnelInitBfeStateChanges>
852  *                 --> unregisters OvsTunnelInitBfeCallback callback
853  *
854  * --------------------------------------------------------------------------
855  */
856 VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
857 {
858     OvsTunnelFilterUninitialize(driverObject);
859     OvsUnsubscribeTunnelInitBfeStateChanges();
860 }
861
862 NTSTATUS
863 OvsTunnelAddFilterEx(HANDLE engineSession,
864                      UINT32 filterPort,
865                      UINT64 *filterID)
866 {
867     NTSTATUS status = STATUS_SUCCESS;
868
869     status = OvsTunnelAddFilter(engineSession,
870                                 L"Datagram-Data OVS Filter (Inbound)",
871                                 L"address/port for UDP",
872                                 (USHORT)filterPort,
873                                 FWP_DIRECTION_INBOUND,
874                                 0,
875                                 NULL,
876                                 &FWPM_LAYER_DATAGRAM_DATA_V4,
877                                 &OVS_TUNNEL_CALLOUT_V4,
878                                 filterID);
879     if (!NT_SUCCESS(status)) {
880         OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.",
881                       filterPort, status);
882     } else {
883         OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.",
884                      filterPort, *filterID);
885     }
886
887     return status;
888 }
889
890 NTSTATUS
891 OvsTunnelRemoveFilterEx(HANDLE engineSession,
892                         UINT64 filterID)
893 {
894     NTSTATUS status = STATUS_SUCCESS;
895     BOOLEAN  error = TRUE;
896
897     do {
898         if (filterID == 0) {
899             OVS_LOG_INFO("No tunnel filter to remove.");
900             break;
901         }
902
903         status = FwpmFilterDeleteById(engineSession, filterID);
904         if (!NT_SUCCESS(status)) {
905             OVS_LOG_ERROR("Failed to remove tunnel with filter ID: %d,\
906                            status: %x.", filterID, status);
907             break;
908         }
909         OVS_LOG_INFO("Filter removed, filter ID: %d.",
910                      filterID);
911
912         error = FALSE;
913     } while (error);
914
915     return status;
916 }
917
918 NTSTATUS
919 OvsTunnelFilterExecuteAction(HANDLE engineSession,
920                              POVS_TUNFLT_REQUEST request)
921 {
922     NTSTATUS status = STATUS_SUCCESS;
923
924     switch (request->operation)
925     {
926     case OVS_TUN_FILTER_CREATE:
927         status = OvsTunnelAddFilterEx(engineSession,
928                                       request->port,
929                                       request->filterID.addID);
930         break;
931     case OVS_TUN_FILTER_DELETE:
932         status = OvsTunnelRemoveFilterEx(engineSession,
933                                          request->filterID.delID);
934         break;
935     default:
936         status = STATUS_NOT_SUPPORTED;
937         break;
938     }
939
940     return status;
941 }
942
943 /*
944  * --------------------------------------------------------------------------
945  * This function pops the whole request entries from the queue and returns the
946  * number of entries through the 'count' parameter. The operation is
947  * synchronized using request list spinlock.
948  * --------------------------------------------------------------------------
949  */
950 VOID
951 OvsTunnelFilterRequestPopList(POVS_TUNFLT_REQUEST_LIST listRequests,
952                               PLIST_ENTRY head,
953                               UINT32 *count)
954 {
955     NdisAcquireSpinLock(&listRequests->spinlock);
956
957     if (!IsListEmpty(&listRequests->head)) {
958         PLIST_ENTRY PrevEntry;
959         PLIST_ENTRY NextEntry;
960
961         NextEntry = listRequests->head.Flink;
962         PrevEntry = listRequests->head.Blink;
963
964         head->Flink = NextEntry;
965         NextEntry->Blink = head;
966
967         head->Blink = PrevEntry;
968         PrevEntry->Flink = head;
969
970         *count = listRequests->numEntries;
971
972         InitializeListHead(&listRequests->head);
973         listRequests->numEntries = 0;
974     }
975
976     NdisReleaseSpinLock(&listRequests->spinlock);
977 }
978
979 /*
980  * --------------------------------------------------------------------------
981  * This function pushes the received request to the list while holding the
982  * request list spinlock.
983  * --------------------------------------------------------------------------
984  */
985 VOID
986 OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests,
987                            POVS_TUNFLT_REQUEST request)
988 {
989     NdisAcquireSpinLock(&listRequests->spinlock);
990
991     InsertTailList(&listRequests->head, &(request->entry));
992     listRequests->numEntries++;
993
994     NdisReleaseSpinLock(&listRequests->spinlock);
995 }
996
997 /*
998  * --------------------------------------------------------------------------
999  * This function pushes the received request to the corresponding thread
1000  * request queue. The arrival of the new request is signaled to the thread,
1001  * in order to start processing it.
1002  *
1003  * For a uniform distribution of requests to thread queues, a thread index is
1004  * calculated based on the received destination port.
1005  * --------------------------------------------------------------------------
1006  */
1007 VOID
1008 OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request)
1009 {
1010     UINT32 threadIndex;
1011
1012     threadIndex = request->port % OVS_TUNFLT_MAX_THREADS;
1013
1014     OvsTunnelFilterRequestPush(
1015         &gTunnelThreadCtx[threadIndex].listRequests,
1016         request);
1017
1018     KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent,
1019                IO_NO_INCREMENT,
1020                FALSE);
1021 }
1022
1023 VOID
1024 OvsTunnelFilterCompleteRequest(PIRP irp,
1025                                PFNTunnelVportPendingOp callback,
1026                                PVOID context,
1027                                NTSTATUS status)
1028 {
1029     UINT32 replyLen = 0;
1030
1031     if (callback) {
1032         callback(context, status, &replyLen);
1033         /* Release the context passed to the callback function. */
1034         OvsFreeMemory(context);
1035     }
1036
1037     if (irp) {
1038         OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
1039     }
1040 }
1041
1042 VOID
1043 OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1044 {
1045     POVS_TUNFLT_REQUEST request = NULL;
1046     PLIST_ENTRY         link = NULL;
1047     PLIST_ENTRY         next = NULL;
1048     LIST_ENTRY          head;
1049     NTSTATUS            status = STATUS_SUCCESS;
1050     UINT32              count = 0;
1051     BOOLEAN             inTransaction = FALSE;
1052     BOOLEAN             error = TRUE;
1053
1054     do
1055     {
1056         if (!InterlockedCompareExchange(
1057             (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) {
1058             OVS_LOG_INFO("Nothing to do... request list is empty.");
1059             break;
1060         }
1061
1062         status = FwpmTransactionBegin(threadCtx->engineSession, 0);
1063         if (!NT_SUCCESS(status)) {
1064             OVS_LOG_ERROR("Failed to start transaction, status: %x.",
1065                           status);
1066             break;
1067         }
1068         inTransaction = TRUE;
1069
1070         InitializeListHead(&head);
1071         OvsTunnelFilterRequestPopList(&threadCtx->listRequests, &head, &count);
1072
1073         LIST_FORALL_SAFE(&head, link, next) {
1074             request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry);
1075
1076             status = OvsTunnelFilterExecuteAction(threadCtx->engineSession,
1077                                                   request);
1078             if (!NT_SUCCESS(status)) {
1079                 RemoveEntryList(&request->entry);
1080                 count--;
1081
1082                 /* Complete the IRP with the failure status. */
1083                 OvsTunnelFilterCompleteRequest(request->irp,
1084                                                request->callback,
1085                                                request->context,
1086                                                status);
1087                 OvsFreeMemory(request);
1088                 request = NULL;
1089             } else {
1090                 error = FALSE;
1091             }
1092         }
1093
1094         if (error) {
1095             /* No successful requests were made, so there is no point to commit
1096              * the transaction. */
1097             break;
1098         }
1099
1100         status = FwpmTransactionCommit(threadCtx->engineSession);
1101         if (!NT_SUCCESS(status)){
1102             OVS_LOG_ERROR("Failed to commit transaction, status: %x.",
1103                           status);
1104             break;
1105         }
1106
1107         inTransaction = FALSE;
1108     } while (inTransaction);
1109
1110     if (inTransaction) {
1111         FwpmTransactionAbort(threadCtx->engineSession);
1112         OVS_LOG_ERROR("Failed to execute request, status: %x.\
1113                        Transaction aborted.", status);
1114     }
1115
1116     /* Complete the requests successfully executed with the transaction commit
1117      * status. */
1118     while (count) {
1119         request = (POVS_TUNFLT_REQUEST)RemoveHeadList(&head);
1120         count--;
1121
1122         OvsTunnelFilterCompleteRequest(request->irp,
1123                                        request->callback,
1124                                        request->context,
1125                                        status);
1126         OvsFreeMemory(request);
1127         request = NULL;
1128     }
1129 }
1130
1131 /*
1132  *----------------------------------------------------------------------------
1133  * System thread routine that processes thread's requests queue. The thread
1134  * routine initializes thread's necessary data and waits on two events,
1135  * requestEvent and stopEvent. Whenever a request is pushed to the thread's
1136  * queue, the requestEvent is signaled and the thread routine starts processing
1137  * the arrived requests. When stopEvent is signaled, all subsequent requests
1138  * are completed with STATUS_CANCELED, without being added to the thread's
1139  * queue, and the routine finishes processing all existing requests from the
1140  * queue before uninitializing the thread and exiting.
1141  *----------------------------------------------------------------------------
1142  */
1143 _Use_decl_annotations_
1144 VOID
1145 OvsTunnelFilterThreadProc(PVOID context)
1146 {
1147     NTSTATUS                   status = STATUS_SUCCESS;
1148     POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context;
1149     PKEVENT                    eventArray[2] = { 0 };
1150     ULONG                      count = 0;
1151     BOOLEAN                    exit = FALSE;
1152     BOOLEAN                    error = TRUE;
1153
1154     OVS_LOG_INFO("Starting OVS Tunnel system thread %d.",
1155                  threadCtx->threadID);
1156
1157     eventArray[0] = &threadCtx->stopEvent;
1158     eventArray[1] = &threadCtx->requestEvent;
1159     count = ARRAY_SIZE(eventArray);
1160
1161     do {
1162         status = OvsTunnelFilterThreadInit(threadCtx);
1163         if (!NT_SUCCESS(status)) {
1164             OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.",
1165                 threadCtx->threadID);
1166             break;
1167         }
1168
1169         do {
1170             status = KeWaitForMultipleObjects(count,
1171                                               (PVOID)eventArray,
1172                                               WaitAny,
1173                                               Executive,
1174                                               KernelMode,
1175                                               FALSE,
1176                                               NULL,
1177                                               NULL);
1178             switch (status) {
1179                 case STATUS_WAIT_1:
1180                     /* Start processing requests. */
1181                     OvsTunnelFilterRequestListProcess(threadCtx);
1182                     break;
1183                 default:
1184                     /* Finish processing the received requests and exit. */
1185                     OvsTunnelFilterRequestListProcess(threadCtx);
1186                     exit = TRUE;
1187                     break;
1188             }
1189         } while (!exit);
1190
1191         OvsTunnelFilterThreadUninit(threadCtx);
1192
1193         error = FALSE;
1194     } while (error);
1195
1196     OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.",
1197                  threadCtx->threadID);
1198
1199     PsTerminateSystemThread(STATUS_SUCCESS);
1200 };
1201
1202 static NTSTATUS
1203 OvsTunnelFilterStartThreads()
1204 {
1205     NTSTATUS status = STATUS_SUCCESS;
1206
1207     for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1208         gTunnelThreadCtx[index].threadID = index;
1209
1210         status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]);
1211         if (!NT_SUCCESS(status)) {
1212             OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index);
1213             break;
1214         }
1215     }
1216
1217     return status;
1218 }
1219
1220 static NTSTATUS
1221 OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1222 {
1223     NTSTATUS    status = STATUS_SUCCESS;
1224     HANDLE      threadHandle = NULL;
1225     BOOLEAN     error = TRUE;
1226
1227     do {
1228         status = PsCreateSystemThread(&threadHandle,
1229                                       SYNCHRONIZE,
1230                                       NULL,
1231                                       NULL,
1232                                       NULL,
1233                                       OvsTunnelFilterThreadProc,
1234                                       threadCtx);
1235         if (!NT_SUCCESS(status)) {
1236             OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
1237                           status);
1238             break;
1239         }
1240
1241         ObReferenceObjectByHandle(threadHandle,
1242                                   SYNCHRONIZE,
1243                                   NULL,
1244                                   KernelMode,
1245                                   &threadCtx->threadObject,
1246                                   NULL);
1247         ZwClose(threadHandle);
1248         threadHandle = NULL;
1249
1250         error = FALSE;
1251     } while (error);
1252
1253     return status;
1254 }
1255
1256 static VOID
1257 OvsTunnelFilterStopThreads()
1258 {
1259     /* Signal all threads to stop and ignore all subsequent requests. */
1260     for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1261         OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], TRUE);
1262     }
1263
1264     /* Wait for all threads to finish processing the requests. */
1265     for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1266         OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], FALSE);
1267     }
1268 }
1269
1270 static VOID
1271 OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
1272                           BOOLEAN signalEvent)
1273 {
1274     if (signalEvent) {
1275         /* Signal stop thread event. */
1276         OVS_LOG_INFO("Received stop event for OVS Tunnel system thread %d.",
1277                      threadCtx->threadID);
1278         KeSetEvent(&threadCtx->stopEvent, IO_NO_INCREMENT, FALSE);
1279     } else {
1280         /* Wait for the tunnel thread to finish. */
1281         KeWaitForSingleObject(threadCtx->threadObject,
1282                               Executive,
1283                               KernelMode,
1284                               FALSE,
1285                               NULL);
1286
1287         ObDereferenceObject(threadCtx->threadObject);
1288     }
1289 }
1290
1291 /*
1292  * --------------------------------------------------------------------------
1293  * This function initializes thread's necessary data. Each thread has its own
1294  * session object to the BFE that is used for processing the requests from
1295  * the thread's queue.
1296  * --------------------------------------------------------------------------
1297  */
1298 static NTSTATUS
1299 OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1300 {
1301     NTSTATUS status = STATUS_SUCCESS;
1302     BOOLEAN error = TRUE;
1303
1304     do {
1305         /* Create thread's engine session object. */
1306         status = OvsTunnelEngineOpen(&threadCtx->engineSession);
1307         if (!NT_SUCCESS(status)) {
1308             break;
1309         }
1310
1311         NdisAllocateSpinLock(&threadCtx->listRequests.spinlock);
1312
1313         InitializeListHead(&threadCtx->listRequests.head);
1314
1315         KeInitializeEvent(&threadCtx->stopEvent,
1316             NotificationEvent,
1317             FALSE);
1318
1319         KeInitializeEvent(&threadCtx->requestEvent,
1320             SynchronizationEvent,
1321             FALSE);
1322
1323         error = FALSE;
1324     } while (error);
1325
1326     return status;
1327 }
1328
1329 /*
1330  * --------------------------------------------------------------------------
1331  * This function uninitializes thread's private data. Thread's engine session
1332  * handle is closed and set to NULL.
1333  * --------------------------------------------------------------------------
1334  */
1335 static VOID
1336 OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1337 {
1338     if (threadCtx->engineSession) {
1339         /* Close thread's FWPM session. */
1340         OvsTunnelEngineClose(&threadCtx->engineSession);
1341
1342         NdisFreeSpinLock(&threadCtx->listRequests.spinlock);
1343     }
1344 }
1345
1346 /*
1347  * --------------------------------------------------------------------------
1348  * This function creates a new tunnel filter request and push it to a thread
1349  * queue. If the thread stop event is signaled, the request is completed with
1350  * STATUS_CANCELLED without pushing it to any queue.
1351  * --------------------------------------------------------------------------
1352  */
1353 NTSTATUS
1354 OvsTunnelFilterQueueRequest(PIRP irp,
1355                             UINT16 remotePort,
1356                             UINT64 *filterID,
1357                             OVS_TUNFLT_OPERATION operation,
1358                             PFNTunnelVportPendingOp callback,
1359                             PVOID tunnelContext)
1360 {
1361     POVS_TUNFLT_REQUEST request = NULL;
1362     NTSTATUS            status = STATUS_PENDING;
1363     BOOLEAN             error = TRUE;
1364     UINT64              timeout = 0;
1365
1366     do {
1367         /* Verify if the stop event was signaled. */
1368         if (STATUS_SUCCESS == KeWaitForSingleObject(
1369                                   &gTunnelThreadCtx[0].stopEvent,
1370                                   Executive,
1371                                   KernelMode,
1372                                   FALSE,
1373                                   (LARGE_INTEGER *)&timeout)) {
1374             /* The stop event is signaled. Completed the IRP with
1375              * STATUS_CANCELLED. */
1376             status = STATUS_CANCELLED;
1377             break;
1378         }
1379
1380         if (NULL == filterID) {
1381             OVS_LOG_ERROR("Invalid request.");
1382             status = STATUS_INVALID_PARAMETER;
1383             break;
1384         }
1385
1386         request = (POVS_TUNFLT_REQUEST) OvsAllocateMemory(sizeof(*request));
1387         if (NULL == request) {
1388             OVS_LOG_ERROR("Failed to allocate list item.");
1389             status = STATUS_INSUFFICIENT_RESOURCES;
1390             break;
1391         }
1392
1393         request->port = remotePort;
1394         request->operation = operation;
1395         switch (operation) {
1396             case OVS_TUN_FILTER_CREATE:
1397                 request->filterID.addID = filterID;
1398                 break;
1399             case OVS_TUN_FILTER_DELETE:
1400                 request->filterID.delID = *filterID;
1401                 break;
1402         }
1403         request->irp = irp;
1404         request->callback = callback;
1405         request->context = tunnelContext;
1406
1407         OvsTunnelFilterThreadPush(request);
1408
1409         error = FALSE;
1410     } while (error);
1411
1412     if (error) {
1413         OvsTunnelFilterCompleteRequest(irp, callback, tunnelContext, status);
1414         if (request) {
1415             OvsFreeMemory(request);
1416             request = NULL;
1417         }
1418     }
1419
1420     return status;
1421 }
1422
1423 /*
1424  * --------------------------------------------------------------------------
1425  *  This function adds a new WFP filter for the received port and returns the
1426  *  ID of the created WFP filter.
1427  *
1428  *  Note:
1429  *  All necessary calls to the WFP filtering engine must be running at IRQL =
1430  *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1431  *  we register an OVS_TUN_FILTER_CREATE request that will be processed by
1432  *  the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1433  *
1434  * OVS VXLAN port add call hierarchy:
1435  * <OvsNewVportCmdHandler>
1436  *     <OvsInitTunnelVport>
1437  *         <OvsInitVxlanTunnel>
1438  *             <OvsTunelFilterCreate>
1439  *                 <OvsTunnelFilterQueueRequest>
1440  *                     --> if thread STOP event is signalled:
1441  *                         --> Complete request with STATUS_CANCELLED
1442  *                         --> EXIT
1443  *                     <OvsTunnelFilterThreadPush>
1444  *                         --> add the request to one of tunnel thread queues
1445  *
1446  * --------------------------------------------------------------------------
1447  */
1448 NTSTATUS
1449 OvsTunelFilterCreate(PIRP irp,
1450                      UINT16 filterPort,
1451                      UINT64 *filterID,
1452                      PFNTunnelVportPendingOp callback,
1453                      PVOID tunnelContext)
1454 {
1455     return OvsTunnelFilterQueueRequest(irp,
1456                                        filterPort,
1457                                        filterID,
1458                                        OVS_TUN_FILTER_CREATE,
1459                                        callback,
1460                                        tunnelContext);
1461 }
1462
1463 /*
1464  * --------------------------------------------------------------------------
1465  *  This function removes a WFP filter using the received filter ID.
1466  *
1467  *  Note:
1468  *  All necessary calls to the WFP filtering engine must be running at IRQL =
1469  *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1470  *  we register an OVS_TUN_FILTER_DELETE request that will be processed by
1471  *  the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1472  *
1473  * OVS VXLAN port delete call hierarchy:
1474  * <OvsDeleteVportCmdHandler>
1475  *     <OvsRemoveAndDeleteVport>
1476  *         <OvsCleanupVxlanTunnel>
1477  *             <OvsTunelFilterCreate>
1478  *                 <OvsTunnelFilterQueueRequest>
1479  *                     --> if thread STOP event is signalled:
1480  *                         --> Complete request with STATUS_CANCELLED
1481  *                         --> EXIT
1482  *                     <OvsTunnelFilterThreadPush>
1483  *                         --> add the request to one of tunnel thread queues
1484  *
1485  * --------------------------------------------------------------------------
1486  */
1487 NTSTATUS
1488 OvsTunelFilterDelete(PIRP irp,
1489                      UINT64 filterID,
1490                      PFNTunnelVportPendingOp callback,
1491                      PVOID tunnelContext)
1492 {
1493     return OvsTunnelFilterQueueRequest(irp,
1494                                        0,
1495                                        &filterID,
1496                                        OVS_TUN_FILTER_DELETE,
1497                                        callback,
1498                                        tunnelContext);
1499 }