netdev-dpdk: fix mbuf leaks
[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 initialization flag. */
151     UINT32                  isInitialized;
152     /* Thread's engine session handle. */
153     HANDLE                  engineSession;
154     /* Reference of the thread object. */
155     PVOID                   threadObject;
156     /* Requests queue list. */
157     OVS_TUNFLT_REQUEST_LIST listRequests;
158     /* Event signaling that there are requests to process. */
159     KEVENT                  requestEvent;
160     /* Event for stopping thread execution. */
161     KEVENT                  stopEvent;
162 } OVS_TUNFLT_THREAD_CONTEXT, *POVS_TUNFLT_THREAD_CONTEXT;
163
164 KSTART_ROUTINE  OvsTunnelFilterThreadProc;
165
166 static NTSTATUS OvsTunnelFilterStartThreads();
167 static NTSTATUS OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
168 static VOID     OvsTunnelFilterStopThreads();
169 static VOID     OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
170                                           BOOLEAN signalEvent);
171 static NTSTATUS OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
172 static VOID     OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
173 static VOID     OvsTunnelFilterSetIrpContext(POVS_TUNFLT_REQUEST_LIST listRequests,
174                                              POVS_TUNFLT_REQUEST request);
175 static VOID     OvsTunnelFilterCancelIrp(PDEVICE_OBJECT DeviceObject,
176                                          PIRP Irp);
177
178 /*
179  * Callout driver global variables
180  */
181
182 /* Pointer to the device object that must be create before we can register our
183  * callout to the base filtering engine. */
184 static PDEVICE_OBJECT            gDeviceObject = NULL;
185 /* Handle to an open session to the filter engine that is used for adding
186  * tunnel's callout. */
187 static HANDLE                    gEngineHandle = NULL;
188 /* A pointer to the received handle that is associated with the registration of
189  * the OvsTunnelProviderBfeCallback callback. */
190 static HANDLE                    gTunnelProviderBfeHandle = NULL;
191 /* A pointer to the received handle that is associated with the registration of
192  * the OvsTunnelInitBfeCallback callback. */
193 static HANDLE                    gTunnelInitBfeHandle = NULL;
194 /* Runtime identifier for tunnel's callout which is retrieved at tunnel
195  * initialization phase when the callout is registered. This ID is then used
196  * for removing the callout object from the system at tunnel
197  * uninitialization phase. */
198 static UINT32                    gCalloutIdV4 = 0;
199 /* Array used for storing tunnel thread's private data. */
200 static OVS_TUNFLT_THREAD_CONTEXT gTunnelThreadCtx[OVS_TUNFLT_MAX_THREADS] = { 0 };
201
202 /*
203  * Callout driver implementation.
204  */
205
206 NTSTATUS
207 OvsTunnelEngineOpen(HANDLE *engineSession)
208 {
209     NTSTATUS status = STATUS_SUCCESS;
210     FWPM_SESSION session = { 0 };
211
212     /*
213     * Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT
214     * errors while waiting to acquire the transaction lock.
215     */
216     session.txnWaitTimeoutInMSec = INFINITE;
217
218     /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
219     status = FwpmEngineOpen(NULL,
220                             RPC_C_AUTHN_DEFAULT,
221                             NULL,
222                             &session,
223                             engineSession);
224     if (!NT_SUCCESS(status)) {
225         OVS_LOG_ERROR("Failed to open filtering engine session, status: %x.",
226                       status);
227     }
228
229     return status;
230 }
231
232 VOID
233 OvsTunnelEngineClose(HANDLE *engineSession)
234 {
235     if (*engineSession) {
236         FwpmEngineClose(*engineSession);
237         *engineSession = NULL;
238     }
239 }
240
241 VOID
242 OvsTunnelAddSystemProvider(HANDLE engineSession)
243 {
244     NTSTATUS status = STATUS_SUCCESS;
245     BOOLEAN inTransaction = FALSE;
246     FWPM_PROVIDER provider = { 0 };
247
248     do {
249         status = FwpmTransactionBegin(engineSession, 0);
250         if (!NT_SUCCESS(status)) {
251             break;
252         }
253         inTransaction = TRUE;
254
255         memset(&provider, 0, sizeof(provider));
256         provider.providerKey = OVS_TUNNEL_PROVIDER_KEY;
257         provider.displayData.name = OVS_TUNNEL_PROVIDER_NAME;
258         provider.displayData.description = OVS_TUNNEL_PROVIDER_DESC;
259         /*
260          * Since we always want the provider to be present, it's easiest to add
261          * it as persistent object during driver load.
262          */
263         provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
264
265         status = FwpmProviderAdd(engineSession,
266                                  &provider,
267                                  NULL);
268         if (!NT_SUCCESS(status)) {
269             if (STATUS_FWP_ALREADY_EXISTS != status) {
270                 OVS_LOG_ERROR("Failed to add WFP provider, status: %x.",
271                               status);
272                 break;
273             }
274         }
275
276         status = FwpmTransactionCommit(engineSession);
277         if (!NT_SUCCESS(status)) {
278             break;
279         }
280
281         inTransaction = FALSE;
282     } while (inTransaction);
283
284     if (inTransaction){
285         FwpmTransactionAbort(engineSession);
286     }
287 }
288
289 VOID
290 OvsTunnelRemoveSystemProvider(HANDLE engineSession)
291 {
292     NTSTATUS status = STATUS_SUCCESS;
293     BOOLEAN inTransaction = FALSE;
294
295     do {
296         status = FwpmTransactionBegin(engineSession, 0);
297         if (!NT_SUCCESS(status)) {
298             break;
299         }
300         inTransaction = TRUE;
301
302         status = FwpmProviderDeleteByKey(engineSession,
303                                          &OVS_TUNNEL_PROVIDER_KEY);
304         if (!NT_SUCCESS(status)) {
305             break;
306         }
307
308         status = FwpmTransactionCommit(engineSession);
309         if (!NT_SUCCESS(status)) {
310             break;
311         }
312
313         inTransaction = FALSE;
314     } while (inTransaction);
315
316     if (inTransaction){
317         FwpmTransactionAbort(engineSession);
318     }
319 }
320
321 NTSTATUS
322 OvsTunnelAddFilter(HANDLE engineSession,
323                    PWSTR filterName,
324                    const PWSTR filterDesc,
325                    USHORT remotePort,
326                    FWP_DIRECTION direction,
327                    UINT64 context,
328                    const GUID *filterKey,
329                    const GUID *layerKey,
330                    const GUID *calloutKey,
331                    UINT64 *filterID)
332 {
333     NTSTATUS status = STATUS_SUCCESS;
334     FWPM_FILTER filter = {0};
335     FWPM_FILTER_CONDITION filterConditions[3] = {0};
336     UINT conditionIndex;
337
338     if (filterKey) {
339         filter.filterKey = *filterKey;
340     }
341     filter.layerKey = *layerKey;
342     filter.displayData.name = (wchar_t*)filterName;
343     filter.displayData.description = (wchar_t*)filterDesc;
344
345     filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
346     filter.action.calloutKey = *calloutKey;
347     filter.filterCondition = filterConditions;
348     filter.subLayerKey = OVS_TUNNEL_SUBLAYER;
349     filter.weight.type = FWP_EMPTY; // auto-weight.
350     filter.rawContext = context;
351
352     conditionIndex = 0;
353
354     filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_DIRECTION;
355     filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
356     filterConditions[conditionIndex].conditionValue.type = FWP_UINT32;
357     filterConditions[conditionIndex].conditionValue.uint32 = direction;
358
359     conditionIndex++;
360
361     filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
362     filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
363     filterConditions[conditionIndex].conditionValue.type = FWP_UINT16;
364     filterConditions[conditionIndex].conditionValue.uint16 = remotePort;
365
366     conditionIndex++;
367
368     filter.numFilterConditions = conditionIndex;
369
370     status = FwpmFilterAdd(engineSession,
371                            &filter,
372                            NULL,
373                            filterID);
374
375     return status;
376 }
377
378 /*
379  * --------------------------------------------------------------------------
380  * This function registers callouts for intercepting UDP traffic at WFP
381  * FWPM_LAYER_DATAGRAM_DATA_V4 layer.
382  * --------------------------------------------------------------------------
383  */
384 NTSTATUS
385 OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
386                                       const GUID *calloutKey,
387                                       VOID *deviceObject,
388                                       UINT32 *calloutId)
389 {
390     NTSTATUS status = STATUS_SUCCESS;
391
392     FWPS_CALLOUT sCallout = {0};
393     FWPM_CALLOUT mCallout = {0};
394
395     FWPM_DISPLAY_DATA displayData = {0};
396
397     BOOLEAN calloutRegistered = FALSE;
398
399     sCallout.calloutKey = *calloutKey;
400     sCallout.classifyFn = OvsTunnelClassify;
401     sCallout.notifyFn = OvsTunnelNotify;
402 #if FLOW_CONTEXT
403     /* Currently we don't associate a context with the flow */
404     sCallout.flowDeleteFn = OvsTunnelFlowDelete;
405     sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
406 #endif
407
408     status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId);
409     if (!NT_SUCCESS(status)) {
410         goto Exit;
411     }
412     calloutRegistered = TRUE;
413
414     displayData.name = L"Datagram-Data OVS Callout";
415     displayData.description = L"Proxies destination address/port for UDP";
416
417     mCallout.calloutKey = *calloutKey;
418     mCallout.displayData = displayData;
419     mCallout.applicableLayer = *layerKey;
420
421     status = FwpmCalloutAdd(gEngineHandle, &mCallout, NULL, NULL);
422     if (!NT_SUCCESS(status)) {
423         if (STATUS_FWP_ALREADY_EXISTS != status) {
424             OVS_LOG_ERROR("Failed to add WFP callout, status: %x.",
425                           status);
426             goto Exit;
427         }
428         status = STATUS_SUCCESS;
429     }
430
431 Exit:
432
433     if (!NT_SUCCESS(status)){
434         if (calloutRegistered) {
435             FwpsCalloutUnregisterById(*calloutId);
436             *calloutId = 0;
437         }
438     }
439
440     return status;
441 }
442
443 /*
444  * --------------------------------------------------------------------------
445  * This function registers non-dynamic callouts for intercepting UDP traffic.
446  * Callouts will be removed during un-initializing phase.
447  * --------------------------------------------------------------------------
448  */
449 NTSTATUS
450 OvsTunnelRegisterCallouts(VOID *deviceObject)
451 {
452     NTSTATUS        status = STATUS_SUCCESS;
453     BOOLEAN         inTransaction = FALSE;
454     FWPM_SUBLAYER   OvsTunnelSubLayer;
455
456     status = FwpmTransactionBegin(gEngineHandle, 0);
457     if (!NT_SUCCESS(status)) {
458         goto Exit;
459     }
460     inTransaction = TRUE;
461
462     RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER));
463
464     OvsTunnelSubLayer.subLayerKey = OVS_TUNNEL_SUBLAYER;
465     OvsTunnelSubLayer.displayData.name = L"Datagram-Data OVS Sub-Layer";
466     OvsTunnelSubLayer.displayData.description =
467         L"Sub-Layer for use by Datagram-Data OVS callouts";
468     OvsTunnelSubLayer.flags = 0;
469     OvsTunnelSubLayer.weight = FWP_EMPTY; /* auto-weight */
470
471     status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL);
472     if (!NT_SUCCESS(status)) {
473         if (STATUS_FWP_ALREADY_EXISTS != status) {
474             OVS_LOG_ERROR("Failed to add WFP sublayer, status: %x.",
475                           status);
476             goto Exit;
477         }
478     }
479
480     /* In order to use this callout a socket must be opened. */
481     status = OvsTunnelRegisterDatagramDataCallouts(&FWPM_LAYER_DATAGRAM_DATA_V4,
482                                                    &OVS_TUNNEL_CALLOUT_V4,
483                                                    deviceObject,
484                                                    &gCalloutIdV4);
485     if (!NT_SUCCESS(status)) {
486         goto Exit;
487     }
488
489     status = FwpmTransactionCommit(gEngineHandle);
490     if (!NT_SUCCESS(status)){
491         goto Exit;
492     }
493     inTransaction = FALSE;
494
495 Exit:
496
497     if (!NT_SUCCESS(status)) {
498         if (inTransaction) {
499             FwpmTransactionAbort(gEngineHandle);
500         }
501     }
502
503     return status;
504 }
505
506 VOID
507 OvsTunnelUnregisterCallouts()
508 {
509     FwpsCalloutUnregisterById(gCalloutIdV4);
510     FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER);
511     FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
512 }
513
514 VOID
515 OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
516 {
517     UNREFERENCED_PARAMETER(driverObject);
518
519     OvsTunnelFilterStopThreads();
520
521     OvsTunnelUnregisterCallouts();
522     OvsTunnelEngineClose(&gEngineHandle);
523
524     if (gDeviceObject) {
525         IoDeleteDevice(gDeviceObject);
526     }
527 }
528
529
530 NTSTATUS
531 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
532 {
533     NTSTATUS        status = STATUS_SUCCESS;
534     UNICODE_STRING  deviceName;
535
536     RtlInitUnicodeString(&deviceName,
537                          L"\\Device\\OvsTunnelFilter");
538
539     status = IoCreateDevice(driverObject,
540                             0,
541                             &deviceName,
542                             FILE_DEVICE_NETWORK,
543                             0,
544                             FALSE,
545                             &gDeviceObject);
546
547     if (!NT_SUCCESS(status)){
548         OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.",
549                       status);
550         goto Exit;
551     }
552
553     status = OvsTunnelFilterStartThreads();
554     if (!NT_SUCCESS(status)){
555         goto Exit;
556     }
557
558     status = OvsTunnelEngineOpen(&gEngineHandle);
559     if (!NT_SUCCESS(status)){
560         goto Exit;
561     }
562
563     status = OvsTunnelRegisterCallouts(gDeviceObject);
564     if (!NT_SUCCESS(status)) {
565         OVS_LOG_ERROR("Failed to register callout, status: %x.",
566                       status);
567     }
568
569 Exit:
570
571     if (!NT_SUCCESS(status)){
572         OvsTunnelFilterUninitialize(driverObject);
573     }
574
575     return status;
576 }
577
578 /*
579  * --------------------------------------------------------------------------
580  * This function adds OVS system provider to the system if the BFE (Base
581  * Filtering Engine) is running.
582  * --------------------------------------------------------------------------
583  */
584 VOID NTAPI
585 OvsTunnelProviderBfeCallback(PVOID context,
586                              FWPM_SERVICE_STATE bfeState)
587 {
588     HANDLE engineSession = NULL;
589
590     DBG_UNREFERENCED_PARAMETER(context);
591
592     if (FWPM_SERVICE_RUNNING == bfeState) {
593         OvsTunnelEngineOpen(&engineSession);
594         if (engineSession) {
595             OvsTunnelAddSystemProvider(engineSession);
596         }
597         OvsTunnelEngineClose(&engineSession);
598     }
599 }
600
601 /*
602  * --------------------------------------------------------------------------
603  * This function registers the OvsTunnelProviderBfeCallback callback that is
604  * called whenever there is a change to the state of base filtering engine.
605  * --------------------------------------------------------------------------
606  */
607 NTSTATUS
608 OvsSubscribeTunnelProviderBfeStateChanges(PVOID deviceObject)
609 {
610     NTSTATUS status = STATUS_SUCCESS;
611
612     if (!gTunnelProviderBfeHandle) {
613         status = FwpmBfeStateSubscribeChanges(deviceObject,
614                                               OvsTunnelProviderBfeCallback,
615                                               NULL,
616                                               &gTunnelProviderBfeHandle);
617         if (!NT_SUCCESS(status)) {
618             OVS_LOG_ERROR(
619                 "Failed to subscribe BFE tunnel provider callback, status: %x.",
620                 status);
621         }
622     }
623
624     return status;
625 }
626
627 /*
628  * --------------------------------------------------------------------------
629  * This function unregisters the OvsTunnelProviderBfeCallback callback that
630  * was previously registered by OvsSubscribeTunnelProviderBfeStateChanges
631  * function.
632  * --------------------------------------------------------------------------
633  */
634 VOID
635 OvsUnsubscribeTunnelProviderBfeStateChanges()
636 {
637     NTSTATUS status = STATUS_SUCCESS;
638
639     if (gTunnelProviderBfeHandle) {
640         status = FwpmBfeStateUnsubscribeChanges(gTunnelProviderBfeHandle);
641         if (!NT_SUCCESS(status)) {
642             OVS_LOG_ERROR(
643                 "Failed to unsubscribe BFE tunnel provider callback, status: %x.",
644                 status);
645         }
646         gTunnelProviderBfeHandle = NULL;
647     }
648 }
649
650 /*
651  * --------------------------------------------------------------------------
652  * This function registers the OVS system provider if the BFE (Base Filtering
653  * Engine) is running.
654  * Otherwise, it will register the OvsTunnelProviderBfeCallback callback.
655
656  * Note: Before calling FwpmBfeStateGet, the callout driver must call the
657  * FwpmBfeStateSubscribeChanges function to register the callback function
658  * to be called whenever the state of the filter engine changes.
659  *
660  * Register WFP system provider call hierarchy:
661  * <DriverEntry>
662  *     <OvsCreateDeviceObject>
663  *         <OvsRegisterSystemProvider>
664  *             <OvsSubscribeTunnelProviderBfeStateChanges>
665  *                 --> registers OvsTunnelProviderBfeCallback callback
666  *                     <OvsTunnelProviderBfeCallback>
667  *                         --> if BFE is running:
668  *                             <OvsTunnelAddSystemProvider>
669  *             --> if BFE is running:
670  *                 <OvsTunnelAddSystemProvider>
671  *                 <OvsUnsubscribeTunnelProviderBfeStateChanges>
672  *                     --> unregisters OvsTunnelProviderBfeCallback callback
673  *
674  * --------------------------------------------------------------------------
675  */
676 VOID
677 OvsRegisterSystemProvider(PVOID deviceObject)
678 {
679     NTSTATUS status = STATUS_SUCCESS;
680     HANDLE engineSession = NULL;
681
682     status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
683     if (NT_SUCCESS(status)) {
684         if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
685             OvsTunnelEngineOpen(&engineSession);
686             if (engineSession) {
687                 OvsTunnelAddSystemProvider(engineSession);
688             }
689             OvsTunnelEngineClose(&engineSession);
690
691             OvsUnsubscribeTunnelProviderBfeStateChanges();
692         }
693     }
694 }
695
696 /*
697  * --------------------------------------------------------------------------
698  * This function removes the OVS system provider and unregisters the
699  * OvsTunnelProviderBfeCallback callback from BFE (Base Filtering Engine).
700  *
701  * Unregister WFP system provider call hierarchy:
702  * <OvsExtUnload>
703  *     <OvsDeleteDeviceObject>
704  *         <OvsUnregisterSystemProvider>
705  *             <OvsTunnelRemoveSystemProvider>
706  *             <OvsUnsubscribeTunnelProviderBfeStateChanges>
707  *                 --> unregisters OvsTunnelProviderBfeCallback callback
708  *
709  * --------------------------------------------------------------------------
710  */
711 VOID
712 OvsUnregisterSystemProvider()
713 {
714     HANDLE engineSession = NULL;
715
716     OvsTunnelEngineOpen(&engineSession);
717     if (engineSession) {
718         OvsTunnelRemoveSystemProvider(engineSession);
719     }
720     OvsTunnelEngineClose(&engineSession);
721
722     OvsUnsubscribeTunnelProviderBfeStateChanges();
723 }
724
725 /*
726  * --------------------------------------------------------------------------
727  * This function initializes the tunnel filter if the BFE is running.
728  * --------------------------------------------------------------------------
729  */
730 VOID NTAPI
731 OvsTunnelInitBfeCallback(PVOID context,
732                          FWPM_SERVICE_STATE bfeState)
733 {
734     NTSTATUS status = STATUS_SUCCESS;
735     PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) context;
736
737     if (FWPM_SERVICE_RUNNING == bfeState) {
738         status = OvsTunnelFilterInitialize(driverObject);
739         if (!NT_SUCCESS(status)) {
740             OVS_LOG_ERROR(
741                 "Failed to initialize tunnel filter, status: %x.",
742                 status);
743         }
744     }
745 }
746
747 /*
748  * --------------------------------------------------------------------------
749  * This function registers the OvsTunnelInitBfeCallback callback that is
750  * called whenever there is a change to the state of base filtering engine.
751  * --------------------------------------------------------------------------
752  */
753 NTSTATUS
754 OvsSubscribeTunnelInitBfeStateChanges(PDRIVER_OBJECT driverObject,
755                                       PVOID deviceObject)
756 {
757     NTSTATUS status = STATUS_SUCCESS;
758
759     if (!gTunnelInitBfeHandle) {
760         status = FwpmBfeStateSubscribeChanges(deviceObject,
761                                               OvsTunnelInitBfeCallback,
762                                               driverObject,
763                                               &gTunnelInitBfeHandle);
764         if (!NT_SUCCESS(status)) {
765             OVS_LOG_ERROR(
766                 "Failed to subscribe BFE tunnel init callback, status: %x.",
767                 status);
768         }
769     }
770
771     return status;
772 }
773
774 /*
775  * --------------------------------------------------------------------------
776  * This function unregisters the OvsTunnelInitBfeCallback callback that
777  * was previously registered by OvsSubscribeTunnelInitBfeStateChanges
778  * function.
779  * --------------------------------------------------------------------------
780  */
781 VOID
782 OvsUnsubscribeTunnelInitBfeStateChanges()
783 {
784     NTSTATUS status = STATUS_SUCCESS;
785
786     if (gTunnelInitBfeHandle) {
787         status = FwpmBfeStateUnsubscribeChanges(gTunnelInitBfeHandle);
788         if (!NT_SUCCESS(status)) {
789             OVS_LOG_ERROR(
790                 "Failed to unsubscribe BFE tunnel init callback, status: %x.",
791                 status);
792         }
793         gTunnelInitBfeHandle = NULL;
794     }
795 }
796
797 /*
798  * --------------------------------------------------------------------------
799  * This function initializes the OVS tunnel filter if the BFE (Base Filtering
800  * Engine) is running.
801  * Otherwise, it will register the OvsTunnelInitBfeCallback callback.
802
803  * Note: Before calling FwpmBfeStateGet, the callout driver must call the
804  * FwpmBfeStateSubscribeChanges function to register the callback function
805  * to be called whenever the state of the filter engine changes.
806  *
807  * Initialize OVS tunnel filter call hierarchy:
808  * <OvsExtAttach>
809  *     <OvsCreateSwitch>
810  *         <OvsInitTunnelFilter>
811  *             <OvsSubscribeTunnelInitBfeStateChanges>
812  *                 --> registers OvsTunnelInitBfeCallback callback
813  *                     <OvsTunnelInitBfeCallback>
814  *                         --> if BFE is running:
815  *                             <OvsTunnelFilterInitialize>
816  *                                 <IoCreateDevice>
817  *                                 <OvsTunnelFilterStartThreads>
818  *                                 <OvsTunnelRegisterCallouts>
819  *             --> if BFE is running:
820  *                 <OvsTunnelFilterInitialize>
821  *                     <IoCreateDevice>
822  *                     <OvsTunnelFilterStartThreads>
823  *                     <OvsTunnelRegisterCallouts>
824  *                 <OvsUnsubscribeTunnelInitBfeStateChanges>
825  *                     --> unregisters OvsTunnelInitBfeCallback callback
826  *
827  * --------------------------------------------------------------------------
828  */
829 NTSTATUS
830 OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject)
831 {
832     NTSTATUS status = STATUS_SUCCESS;
833
834     if (deviceObject) {
835         status = OvsSubscribeTunnelInitBfeStateChanges(driverObject, deviceObject);
836         if (NT_SUCCESS(status)) {
837             if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
838                 status = OvsTunnelFilterInitialize(driverObject);
839                 if (!NT_SUCCESS(status)) {
840                     /* XXX: We need to decide what actions to take in case of
841                      * failure to initialize tunnel filter. */
842                     ASSERT(status == NDIS_STATUS_SUCCESS);
843                     OVS_LOG_ERROR(
844                         "Failed to initialize tunnel filter, status: %x.",
845                         status);
846                 }
847                 OvsUnsubscribeTunnelInitBfeStateChanges();
848             }
849         }
850     } else {
851         status = OvsTunnelFilterInitialize(driverObject);
852     }
853
854     return status;
855 }
856
857 /*
858  * --------------------------------------------------------------------------
859  * This function uninitializes the OVS tunnel filter and unregisters the
860  * OvsTunnelInitBfeCallback callback from BFE.
861  *
862  * Uninitialize OVS tunnel filter call hierarchy:
863  * <OvsExtDetach>
864  *     <OvsDeleteSwitch>
865  *         <OvsUninitTunnelFilter>
866  *             <OvsTunnelFilterUninitialize>
867  *                 <OvsTunnelFilterStopThreads>
868  *                 <OvsTunnelUnregisterCallouts>
869  *                 <IoDeleteDevice>
870  *             <OvsUnsubscribeTunnelInitBfeStateChanges>
871  *                 --> unregisters OvsTunnelInitBfeCallback callback
872  *
873  * --------------------------------------------------------------------------
874  */
875 VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
876 {
877     OvsTunnelFilterUninitialize(driverObject);
878     OvsUnsubscribeTunnelInitBfeStateChanges();
879 }
880
881 NTSTATUS
882 OvsTunnelAddFilterEx(HANDLE engineSession,
883                      UINT32 filterPort,
884                      UINT64 *filterID)
885 {
886     NTSTATUS status = STATUS_SUCCESS;
887
888     status = OvsTunnelAddFilter(engineSession,
889                                 L"Datagram-Data OVS Filter (Inbound)",
890                                 L"address/port for UDP",
891                                 (USHORT)filterPort,
892                                 FWP_DIRECTION_INBOUND,
893                                 0,
894                                 NULL,
895                                 &FWPM_LAYER_DATAGRAM_DATA_V4,
896                                 &OVS_TUNNEL_CALLOUT_V4,
897                                 filterID);
898     if (!NT_SUCCESS(status)) {
899         OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.",
900                       filterPort, status);
901     } else {
902         OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.",
903                      filterPort, *filterID);
904     }
905
906     return status;
907 }
908
909 NTSTATUS
910 OvsTunnelRemoveFilterEx(HANDLE engineSession,
911                         UINT64 filterID)
912 {
913     NTSTATUS status = STATUS_SUCCESS;
914     BOOLEAN  error = TRUE;
915
916     do {
917         if (filterID == 0) {
918             OVS_LOG_INFO("No tunnel filter to remove.");
919             break;
920         }
921
922         status = FwpmFilterDeleteById(engineSession, filterID);
923         if (!NT_SUCCESS(status)) {
924             OVS_LOG_ERROR("Failed to remove tunnel with filter ID: %d,\
925                            status: %x.", filterID, status);
926             break;
927         }
928         OVS_LOG_INFO("Filter removed, filter ID: %d.",
929                      filterID);
930
931         error = FALSE;
932     } while (error);
933
934     return status;
935 }
936
937 NTSTATUS
938 OvsTunnelFilterExecuteAction(HANDLE engineSession,
939                              POVS_TUNFLT_REQUEST request)
940 {
941     NTSTATUS status = STATUS_SUCCESS;
942
943     switch (request->operation)
944     {
945     case OVS_TUN_FILTER_CREATE:
946         status = OvsTunnelAddFilterEx(engineSession,
947                                       request->port,
948                                       request->filterID.addID);
949         break;
950     case OVS_TUN_FILTER_DELETE:
951         status = OvsTunnelRemoveFilterEx(engineSession,
952                                          request->filterID.delID);
953         break;
954     default:
955         status = STATUS_NOT_SUPPORTED;
956         break;
957     }
958
959     return status;
960 }
961
962 /*
963  * --------------------------------------------------------------------------
964  * This function pops the head request from the queue while holding the
965  * queue lock. If the request has already been cancelled or is about to be
966  * cancelled, the function retrieves the next valid request.
967  *
968  * Returns a pointer to the OVS_TUNFLT_REQUEST_LIST request object retrieved
969  * from the queue.
970  * --------------------------------------------------------------------------
971  */
972 POVS_TUNFLT_REQUEST
973 OvsTunnelFilterRequestPop(POVS_TUNFLT_REQUEST_LIST listRequests)
974 {
975     POVS_TUNFLT_REQUEST request = NULL;
976     PLIST_ENTRY         link, next, head;
977
978     NdisAcquireSpinLock(&listRequests->spinlock);
979
980     if (!IsListEmpty(&listRequests->head)) {
981         head = &listRequests->head;
982         LIST_FORALL_SAFE(head, link, next) {
983             PDRIVER_CANCEL oldCancelRoutine;
984
985             request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry);
986             if (request->irp) {
987                 oldCancelRoutine = IoSetCancelRoutine(request->irp, NULL);
988                 if (oldCancelRoutine == NULL) {
989                     /*
990                      * The Cancel routine for the current IRP is running. The
991                      * request is to be completed by the Cancel routine. Leave
992                      * this request alone and go to the next one.
993                      */
994                     continue;
995                 } else {
996                     /*
997                      * The Cancel routine cannot run now and cannot already have
998                      * started to run. This request can be processed.
999                      */
1000                 }
1001             }
1002
1003             RemoveEntryList(&request->entry);
1004             listRequests->numEntries--;
1005             break;
1006         }
1007     }
1008
1009     NdisReleaseSpinLock(&listRequests->spinlock);
1010
1011     return request;
1012 }
1013
1014 /*
1015  * --------------------------------------------------------------------------
1016  * This function pushes the received request to the queue, marks the IRP as
1017  * pending and sets its Cancel routine, while holding the queue lock.
1018  *
1019  * Returns STATUS_CANCELLED if the IRP has already been cancelled. Otherwise,
1020  * STATUS_SUCCESS is returned.
1021  * --------------------------------------------------------------------------
1022  */
1023 NTSTATUS
1024 OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests,
1025                            POVS_TUNFLT_REQUEST request)
1026 {
1027     NTSTATUS status = STATUS_SUCCESS;
1028     PIRP irp = request->irp;
1029     PDRIVER_CANCEL oldCancelRoutine;
1030     BOOLEAN cancelled = FALSE;
1031
1032     NdisAcquireSpinLock(&listRequests->spinlock);
1033
1034     if (irp) {
1035         /*
1036          * Mark the IRP pending to indicate that the request may complete on
1037          * a different thread.
1038          */
1039         IoMarkIrpPending(irp);
1040
1041         /*
1042          * Set the Cancel routine for the pending IRP, before checking the
1043          * Cancel flag.
1044          */
1045         oldCancelRoutine = IoSetCancelRoutine(irp, OvsTunnelFilterCancelIrp);
1046         ASSERT(oldCancelRoutine == NULL);
1047
1048         if (irp->Cancel) {
1049             /*
1050              * The IRP has already been cancelled.
1051              * Determine wheather the Cancel routine has started to run.
1052              */
1053             oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
1054             if (oldCancelRoutine) {
1055                 /*
1056                  * The I/O Manager has not called the Cancel routine and it
1057                  * won't be called anymore, because we just set it to NULL.
1058                  * Return STATUS_CANCELLED and complete the request after
1059                  * releasing the lock.
1060                  */
1061                 status = STATUS_CANCELLED;
1062                 cancelled = TRUE;
1063             } else {
1064                 /*
1065                  * The Cancel routine has already started to run, but it is
1066                  * blocked while it waits for the queue lock. Release the lock
1067                  * and return STATUS_SUCCESS to avoid completing the request.
1068                  * It will be completed in the Cancel routine.
1069                  */
1070             }
1071         } else {
1072             /*
1073              * The IRP has not been cancelled, so set its context used in the
1074              * Cancel routine.
1075              */
1076             OvsTunnelFilterSetIrpContext(listRequests, request);
1077         }
1078     }
1079
1080     if (!cancelled) {
1081         InsertTailList(&listRequests->head, &(request->entry));
1082         listRequests->numEntries++;
1083     }
1084
1085     NdisReleaseSpinLock(&listRequests->spinlock);
1086
1087     return status;
1088 }
1089
1090 /*
1091  * --------------------------------------------------------------------------
1092  * This function pushes the received request to the corresponding thread
1093  * request queue. The arrival of the new request is signaled to the thread,
1094  * in order to start processing it.
1095  *
1096  * Note:
1097  * If the thread is not initialized, no operation is performed.
1098  *
1099  * For a uniform distribution of requests to thread queues, a thread index is
1100  * calculated based on the received destination port.
1101  * --------------------------------------------------------------------------
1102  */
1103 NTSTATUS
1104 OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request)
1105 {
1106     NTSTATUS status = STATUS_REQUEST_ABORTED;
1107     UINT32 count = OVS_TUNFLT_MAX_THREADS;
1108     UINT32 threadIndex;
1109
1110     threadIndex = request->port % OVS_TUNFLT_MAX_THREADS;
1111
1112     while (count--) {
1113         if (gTunnelThreadCtx[threadIndex].isInitialized) {
1114
1115             status = OvsTunnelFilterRequestPush(
1116                 &gTunnelThreadCtx[threadIndex].listRequests,
1117                 request);
1118
1119             if (NT_SUCCESS(status)) {
1120                 KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent,
1121                            IO_NO_INCREMENT,
1122                            FALSE);
1123             }
1124
1125             break;
1126         } else {
1127             OVS_LOG_INFO("OVS tunnel filter thread %d not initialized.",
1128                          threadIndex);
1129         }
1130
1131         threadIndex = (threadIndex + 1) % OVS_TUNFLT_MAX_THREADS;
1132     }
1133
1134     return status;
1135 }
1136
1137 VOID
1138 OvsTunnelFilterCompleteRequest(PIRP irp,
1139                                PFNTunnelVportPendingOp callback,
1140                                PVOID context,
1141                                NTSTATUS status)
1142 {
1143     UINT32 replyLen = 0;
1144
1145     if (callback) {
1146         callback(context, status, &replyLen);
1147         /* Release the context passed to the callback function. */
1148         OvsFreeMemory(context);
1149     }
1150
1151     if (irp) {
1152         OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
1153     }
1154 }
1155
1156 VOID
1157 OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1158 {
1159     POVS_TUNFLT_REQUEST request = NULL;
1160     NTSTATUS            status = STATUS_SUCCESS;
1161     BOOLEAN             inTransaction = FALSE;
1162
1163     do {
1164         if (!InterlockedCompareExchange(
1165             (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) {
1166             OVS_LOG_INFO("Nothing to do... request list is empty.");
1167             break;
1168         }
1169
1170         status = FwpmTransactionBegin(threadCtx->engineSession, 0);
1171         if (!NT_SUCCESS(status)) {
1172             OVS_LOG_ERROR("Failed to start transaction, status: %x.",
1173                           status);
1174             break;
1175         }
1176         inTransaction = TRUE;
1177
1178         while (NULL !=
1179             (request = OvsTunnelFilterRequestPop(&threadCtx->listRequests))) {
1180
1181             status = OvsTunnelFilterExecuteAction(threadCtx->engineSession,
1182                                                   request);
1183
1184             /* Complete the IRP with the last operation status. */
1185             OvsTunnelFilterCompleteRequest(request->irp,
1186                                            request->callback,
1187                                            request->context,
1188                                            status);
1189
1190             OvsFreeMemory(request);
1191             request = NULL;
1192         }
1193
1194         status = FwpmTransactionCommit(threadCtx->engineSession);
1195         if (!NT_SUCCESS(status)) {
1196             OVS_LOG_ERROR("Failed to commit transaction, status: %x.",
1197                           status);
1198             break;
1199         }
1200
1201         inTransaction = FALSE;
1202     } while (inTransaction);
1203
1204     if (inTransaction) {
1205         FwpmTransactionAbort(threadCtx->engineSession);
1206         OVS_LOG_ERROR("Failed to execute request, status: %x.\
1207                        Transaction aborted.", status);
1208     }
1209 }
1210
1211 /*
1212  *----------------------------------------------------------------------------
1213  * System thread routine that processes thread's requests queue. The thread
1214  * routine initializes thread's necessary data and waits on two events,
1215  * requestEvent and stopEvent. Whenever a request is pushed to the thread's
1216  * queue, the requestEvent is signaled and the thread routine starts processing
1217  * the arrived requests. When stopEvent is signaled, all subsequent requests
1218  * are completed with STATUS_CANCELED, without being added to the thread's
1219  * queue, and the routine finishes processing all existing requests from the
1220  * queue before uninitializing the thread and exiting.
1221  *----------------------------------------------------------------------------
1222  */
1223 _Use_decl_annotations_
1224 VOID
1225 OvsTunnelFilterThreadProc(PVOID context)
1226 {
1227     NTSTATUS                   status = STATUS_SUCCESS;
1228     POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context;
1229     PKEVENT                    eventArray[2] = { 0 };
1230     ULONG                      count = 0;
1231     BOOLEAN                    exit = FALSE;
1232     BOOLEAN                    error = TRUE;
1233
1234     OVS_LOG_INFO("Starting OVS Tunnel system thread %d.",
1235                  threadCtx->threadID);
1236
1237     eventArray[0] = &threadCtx->stopEvent;
1238     eventArray[1] = &threadCtx->requestEvent;
1239     count = ARRAY_SIZE(eventArray);
1240
1241     do {
1242         status = OvsTunnelFilterThreadInit(threadCtx);
1243         if (!NT_SUCCESS(status)) {
1244             OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.",
1245                 threadCtx->threadID);
1246             break;
1247         }
1248
1249         do {
1250             status = KeWaitForMultipleObjects(count,
1251                                               (PVOID)eventArray,
1252                                               WaitAny,
1253                                               Executive,
1254                                               KernelMode,
1255                                               FALSE,
1256                                               NULL,
1257                                               NULL);
1258             switch (status) {
1259                 case STATUS_WAIT_1:
1260                     /* Start processing requests. */
1261                     OvsTunnelFilterRequestListProcess(threadCtx);
1262                     break;
1263                 default:
1264                     /* Finish processing the remaining requests and exit. */
1265                     OvsTunnelFilterRequestListProcess(threadCtx);
1266                     exit = TRUE;
1267                     break;
1268             }
1269         } while (!exit);
1270
1271         OvsTunnelFilterThreadUninit(threadCtx);
1272
1273         error = FALSE;
1274     } while (error);
1275
1276     OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.",
1277                  threadCtx->threadID);
1278
1279     PsTerminateSystemThread(STATUS_SUCCESS);
1280 };
1281
1282 static NTSTATUS
1283 OvsTunnelFilterStartThreads()
1284 {
1285     NTSTATUS status = STATUS_SUCCESS;
1286
1287     for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1288         gTunnelThreadCtx[index].threadID = index;
1289
1290         status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]);
1291         if (!NT_SUCCESS(status)) {
1292             OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index);
1293             break;
1294         }
1295     }
1296
1297     return status;
1298 }
1299
1300 static NTSTATUS
1301 OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1302 {
1303     NTSTATUS    status = STATUS_SUCCESS;
1304     HANDLE      threadHandle = NULL;
1305     BOOLEAN     error = TRUE;
1306
1307     do {
1308         status = PsCreateSystemThread(&threadHandle,
1309                                       SYNCHRONIZE,
1310                                       NULL,
1311                                       NULL,
1312                                       NULL,
1313                                       OvsTunnelFilterThreadProc,
1314                                       threadCtx);
1315         if (!NT_SUCCESS(status)) {
1316             OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
1317                           status);
1318             break;
1319         }
1320
1321         ObReferenceObjectByHandle(threadHandle,
1322                                   SYNCHRONIZE,
1323                                   NULL,
1324                                   KernelMode,
1325                                   &threadCtx->threadObject,
1326                                   NULL);
1327         ZwClose(threadHandle);
1328         threadHandle = NULL;
1329
1330         error = FALSE;
1331     } while (error);
1332
1333     return status;
1334 }
1335
1336 static VOID
1337 OvsTunnelFilterStopThreads()
1338 {
1339     /* Signal all threads to stop and ignore all subsequent requests. */
1340     for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1341         OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], TRUE);
1342     }
1343
1344     /* Wait for all threads to finish processing the requests. */
1345     for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1346         OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], FALSE);
1347     }
1348 }
1349
1350 static VOID
1351 OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
1352                           BOOLEAN signalEvent)
1353 {
1354     if (threadCtx->isInitialized) {
1355
1356         if (signalEvent) {
1357             /* Signal stop thread event. */
1358             OVS_LOG_INFO("Received stop event for OVS Tunnel system thread %d.",
1359                          threadCtx->threadID);
1360             KeSetEvent(&threadCtx->stopEvent, IO_NO_INCREMENT, FALSE);
1361         } else {
1362             /* Wait for the tunnel thread to finish. */
1363             KeWaitForSingleObject(threadCtx->threadObject,
1364                                   Executive,
1365                                   KernelMode,
1366                                   FALSE,
1367                                   NULL);
1368
1369             ObDereferenceObject(threadCtx->threadObject);
1370         }
1371     }
1372 }
1373
1374 /*
1375  * --------------------------------------------------------------------------
1376  * This function initializes thread's necessary data. Each thread has its own
1377  * session object to the BFE that is used for processing the requests from
1378  * the thread's queue.
1379  * --------------------------------------------------------------------------
1380  */
1381 static NTSTATUS
1382 OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1383 {
1384     NTSTATUS status = STATUS_SUCCESS;
1385     BOOLEAN error = TRUE;
1386
1387     do {
1388         /* Create thread's engine session object. */
1389         status = OvsTunnelEngineOpen(&threadCtx->engineSession);
1390         if (!NT_SUCCESS(status)) {
1391             break;
1392         }
1393
1394         NdisAllocateSpinLock(&threadCtx->listRequests.spinlock);
1395
1396         InitializeListHead(&threadCtx->listRequests.head);
1397
1398         KeInitializeEvent(&threadCtx->stopEvent,
1399             NotificationEvent,
1400             FALSE);
1401
1402         KeInitializeEvent(&threadCtx->requestEvent,
1403             SynchronizationEvent,
1404             FALSE);
1405
1406         threadCtx->isInitialized = TRUE;
1407
1408         error = FALSE;
1409     } while (error);
1410
1411     return status;
1412 }
1413
1414 /*
1415  * --------------------------------------------------------------------------
1416  * This function uninitializes thread's private data. Thread's engine session
1417  * handle is closed and set to NULL.
1418  * --------------------------------------------------------------------------
1419  */
1420 static VOID
1421 OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1422 {
1423     if (threadCtx->engineSession) {
1424         /* Close thread's FWPM session. */
1425         OvsTunnelEngineClose(&threadCtx->engineSession);
1426
1427         NdisFreeSpinLock(&threadCtx->listRequests.spinlock);
1428
1429         threadCtx->isInitialized = FALSE;
1430     }
1431 }
1432
1433 /*
1434  * --------------------------------------------------------------------------
1435  * This function creates a new tunnel filter request and push it to a thread
1436  * queue. If the thread stop event is signaled, the request is completed with
1437  * STATUS_REQUEST_ABORTED without pushing it to any queue.
1438  * --------------------------------------------------------------------------
1439  */
1440 NTSTATUS
1441 OvsTunnelFilterQueueRequest(PIRP irp,
1442                             UINT16 remotePort,
1443                             UINT64 *filterID,
1444                             OVS_TUNFLT_OPERATION operation,
1445                             PFNTunnelVportPendingOp callback,
1446                             PVOID tunnelContext)
1447 {
1448     POVS_TUNFLT_REQUEST request = NULL;
1449     NTSTATUS            status = STATUS_PENDING;
1450     NTSTATUS            result = STATUS_SUCCESS;
1451     BOOLEAN             error = TRUE;
1452     UINT64              timeout = 0;
1453
1454     do {
1455         /* Verify if the stop event was signaled. */
1456         if (STATUS_SUCCESS == KeWaitForSingleObject(
1457                                   &gTunnelThreadCtx[0].stopEvent,
1458                                   Executive,
1459                                   KernelMode,
1460                                   FALSE,
1461                                   (LARGE_INTEGER *)&timeout)) {
1462             /* The stop event is signaled. Completed the IRP with
1463              * STATUS_REQUEST_ABORTED. */
1464             status = STATUS_REQUEST_ABORTED;
1465             break;
1466         }
1467
1468         if (NULL == filterID) {
1469             OVS_LOG_ERROR("Invalid request.");
1470             status = STATUS_INVALID_PARAMETER;
1471             break;
1472         }
1473
1474         request = (POVS_TUNFLT_REQUEST)
1475             OvsAllocateMemoryWithTag(sizeof(*request),
1476                                      OVS_TUNFLT_POOL_TAG);
1477         if (NULL == request) {
1478             OVS_LOG_ERROR("Failed to allocate list item.");
1479             status = STATUS_INSUFFICIENT_RESOURCES;
1480             break;
1481         }
1482
1483         request->port = remotePort;
1484         request->operation = operation;
1485         switch (operation) {
1486             case OVS_TUN_FILTER_CREATE:
1487                 request->filterID.addID = filterID;
1488                 break;
1489             case OVS_TUN_FILTER_DELETE:
1490                 request->filterID.delID = *filterID;
1491                 break;
1492         }
1493         request->irp = irp;
1494         request->callback = callback;
1495         request->context = tunnelContext;
1496
1497         result = OvsTunnelFilterThreadPush(request);
1498         if (!NT_SUCCESS(result)) {
1499             status = result;
1500             break;
1501         }
1502
1503         error = FALSE;
1504     } while (error);
1505
1506     if (error) {
1507         OvsTunnelFilterCompleteRequest(irp,
1508                                        callback,
1509                                        tunnelContext,
1510                                        status);
1511         if (request) {
1512             OvsFreeMemory(request);
1513             request = NULL;
1514         }
1515     }
1516
1517     return status;
1518 }
1519
1520 /*
1521  * --------------------------------------------------------------------------
1522  *  This function adds a new WFP filter for the received port and returns the
1523  *  ID of the created WFP filter.
1524  *
1525  *  Note:
1526  *  All necessary calls to the WFP filtering engine must be running at IRQL =
1527  *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1528  *  we register an OVS_TUN_FILTER_CREATE request that will be processed by
1529  *  the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1530  *
1531  * OVS VXLAN port add call hierarchy:
1532  * <OvsNewVportCmdHandler>
1533  *     <OvsInitTunnelVport>
1534  *         <OvsInitVxlanTunnel>
1535  *             <OvsTunnelFilterCreate>
1536  *                 <OvsTunnelFilterQueueRequest>
1537  *                     --> if thread STOP event is signalled:
1538  *                         --> Complete request with STATUS_CANCELLED
1539  *                         --> EXIT
1540  *                     <OvsTunnelFilterThreadPush>
1541  *                         --> add the request to one of tunnel thread queues
1542  *
1543  * --------------------------------------------------------------------------
1544  */
1545 NTSTATUS
1546 OvsTunnelFilterCreate(PIRP irp,
1547                       UINT16 filterPort,
1548                       UINT64 *filterID,
1549                       PFNTunnelVportPendingOp callback,
1550                       PVOID tunnelContext)
1551 {
1552     return OvsTunnelFilterQueueRequest(irp,
1553                                        filterPort,
1554                                        filterID,
1555                                        OVS_TUN_FILTER_CREATE,
1556                                        callback,
1557                                        tunnelContext);
1558 }
1559
1560 /*
1561  * --------------------------------------------------------------------------
1562  *  This function removes a WFP filter using the received filter ID.
1563  *
1564  *  Note:
1565  *  All necessary calls to the WFP filtering engine must be running at IRQL =
1566  *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1567  *  we register an OVS_TUN_FILTER_DELETE request that will be processed by
1568  *  the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1569  *
1570  * OVS VXLAN port delete call hierarchy:
1571  * <OvsDeleteVportCmdHandler>
1572  *     <OvsRemoveAndDeleteVport>
1573  *         <OvsCleanupVxlanTunnel>
1574  *             <OvsTunnelFilterDelete>
1575  *                 <OvsTunnelFilterQueueRequest>
1576  *                     --> if thread STOP event is signalled:
1577  *                         --> Complete request with STATUS_CANCELLED
1578  *                         --> EXIT
1579  *                     <OvsTunnelFilterThreadPush>
1580  *                         --> add the request to one of tunnel thread queues
1581  *
1582  * --------------------------------------------------------------------------
1583  */
1584 NTSTATUS
1585 OvsTunnelFilterDelete(PIRP irp,
1586                       UINT64 filterID,
1587                       PFNTunnelVportPendingOp callback,
1588                       PVOID tunnelContext)
1589 {
1590     return OvsTunnelFilterQueueRequest(irp,
1591                                        0,
1592                                        &filterID,
1593                                        OVS_TUN_FILTER_DELETE,
1594                                        callback,
1595                                        tunnelContext);
1596 }
1597
1598 /*
1599  * --------------------------------------------------------------------------
1600  * This function sets the context for the IRP. The context is used by the
1601  * Cancel routine, in order to identify the request object, corresponding to
1602  * the IRP, to be completed and to have access to the queue lock to remove
1603  * the request link from the queue.
1604  * --------------------------------------------------------------------------
1605  */
1606 VOID
1607 OvsTunnelFilterSetIrpContext(POVS_TUNFLT_REQUEST_LIST listRequests,
1608                              POVS_TUNFLT_REQUEST request)
1609 {
1610     PIRP irp = request->irp;
1611
1612     if (irp) {
1613         /* Set the IRP's DriverContext to be used for later. */
1614         irp->Tail.Overlay.DriverContext[0] = (PVOID)request;
1615         irp->Tail.Overlay.DriverContext[1] = (PVOID)listRequests;
1616     }
1617 }
1618
1619 /*
1620  * --------------------------------------------------------------------------
1621  * This function is the Cancel routine to be called by the I/O Manager in the
1622  * case when the IRP is cancelled.
1623  * --------------------------------------------------------------------------
1624  */
1625 VOID
1626 OvsTunnelFilterCancelIrp(PDEVICE_OBJECT DeviceObject,
1627                          PIRP irp)
1628 {
1629     POVS_TUNFLT_REQUEST request =
1630         (POVS_TUNFLT_REQUEST)irp->Tail.Overlay.DriverContext[0];
1631     POVS_TUNFLT_REQUEST_LIST listRequests =
1632         (POVS_TUNFLT_REQUEST_LIST)irp->Tail.Overlay.DriverContext[1];
1633
1634     DBG_UNREFERENCED_PARAMETER(DeviceObject);
1635
1636     /* Release the global cancel spinlock. */
1637     IoReleaseCancelSpinLock(irp->CancelIrql);
1638
1639     /* Clear the cancel routine from the IRP. */
1640     IoSetCancelRoutine(irp, NULL);
1641
1642     NdisAcquireSpinLock(&listRequests->spinlock);
1643
1644     /* Remove the request from the corresponding tunnel filter thread queue. */
1645     RemoveEntryList(&request->entry);
1646     listRequests->numEntries--;
1647
1648     NdisReleaseSpinLock(&listRequests->spinlock);
1649
1650     /* We are done with this IRP, so complete it with STATUS_CANCELLED. */
1651     OvsTunnelFilterCompleteRequest(request->irp,
1652                                    request->callback,
1653                                    request->context,
1654                                    STATUS_CANCELLED);
1655
1656     OvsFreeMemory(request);
1657 }