231750e4682437aabfd0f73c730cf3913bb08384
[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         if (STATUS_FWP_ALREADY_EXISTS != status) {
418             OVS_LOG_ERROR("Failed to add WFP callout, status: %x.",
419                           status);
420             goto Exit;
421         }
422         status = STATUS_SUCCESS;
423     }
424
425 Exit:
426
427     if (!NT_SUCCESS(status)){
428         if (calloutRegistered) {
429             FwpsCalloutUnregisterById(*calloutId);
430             *calloutId = 0;
431         }
432     }
433
434     return status;
435 }
436
437 /*
438  * --------------------------------------------------------------------------
439  * This function registers non-dynamic callouts for intercepting UDP traffic.
440  * Callouts will be removed during un-initializing phase.
441  * --------------------------------------------------------------------------
442  */
443 NTSTATUS
444 OvsTunnelRegisterCallouts(VOID *deviceObject)
445 {
446     NTSTATUS        status = STATUS_SUCCESS;
447     BOOLEAN         inTransaction = FALSE;
448     FWPM_SUBLAYER   OvsTunnelSubLayer;
449
450     status = FwpmTransactionBegin(gEngineHandle, 0);
451     if (!NT_SUCCESS(status)) {
452         goto Exit;
453     }
454     inTransaction = TRUE;
455
456     RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER));
457
458     OvsTunnelSubLayer.subLayerKey = OVS_TUNNEL_SUBLAYER;
459     OvsTunnelSubLayer.displayData.name = L"Datagram-Data OVS Sub-Layer";
460     OvsTunnelSubLayer.displayData.description =
461         L"Sub-Layer for use by Datagram-Data OVS callouts";
462     OvsTunnelSubLayer.flags = 0;
463     OvsTunnelSubLayer.weight = FWP_EMPTY; /* auto-weight */
464
465     status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL);
466     if (!NT_SUCCESS(status)) {
467         if (STATUS_FWP_ALREADY_EXISTS != status) {
468             OVS_LOG_ERROR("Failed to add WFP sublayer, status: %x.",
469                           status);
470             goto Exit;
471         }
472     }
473
474     /* In order to use this callout a socket must be opened. */
475     status = OvsTunnelRegisterDatagramDataCallouts(&FWPM_LAYER_DATAGRAM_DATA_V4,
476                                                    &OVS_TUNNEL_CALLOUT_V4,
477                                                    deviceObject,
478                                                    &gCalloutIdV4);
479     if (!NT_SUCCESS(status)) {
480         goto Exit;
481     }
482
483     status = FwpmTransactionCommit(gEngineHandle);
484     if (!NT_SUCCESS(status)){
485         goto Exit;
486     }
487     inTransaction = FALSE;
488
489 Exit:
490
491     if (!NT_SUCCESS(status)) {
492         if (inTransaction) {
493             FwpmTransactionAbort(gEngineHandle);
494         }
495     }
496
497     return status;
498 }
499
500 VOID
501 OvsTunnelUnregisterCallouts()
502 {
503     FwpsCalloutUnregisterById(gCalloutIdV4);
504     FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER);
505     FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
506 }
507
508 VOID
509 OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
510 {
511     UNREFERENCED_PARAMETER(driverObject);
512
513     OvsTunnelFilterStopThreads();
514
515     OvsTunnelUnregisterCallouts();
516     OvsTunnelEngineClose(&gEngineHandle);
517
518     if (gDeviceObject) {
519         IoDeleteDevice(gDeviceObject);
520     }
521 }
522
523
524 NTSTATUS
525 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
526 {
527     NTSTATUS        status = STATUS_SUCCESS;
528     UNICODE_STRING  deviceName;
529
530     RtlInitUnicodeString(&deviceName,
531                          L"\\Device\\OvsTunnelFilter");
532
533     status = IoCreateDevice(driverObject,
534                             0,
535                             &deviceName,
536                             FILE_DEVICE_NETWORK,
537                             0,
538                             FALSE,
539                             &gDeviceObject);
540
541     if (!NT_SUCCESS(status)){
542         OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.",
543                       status);
544         goto Exit;
545     }
546
547     status = OvsTunnelFilterStartThreads();
548     if (!NT_SUCCESS(status)){
549         goto Exit;
550     }
551
552     status = OvsTunnelEngineOpen(&gEngineHandle);
553     if (!NT_SUCCESS(status)){
554         goto Exit;
555     }
556
557     status = OvsTunnelRegisterCallouts(gDeviceObject);
558     if (!NT_SUCCESS(status)) {
559         OVS_LOG_ERROR("Failed to register callout, status: %x.",
560                       status);
561     }
562
563 Exit:
564
565     if (!NT_SUCCESS(status)){
566         OvsTunnelFilterUninitialize(driverObject);
567     }
568
569     return status;
570 }
571
572 /*
573  * --------------------------------------------------------------------------
574  * This function adds OVS system provider to the system if the BFE (Base
575  * Filtering Engine) is running.
576  * --------------------------------------------------------------------------
577  */
578 VOID NTAPI
579 OvsTunnelProviderBfeCallback(PVOID context,
580                              FWPM_SERVICE_STATE bfeState)
581 {
582     HANDLE engineSession = NULL;
583
584     DBG_UNREFERENCED_PARAMETER(context);
585
586     if (FWPM_SERVICE_RUNNING == bfeState) {
587         OvsTunnelEngineOpen(&engineSession);
588         if (engineSession) {
589             OvsTunnelAddSystemProvider(engineSession);
590         }
591         OvsTunnelEngineClose(&engineSession);
592     }
593 }
594
595 /*
596  * --------------------------------------------------------------------------
597  * This function registers the OvsTunnelProviderBfeCallback callback that is
598  * called whenever there is a change to the state of base filtering engine.
599  * --------------------------------------------------------------------------
600  */
601 NTSTATUS
602 OvsSubscribeTunnelProviderBfeStateChanges(PVOID deviceObject)
603 {
604     NTSTATUS status = STATUS_SUCCESS;
605
606     if (!gTunnelProviderBfeHandle) {
607         status = FwpmBfeStateSubscribeChanges(deviceObject,
608                                               OvsTunnelProviderBfeCallback,
609                                               NULL,
610                                               &gTunnelProviderBfeHandle);
611         if (!NT_SUCCESS(status)) {
612             OVS_LOG_ERROR(
613                 "Failed to subscribe BFE tunnel provider callback, status: %x.",
614                 status);
615         }
616     }
617
618     return status;
619 }
620
621 /*
622  * --------------------------------------------------------------------------
623  * This function unregisters the OvsTunnelProviderBfeCallback callback that
624  * was previously registered by OvsSubscribeTunnelProviderBfeStateChanges
625  * function.
626  * --------------------------------------------------------------------------
627  */
628 VOID
629 OvsUnsubscribeTunnelProviderBfeStateChanges()
630 {
631     NTSTATUS status = STATUS_SUCCESS;
632
633     if (gTunnelProviderBfeHandle) {
634         status = FwpmBfeStateUnsubscribeChanges(gTunnelProviderBfeHandle);
635         if (!NT_SUCCESS(status)) {
636             OVS_LOG_ERROR(
637                 "Failed to unsubscribe BFE tunnel provider callback, status: %x.",
638                 status);
639         }
640         gTunnelProviderBfeHandle = NULL;
641     }
642 }
643
644 /*
645  * --------------------------------------------------------------------------
646  * This function registers the OVS system provider if the BFE (Base Filtering
647  * Engine) is running.
648  * Otherwise, it will register the OvsTunnelProviderBfeCallback callback.
649
650  * Note: Before calling FwpmBfeStateGet, the callout driver must call the
651  * FwpmBfeStateSubscribeChanges function to register the callback function
652  * to be called whenever the state of the filter engine changes.
653  *
654  * Register WFP system provider call hierarchy:
655  * <DriverEntry>
656  *     <OvsCreateDeviceObject>
657  *         <OvsRegisterSystemProvider>
658  *             <OvsSubscribeTunnelProviderBfeStateChanges>
659  *                 --> registers OvsTunnelProviderBfeCallback callback
660  *                     <OvsTunnelProviderBfeCallback>
661  *                         --> if BFE is running:
662  *                             <OvsTunnelAddSystemProvider>
663  *             --> if BFE is running:
664  *                 <OvsTunnelAddSystemProvider>
665  *                 <OvsUnsubscribeTunnelProviderBfeStateChanges>
666  *                     --> unregisters OvsTunnelProviderBfeCallback callback
667  *
668  * --------------------------------------------------------------------------
669  */
670 VOID
671 OvsRegisterSystemProvider(PVOID deviceObject)
672 {
673     NTSTATUS status = STATUS_SUCCESS;
674     HANDLE engineSession = NULL;
675
676     status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
677     if (NT_SUCCESS(status)) {
678         if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
679             OvsTunnelEngineOpen(&engineSession);
680             if (engineSession) {
681                 OvsTunnelAddSystemProvider(engineSession);
682             }
683             OvsTunnelEngineClose(&engineSession);
684
685             OvsUnsubscribeTunnelProviderBfeStateChanges();
686         }
687     }
688 }
689
690 /*
691  * --------------------------------------------------------------------------
692  * This function removes the OVS system provider and unregisters the
693  * OvsTunnelProviderBfeCallback callback from BFE (Base Filtering Engine).
694  *
695  * Unregister WFP system provider call hierarchy:
696  * <OvsExtUnload>
697  *     <OvsDeleteDeviceObject>
698  *         <OvsUnregisterSystemProvider>
699  *             <OvsTunnelRemoveSystemProvider>
700  *             <OvsUnsubscribeTunnelProviderBfeStateChanges>
701  *                 --> unregisters OvsTunnelProviderBfeCallback callback
702  *
703  * --------------------------------------------------------------------------
704  */
705 VOID
706 OvsUnregisterSystemProvider()
707 {
708     HANDLE engineSession = NULL;
709
710     OvsTunnelEngineOpen(&engineSession);
711     if (engineSession) {
712         OvsTunnelRemoveSystemProvider(engineSession);
713     }
714     OvsTunnelEngineClose(&engineSession);
715
716     OvsUnsubscribeTunnelProviderBfeStateChanges();
717 }
718
719 /*
720  * --------------------------------------------------------------------------
721  * This function initializes the tunnel filter if the BFE is running.
722  * --------------------------------------------------------------------------
723  */
724 VOID NTAPI
725 OvsTunnelInitBfeCallback(PVOID context,
726                          FWPM_SERVICE_STATE bfeState)
727 {
728     NTSTATUS status = STATUS_SUCCESS;
729     PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) context;
730
731     if (FWPM_SERVICE_RUNNING == bfeState) {
732         status = OvsTunnelFilterInitialize(driverObject);
733         if (!NT_SUCCESS(status)) {
734             OVS_LOG_ERROR(
735                 "Failed to initialize tunnel filter, status: %x.",
736                 status);
737         }
738     }
739 }
740
741 /*
742  * --------------------------------------------------------------------------
743  * This function registers the OvsTunnelInitBfeCallback callback that is
744  * called whenever there is a change to the state of base filtering engine.
745  * --------------------------------------------------------------------------
746  */
747 NTSTATUS
748 OvsSubscribeTunnelInitBfeStateChanges(PDRIVER_OBJECT driverObject,
749                                       PVOID deviceObject)
750 {
751     NTSTATUS status = STATUS_SUCCESS;
752
753     if (!gTunnelInitBfeHandle) {
754         status = FwpmBfeStateSubscribeChanges(deviceObject,
755                                               OvsTunnelInitBfeCallback,
756                                               driverObject,
757                                               &gTunnelInitBfeHandle);
758         if (!NT_SUCCESS(status)) {
759             OVS_LOG_ERROR(
760                 "Failed to subscribe BFE tunnel init callback, status: %x.",
761                 status);
762         }
763     }
764
765     return status;
766 }
767
768 /*
769  * --------------------------------------------------------------------------
770  * This function unregisters the OvsTunnelInitBfeCallback callback that
771  * was previously registered by OvsSubscribeTunnelInitBfeStateChanges
772  * function.
773  * --------------------------------------------------------------------------
774  */
775 VOID
776 OvsUnsubscribeTunnelInitBfeStateChanges()
777 {
778     NTSTATUS status = STATUS_SUCCESS;
779
780     if (gTunnelInitBfeHandle) {
781         status = FwpmBfeStateUnsubscribeChanges(gTunnelInitBfeHandle);
782         if (!NT_SUCCESS(status)) {
783             OVS_LOG_ERROR(
784                 "Failed to unsubscribe BFE tunnel init callback, status: %x.",
785                 status);
786         }
787         gTunnelInitBfeHandle = NULL;
788     }
789 }
790
791 /*
792  * --------------------------------------------------------------------------
793  * This function initializes the OVS tunnel filter if the BFE (Base Filtering
794  * Engine) is running.
795  * Otherwise, it will register the OvsTunnelInitBfeCallback callback.
796
797  * Note: Before calling FwpmBfeStateGet, the callout driver must call the
798  * FwpmBfeStateSubscribeChanges function to register the callback function
799  * to be called whenever the state of the filter engine changes.
800  *
801  * Initialize OVS tunnel filter call hierarchy:
802  * <OvsExtAttach>
803  *     <OvsCreateSwitch>
804  *         <OvsInitTunnelFilter>
805  *             <OvsSubscribeTunnelInitBfeStateChanges>
806  *                 --> registers OvsTunnelInitBfeCallback callback
807  *                     <OvsTunnelInitBfeCallback>
808  *                         --> if BFE is running:
809  *                             <OvsTunnelFilterInitialize>
810  *                                 <IoCreateDevice>
811  *                                 <OvsTunnelFilterStartThreads>
812  *                                 <OvsTunnelRegisterCallouts>
813  *             --> if BFE is running:
814  *                 <OvsTunnelFilterInitialize>
815  *                     <IoCreateDevice>
816  *                     <OvsTunnelFilterStartThreads>
817  *                     <OvsTunnelRegisterCallouts>
818  *                 <OvsUnsubscribeTunnelInitBfeStateChanges>
819  *                     --> unregisters OvsTunnelInitBfeCallback callback
820  *
821  * --------------------------------------------------------------------------
822  */
823 NTSTATUS
824 OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject)
825 {
826     NTSTATUS status = STATUS_SUCCESS;
827
828     status = OvsSubscribeTunnelInitBfeStateChanges(driverObject, deviceObject);
829     if (NT_SUCCESS(status)) {
830         if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
831             status = OvsTunnelFilterInitialize(driverObject);
832             if (!NT_SUCCESS(status)) {
833                 /* XXX: We need to decide what actions to take in case of
834                  * failure to initialize tunnel filter. */
835                 ASSERT(status == NDIS_STATUS_SUCCESS);
836                 OVS_LOG_ERROR(
837                     "Failed to initialize tunnel filter, status: %x.",
838                     status);
839             }
840             OvsUnsubscribeTunnelInitBfeStateChanges();
841         }
842     }
843
844     return status;
845 }
846
847 /*
848  * --------------------------------------------------------------------------
849  * This function uninitializes the OVS tunnel filter and unregisters the
850  * OvsTunnelInitBfeCallback callback from BFE.
851  *
852  * Uninitialize OVS tunnel filter call hierarchy:
853  * <OvsExtDetach>
854  *     <OvsDeleteSwitch>
855  *         <OvsUninitTunnelFilter>
856  *             <OvsTunnelFilterUninitialize>
857  *                 <OvsTunnelFilterStopThreads>
858  *                 <OvsTunnelUnregisterCallouts>
859  *                 <IoDeleteDevice>
860  *             <OvsUnsubscribeTunnelInitBfeStateChanges>
861  *                 --> unregisters OvsTunnelInitBfeCallback callback
862  *
863  * --------------------------------------------------------------------------
864  */
865 VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
866 {
867     OvsTunnelFilterUninitialize(driverObject);
868     OvsUnsubscribeTunnelInitBfeStateChanges();
869 }
870
871 NTSTATUS
872 OvsTunnelAddFilterEx(HANDLE engineSession,
873                      UINT32 filterPort,
874                      UINT64 *filterID)
875 {
876     NTSTATUS status = STATUS_SUCCESS;
877
878     status = OvsTunnelAddFilter(engineSession,
879                                 L"Datagram-Data OVS Filter (Inbound)",
880                                 L"address/port for UDP",
881                                 (USHORT)filterPort,
882                                 FWP_DIRECTION_INBOUND,
883                                 0,
884                                 NULL,
885                                 &FWPM_LAYER_DATAGRAM_DATA_V4,
886                                 &OVS_TUNNEL_CALLOUT_V4,
887                                 filterID);
888     if (!NT_SUCCESS(status)) {
889         OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.",
890                       filterPort, status);
891     } else {
892         OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.",
893                      filterPort, *filterID);
894     }
895
896     return status;
897 }
898
899 NTSTATUS
900 OvsTunnelRemoveFilterEx(HANDLE engineSession,
901                         UINT64 filterID)
902 {
903     NTSTATUS status = STATUS_SUCCESS;
904     BOOLEAN  error = TRUE;
905
906     do {
907         if (filterID == 0) {
908             OVS_LOG_INFO("No tunnel filter to remove.");
909             break;
910         }
911
912         status = FwpmFilterDeleteById(engineSession, filterID);
913         if (!NT_SUCCESS(status)) {
914             OVS_LOG_ERROR("Failed to remove tunnel with filter ID: %d,\
915                            status: %x.", filterID, status);
916             break;
917         }
918         OVS_LOG_INFO("Filter removed, filter ID: %d.",
919                      filterID);
920
921         error = FALSE;
922     } while (error);
923
924     return status;
925 }
926
927 NTSTATUS
928 OvsTunnelFilterExecuteAction(HANDLE engineSession,
929                              POVS_TUNFLT_REQUEST request)
930 {
931     NTSTATUS status = STATUS_SUCCESS;
932
933     switch (request->operation)
934     {
935     case OVS_TUN_FILTER_CREATE:
936         status = OvsTunnelAddFilterEx(engineSession,
937                                       request->port,
938                                       request->filterID.addID);
939         break;
940     case OVS_TUN_FILTER_DELETE:
941         status = OvsTunnelRemoveFilterEx(engineSession,
942                                          request->filterID.delID);
943         break;
944     default:
945         status = STATUS_NOT_SUPPORTED;
946         break;
947     }
948
949     return status;
950 }
951
952 /*
953  * --------------------------------------------------------------------------
954  * This function pops the whole request entries from the queue and returns the
955  * number of entries through the 'count' parameter. The operation is
956  * synchronized using request list spinlock.
957  * --------------------------------------------------------------------------
958  */
959 VOID
960 OvsTunnelFilterRequestPopList(POVS_TUNFLT_REQUEST_LIST listRequests,
961                               PLIST_ENTRY head,
962                               UINT32 *count)
963 {
964     NdisAcquireSpinLock(&listRequests->spinlock);
965
966     if (!IsListEmpty(&listRequests->head)) {
967         PLIST_ENTRY PrevEntry;
968         PLIST_ENTRY NextEntry;
969
970         NextEntry = listRequests->head.Flink;
971         PrevEntry = listRequests->head.Blink;
972
973         head->Flink = NextEntry;
974         NextEntry->Blink = head;
975
976         head->Blink = PrevEntry;
977         PrevEntry->Flink = head;
978
979         *count = listRequests->numEntries;
980
981         InitializeListHead(&listRequests->head);
982         listRequests->numEntries = 0;
983     }
984
985     NdisReleaseSpinLock(&listRequests->spinlock);
986 }
987
988 /*
989  * --------------------------------------------------------------------------
990  * This function pushes the received request to the list while holding the
991  * request list spinlock.
992  * --------------------------------------------------------------------------
993  */
994 VOID
995 OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests,
996                            POVS_TUNFLT_REQUEST request)
997 {
998     NdisAcquireSpinLock(&listRequests->spinlock);
999
1000     InsertTailList(&listRequests->head, &(request->entry));
1001     listRequests->numEntries++;
1002
1003     NdisReleaseSpinLock(&listRequests->spinlock);
1004 }
1005
1006 /*
1007  * --------------------------------------------------------------------------
1008  * This function pushes the received request to the corresponding thread
1009  * request queue. The arrival of the new request is signaled to the thread,
1010  * in order to start processing it.
1011  *
1012  * For a uniform distribution of requests to thread queues, a thread index is
1013  * calculated based on the received destination port.
1014  * --------------------------------------------------------------------------
1015  */
1016 VOID
1017 OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request)
1018 {
1019     UINT32 threadIndex;
1020
1021     threadIndex = request->port % OVS_TUNFLT_MAX_THREADS;
1022
1023     OvsTunnelFilterRequestPush(
1024         &gTunnelThreadCtx[threadIndex].listRequests,
1025         request);
1026
1027     KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent,
1028                IO_NO_INCREMENT,
1029                FALSE);
1030 }
1031
1032 VOID
1033 OvsTunnelFilterCompleteRequest(PIRP irp,
1034                                PFNTunnelVportPendingOp callback,
1035                                PVOID context,
1036                                NTSTATUS status)
1037 {
1038     UINT32 replyLen = 0;
1039
1040     if (callback) {
1041         callback(context, status, &replyLen);
1042         /* Release the context passed to the callback function. */
1043         OvsFreeMemory(context);
1044     }
1045
1046     if (irp) {
1047         OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
1048     }
1049 }
1050
1051 VOID
1052 OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1053 {
1054     POVS_TUNFLT_REQUEST request = NULL;
1055     PLIST_ENTRY         link = NULL;
1056     PLIST_ENTRY         next = NULL;
1057     LIST_ENTRY          head;
1058     NTSTATUS            status = STATUS_SUCCESS;
1059     UINT32              count = 0;
1060     BOOLEAN             inTransaction = FALSE;
1061     BOOLEAN             error = TRUE;
1062
1063     do
1064     {
1065         if (!InterlockedCompareExchange(
1066             (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) {
1067             OVS_LOG_INFO("Nothing to do... request list is empty.");
1068             break;
1069         }
1070
1071         status = FwpmTransactionBegin(threadCtx->engineSession, 0);
1072         if (!NT_SUCCESS(status)) {
1073             OVS_LOG_ERROR("Failed to start transaction, status: %x.",
1074                           status);
1075             break;
1076         }
1077         inTransaction = TRUE;
1078
1079         InitializeListHead(&head);
1080         OvsTunnelFilterRequestPopList(&threadCtx->listRequests, &head, &count);
1081
1082         LIST_FORALL_SAFE(&head, link, next) {
1083             request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry);
1084
1085             status = OvsTunnelFilterExecuteAction(threadCtx->engineSession,
1086                                                   request);
1087             if (!NT_SUCCESS(status)) {
1088                 RemoveEntryList(&request->entry);
1089                 count--;
1090
1091                 /* Complete the IRP with the failure status. */
1092                 OvsTunnelFilterCompleteRequest(request->irp,
1093                                                request->callback,
1094                                                request->context,
1095                                                status);
1096                 OvsFreeMemory(request);
1097                 request = NULL;
1098             } else {
1099                 error = FALSE;
1100             }
1101         }
1102
1103         if (error) {
1104             /* No successful requests were made, so there is no point to commit
1105              * the transaction. */
1106             break;
1107         }
1108
1109         status = FwpmTransactionCommit(threadCtx->engineSession);
1110         if (!NT_SUCCESS(status)){
1111             OVS_LOG_ERROR("Failed to commit transaction, status: %x.",
1112                           status);
1113             break;
1114         }
1115
1116         inTransaction = FALSE;
1117     } while (inTransaction);
1118
1119     if (inTransaction) {
1120         FwpmTransactionAbort(threadCtx->engineSession);
1121         OVS_LOG_ERROR("Failed to execute request, status: %x.\
1122                        Transaction aborted.", status);
1123     }
1124
1125     /* Complete the requests successfully executed with the transaction commit
1126      * status. */
1127     while (count) {
1128         request = (POVS_TUNFLT_REQUEST)RemoveHeadList(&head);
1129         count--;
1130
1131         OvsTunnelFilterCompleteRequest(request->irp,
1132                                        request->callback,
1133                                        request->context,
1134                                        status);
1135         OvsFreeMemory(request);
1136         request = NULL;
1137     }
1138 }
1139
1140 /*
1141  *----------------------------------------------------------------------------
1142  * System thread routine that processes thread's requests queue. The thread
1143  * routine initializes thread's necessary data and waits on two events,
1144  * requestEvent and stopEvent. Whenever a request is pushed to the thread's
1145  * queue, the requestEvent is signaled and the thread routine starts processing
1146  * the arrived requests. When stopEvent is signaled, all subsequent requests
1147  * are completed with STATUS_CANCELED, without being added to the thread's
1148  * queue, and the routine finishes processing all existing requests from the
1149  * queue before uninitializing the thread and exiting.
1150  *----------------------------------------------------------------------------
1151  */
1152 _Use_decl_annotations_
1153 VOID
1154 OvsTunnelFilterThreadProc(PVOID context)
1155 {
1156     NTSTATUS                   status = STATUS_SUCCESS;
1157     POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context;
1158     PKEVENT                    eventArray[2] = { 0 };
1159     ULONG                      count = 0;
1160     BOOLEAN                    exit = FALSE;
1161     BOOLEAN                    error = TRUE;
1162
1163     OVS_LOG_INFO("Starting OVS Tunnel system thread %d.",
1164                  threadCtx->threadID);
1165
1166     eventArray[0] = &threadCtx->stopEvent;
1167     eventArray[1] = &threadCtx->requestEvent;
1168     count = ARRAY_SIZE(eventArray);
1169
1170     do {
1171         status = OvsTunnelFilterThreadInit(threadCtx);
1172         if (!NT_SUCCESS(status)) {
1173             OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.",
1174                 threadCtx->threadID);
1175             break;
1176         }
1177
1178         do {
1179             status = KeWaitForMultipleObjects(count,
1180                                               (PVOID)eventArray,
1181                                               WaitAny,
1182                                               Executive,
1183                                               KernelMode,
1184                                               FALSE,
1185                                               NULL,
1186                                               NULL);
1187             switch (status) {
1188                 case STATUS_WAIT_1:
1189                     /* Start processing requests. */
1190                     OvsTunnelFilterRequestListProcess(threadCtx);
1191                     break;
1192                 default:
1193                     /* Finish processing the received requests and exit. */
1194                     OvsTunnelFilterRequestListProcess(threadCtx);
1195                     exit = TRUE;
1196                     break;
1197             }
1198         } while (!exit);
1199
1200         OvsTunnelFilterThreadUninit(threadCtx);
1201
1202         error = FALSE;
1203     } while (error);
1204
1205     OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.",
1206                  threadCtx->threadID);
1207
1208     PsTerminateSystemThread(STATUS_SUCCESS);
1209 };
1210
1211 static NTSTATUS
1212 OvsTunnelFilterStartThreads()
1213 {
1214     NTSTATUS status = STATUS_SUCCESS;
1215
1216     for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1217         gTunnelThreadCtx[index].threadID = index;
1218
1219         status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]);
1220         if (!NT_SUCCESS(status)) {
1221             OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index);
1222             break;
1223         }
1224     }
1225
1226     return status;
1227 }
1228
1229 static NTSTATUS
1230 OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1231 {
1232     NTSTATUS    status = STATUS_SUCCESS;
1233     HANDLE      threadHandle = NULL;
1234     BOOLEAN     error = TRUE;
1235
1236     do {
1237         status = PsCreateSystemThread(&threadHandle,
1238                                       SYNCHRONIZE,
1239                                       NULL,
1240                                       NULL,
1241                                       NULL,
1242                                       OvsTunnelFilterThreadProc,
1243                                       threadCtx);
1244         if (!NT_SUCCESS(status)) {
1245             OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
1246                           status);
1247             break;
1248         }
1249
1250         ObReferenceObjectByHandle(threadHandle,
1251                                   SYNCHRONIZE,
1252                                   NULL,
1253                                   KernelMode,
1254                                   &threadCtx->threadObject,
1255                                   NULL);
1256         ZwClose(threadHandle);
1257         threadHandle = NULL;
1258
1259         error = FALSE;
1260     } while (error);
1261
1262     return status;
1263 }
1264
1265 static VOID
1266 OvsTunnelFilterStopThreads()
1267 {
1268     /* Signal all threads to stop and ignore all subsequent requests. */
1269     for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1270         OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], TRUE);
1271     }
1272
1273     /* Wait for all threads to finish processing the requests. */
1274     for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1275         OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], FALSE);
1276     }
1277 }
1278
1279 static VOID
1280 OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
1281                           BOOLEAN signalEvent)
1282 {
1283     if (signalEvent) {
1284         /* Signal stop thread event. */
1285         OVS_LOG_INFO("Received stop event for OVS Tunnel system thread %d.",
1286                      threadCtx->threadID);
1287         KeSetEvent(&threadCtx->stopEvent, IO_NO_INCREMENT, FALSE);
1288     } else {
1289         /* Wait for the tunnel thread to finish. */
1290         KeWaitForSingleObject(threadCtx->threadObject,
1291                               Executive,
1292                               KernelMode,
1293                               FALSE,
1294                               NULL);
1295
1296         ObDereferenceObject(threadCtx->threadObject);
1297     }
1298 }
1299
1300 /*
1301  * --------------------------------------------------------------------------
1302  * This function initializes thread's necessary data. Each thread has its own
1303  * session object to the BFE that is used for processing the requests from
1304  * the thread's queue.
1305  * --------------------------------------------------------------------------
1306  */
1307 static NTSTATUS
1308 OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1309 {
1310     NTSTATUS status = STATUS_SUCCESS;
1311     BOOLEAN error = TRUE;
1312
1313     do {
1314         /* Create thread's engine session object. */
1315         status = OvsTunnelEngineOpen(&threadCtx->engineSession);
1316         if (!NT_SUCCESS(status)) {
1317             break;
1318         }
1319
1320         NdisAllocateSpinLock(&threadCtx->listRequests.spinlock);
1321
1322         InitializeListHead(&threadCtx->listRequests.head);
1323
1324         KeInitializeEvent(&threadCtx->stopEvent,
1325             NotificationEvent,
1326             FALSE);
1327
1328         KeInitializeEvent(&threadCtx->requestEvent,
1329             SynchronizationEvent,
1330             FALSE);
1331
1332         error = FALSE;
1333     } while (error);
1334
1335     return status;
1336 }
1337
1338 /*
1339  * --------------------------------------------------------------------------
1340  * This function uninitializes thread's private data. Thread's engine session
1341  * handle is closed and set to NULL.
1342  * --------------------------------------------------------------------------
1343  */
1344 static VOID
1345 OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1346 {
1347     if (threadCtx->engineSession) {
1348         /* Close thread's FWPM session. */
1349         OvsTunnelEngineClose(&threadCtx->engineSession);
1350
1351         NdisFreeSpinLock(&threadCtx->listRequests.spinlock);
1352     }
1353 }
1354
1355 /*
1356  * --------------------------------------------------------------------------
1357  * This function creates a new tunnel filter request and push it to a thread
1358  * queue. If the thread stop event is signaled, the request is completed with
1359  * STATUS_CANCELLED without pushing it to any queue.
1360  * --------------------------------------------------------------------------
1361  */
1362 NTSTATUS
1363 OvsTunnelFilterQueueRequest(PIRP irp,
1364                             UINT16 remotePort,
1365                             UINT64 *filterID,
1366                             OVS_TUNFLT_OPERATION operation,
1367                             PFNTunnelVportPendingOp callback,
1368                             PVOID tunnelContext)
1369 {
1370     POVS_TUNFLT_REQUEST request = NULL;
1371     NTSTATUS            status = STATUS_PENDING;
1372     BOOLEAN             error = TRUE;
1373     UINT64              timeout = 0;
1374
1375     do {
1376         /* Verify if the stop event was signaled. */
1377         if (STATUS_SUCCESS == KeWaitForSingleObject(
1378                                   &gTunnelThreadCtx[0].stopEvent,
1379                                   Executive,
1380                                   KernelMode,
1381                                   FALSE,
1382                                   (LARGE_INTEGER *)&timeout)) {
1383             /* The stop event is signaled. Completed the IRP with
1384              * STATUS_CANCELLED. */
1385             status = STATUS_CANCELLED;
1386             break;
1387         }
1388
1389         if (NULL == filterID) {
1390             OVS_LOG_ERROR("Invalid request.");
1391             status = STATUS_INVALID_PARAMETER;
1392             break;
1393         }
1394
1395         request = (POVS_TUNFLT_REQUEST) OvsAllocateMemory(sizeof(*request));
1396         if (NULL == request) {
1397             OVS_LOG_ERROR("Failed to allocate list item.");
1398             status = STATUS_INSUFFICIENT_RESOURCES;
1399             break;
1400         }
1401
1402         request->port = remotePort;
1403         request->operation = operation;
1404         switch (operation) {
1405             case OVS_TUN_FILTER_CREATE:
1406                 request->filterID.addID = filterID;
1407                 break;
1408             case OVS_TUN_FILTER_DELETE:
1409                 request->filterID.delID = *filterID;
1410                 break;
1411         }
1412         request->irp = irp;
1413         request->callback = callback;
1414         request->context = tunnelContext;
1415
1416         OvsTunnelFilterThreadPush(request);
1417
1418         error = FALSE;
1419     } while (error);
1420
1421     if (error) {
1422         OvsTunnelFilterCompleteRequest(irp, callback, tunnelContext, status);
1423         if (request) {
1424             OvsFreeMemory(request);
1425             request = NULL;
1426         }
1427     }
1428
1429     return status;
1430 }
1431
1432 /*
1433  * --------------------------------------------------------------------------
1434  *  This function adds a new WFP filter for the received port and returns the
1435  *  ID of the created WFP filter.
1436  *
1437  *  Note:
1438  *  All necessary calls to the WFP filtering engine must be running at IRQL =
1439  *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1440  *  we register an OVS_TUN_FILTER_CREATE request that will be processed by
1441  *  the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1442  *
1443  * OVS VXLAN port add call hierarchy:
1444  * <OvsNewVportCmdHandler>
1445  *     <OvsInitTunnelVport>
1446  *         <OvsInitVxlanTunnel>
1447  *             <OvsTunelFilterCreate>
1448  *                 <OvsTunnelFilterQueueRequest>
1449  *                     --> if thread STOP event is signalled:
1450  *                         --> Complete request with STATUS_CANCELLED
1451  *                         --> EXIT
1452  *                     <OvsTunnelFilterThreadPush>
1453  *                         --> add the request to one of tunnel thread queues
1454  *
1455  * --------------------------------------------------------------------------
1456  */
1457 NTSTATUS
1458 OvsTunelFilterCreate(PIRP irp,
1459                      UINT16 filterPort,
1460                      UINT64 *filterID,
1461                      PFNTunnelVportPendingOp callback,
1462                      PVOID tunnelContext)
1463 {
1464     return OvsTunnelFilterQueueRequest(irp,
1465                                        filterPort,
1466                                        filterID,
1467                                        OVS_TUN_FILTER_CREATE,
1468                                        callback,
1469                                        tunnelContext);
1470 }
1471
1472 /*
1473  * --------------------------------------------------------------------------
1474  *  This function removes a WFP filter using the received filter ID.
1475  *
1476  *  Note:
1477  *  All necessary calls to the WFP filtering engine must be running at IRQL =
1478  *  PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1479  *  we register an OVS_TUN_FILTER_DELETE request that will be processed by
1480  *  the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1481  *
1482  * OVS VXLAN port delete call hierarchy:
1483  * <OvsDeleteVportCmdHandler>
1484  *     <OvsRemoveAndDeleteVport>
1485  *         <OvsCleanupVxlanTunnel>
1486  *             <OvsTunelFilterCreate>
1487  *                 <OvsTunnelFilterQueueRequest>
1488  *                     --> if thread STOP event is signalled:
1489  *                         --> Complete request with STATUS_CANCELLED
1490  *                         --> EXIT
1491  *                     <OvsTunnelFilterThreadPush>
1492  *                         --> add the request to one of tunnel thread queues
1493  *
1494  * --------------------------------------------------------------------------
1495  */
1496 NTSTATUS
1497 OvsTunelFilterDelete(PIRP irp,
1498                      UINT64 filterID,
1499                      PFNTunnelVportPendingOp callback,
1500                      PVOID tunnelContext)
1501 {
1502     return OvsTunnelFilterQueueRequest(irp,
1503                                        0,
1504                                        &filterID,
1505                                        OVS_TUN_FILTER_DELETE,
1506                                        callback,
1507                                        tunnelContext);
1508 }