2 * Copyright (c) 2014 VMware, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #pragma warning(disable:4201) // unnamed struct/union
24 #define OVS_DBG_MOD OVS_DBG_TUNFLT
35 #include <ip2string.h>
48 /* Infinite timeout */
49 #define INFINITE 0xFFFFFFFF
51 /* The provider name should always match the provider string from the install
53 #define OVS_TUNNEL_PROVIDER_NAME L"Open vSwitch"
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"
59 /* The session name isn't required but it's useful for diagnostics. */
60 #define OVS_TUNNEL_SESSION_NAME L"OVS tunnel session"
62 /* Maximum number of tunnel threads to be created. */
63 #define OVS_TUNFLT_MAX_THREADS 8
66 * Callout and sublayer GUIDs
69 /* b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8 */
71 OVS_TUNNEL_CALLOUT_V4,
75 0x8b, 0x39, 0xbd, 0x3f, 0xfc, 0x85, 0x5f, 0xf8
78 /* 0104fd7e-c825-414e-94c9-f0d525bbc169 */
84 0x94, 0xc9, 0xf0, 0xd5, 0x25, 0xbb, 0xc1, 0x69
87 /* 6fc957d7-14e7-47c7-812b-4668be994ba1 */
89 OVS_TUNNEL_PROVIDER_KEY,
93 0x81, 0x2b, 0x46, 0x68, 0xbe, 0x99, 0x4b, 0xa1
96 /* bfd4814c-9650-4de3-a536-1eedb9e9ba6a */
98 OVS_TUNNEL_FILTER_KEY,
102 0xa5, 0x36, 0x1e, 0xed, 0xb9, 0xe9, 0xba, 0x6a
106 * Callout driver type definitions
108 typedef enum _OVS_TUNFLT_OPERATION {
109 OVS_TUN_FILTER_CREATE = 0,
110 OVS_TUN_FILTER_DELETE
111 } OVS_TUNFLT_OPERATION;
113 typedef struct _OVS_TUNFLT_REQUEST {
115 /* Tunnel filter destination 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.*/
120 /* Tunnel filter identification used for filter deletion. */
122 /* Pointer used to return filter ID to the caller on filter creation. */
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. */
130 /* Callback function called before completing the IRP. */
131 PFNTunnelVportPendingOp callback;
132 /* Context passed to the callback function. */
134 } OVS_TUNFLT_REQUEST, *POVS_TUNFLT_REQUEST;
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. */
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. */
145 } OVS_TUNFLT_REQUEST_LIST, *POVS_TUNFLT_REQUEST_LIST;
147 typedef struct _OVS_TUNFLT_THREAD_CONTEXT {
148 /* Thread identification. */
150 /* Thread's engine session handle. */
151 HANDLE engineSession;
152 /* Reference of the thread object. */
154 /* Requests queue list. */
155 OVS_TUNFLT_REQUEST_LIST listRequests;
156 /* Event signaling that there are requests to process. */
158 /* Event for stopping thread execution. */
160 } OVS_TUNFLT_THREAD_CONTEXT, *POVS_TUNFLT_THREAD_CONTEXT;
162 KSTART_ROUTINE OvsTunnelFilterThreadProc;
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);
173 * Callout driver global variables
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 };
197 * Callout driver implementation.
201 OvsTunnelEngineOpen(HANDLE *engineSession)
203 NTSTATUS status = STATUS_SUCCESS;
204 FWPM_SESSION session = { 0 };
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.
210 session.txnWaitTimeoutInMSec = INFINITE;
212 /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
213 status = FwpmEngineOpen(NULL,
218 if (!NT_SUCCESS(status)) {
219 OVS_LOG_ERROR("Failed to open filtering engine session, status: %x.",
227 OvsTunnelEngineClose(HANDLE *engineSession)
229 if (*engineSession) {
230 FwpmEngineClose(*engineSession);
231 *engineSession = NULL;
236 OvsTunnelAddSystemProvider(HANDLE engineSession)
238 NTSTATUS status = STATUS_SUCCESS;
239 BOOLEAN inTransaction = FALSE;
240 FWPM_PROVIDER provider = { 0 };
243 status = FwpmTransactionBegin(engineSession, 0);
244 if (!NT_SUCCESS(status)) {
247 inTransaction = TRUE;
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;
254 * Since we always want the provider to be present, it's easiest to add
255 * it as persistent object during driver load.
257 provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
259 status = FwpmProviderAdd(engineSession,
262 if (!NT_SUCCESS(status)) {
263 if (STATUS_FWP_ALREADY_EXISTS != status) {
264 OVS_LOG_ERROR("Failed to add WFP provider, status: %x.",
270 status = FwpmTransactionCommit(engineSession);
271 if (!NT_SUCCESS(status)) {
275 inTransaction = FALSE;
276 } while (inTransaction);
279 FwpmTransactionAbort(engineSession);
284 OvsTunnelRemoveSystemProvider(HANDLE engineSession)
286 NTSTATUS status = STATUS_SUCCESS;
287 BOOLEAN inTransaction = FALSE;
290 status = FwpmTransactionBegin(engineSession, 0);
291 if (!NT_SUCCESS(status)) {
294 inTransaction = TRUE;
296 status = FwpmProviderDeleteByKey(engineSession,
297 &OVS_TUNNEL_PROVIDER_KEY);
298 if (!NT_SUCCESS(status)) {
302 status = FwpmTransactionCommit(engineSession);
303 if (!NT_SUCCESS(status)) {
307 inTransaction = FALSE;
308 } while (inTransaction);
311 FwpmTransactionAbort(engineSession);
316 OvsTunnelAddFilter(HANDLE engineSession,
318 const PWSTR filterDesc,
320 FWP_DIRECTION direction,
322 const GUID *filterKey,
323 const GUID *layerKey,
324 const GUID *calloutKey,
327 NTSTATUS status = STATUS_SUCCESS;
328 FWPM_FILTER filter = {0};
329 FWPM_FILTER_CONDITION filterConditions[3] = {0};
333 filter.filterKey = *filterKey;
335 filter.layerKey = *layerKey;
336 filter.displayData.name = (wchar_t*)filterName;
337 filter.displayData.description = (wchar_t*)filterDesc;
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;
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;
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;
362 filter.numFilterConditions = conditionIndex;
364 status = FwpmFilterAdd(engineSession,
373 * --------------------------------------------------------------------------
374 * This function registers callouts for intercepting UDP traffic at WFP
375 * FWPM_LAYER_DATAGRAM_DATA_V4 layer.
376 * --------------------------------------------------------------------------
379 OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
380 const GUID *calloutKey,
384 NTSTATUS status = STATUS_SUCCESS;
386 FWPS_CALLOUT sCallout = {0};
387 FWPM_CALLOUT mCallout = {0};
389 FWPM_DISPLAY_DATA displayData = {0};
391 BOOLEAN calloutRegistered = FALSE;
393 sCallout.calloutKey = *calloutKey;
394 sCallout.classifyFn = OvsTunnelClassify;
395 sCallout.notifyFn = OvsTunnelNotify;
397 /* Currently we don't associate a context with the flow */
398 sCallout.flowDeleteFn = OvsTunnelFlowDelete;
399 sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
402 status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId);
403 if (!NT_SUCCESS(status)) {
406 calloutRegistered = TRUE;
408 displayData.name = L"Datagram-Data OVS Callout";
409 displayData.description = L"Proxies destination address/port for UDP";
411 mCallout.calloutKey = *calloutKey;
412 mCallout.displayData = displayData;
413 mCallout.applicableLayer = *layerKey;
415 status = FwpmCalloutAdd(gEngineHandle, &mCallout, NULL, NULL);
416 if (!NT_SUCCESS(status)) {
422 if (!NT_SUCCESS(status)){
423 if (calloutRegistered) {
424 FwpsCalloutUnregisterById(*calloutId);
433 * --------------------------------------------------------------------------
434 * This function registers non-dynamic callouts for intercepting UDP traffic.
435 * Callouts will be removed during un-initializing phase.
436 * --------------------------------------------------------------------------
439 OvsTunnelRegisterCallouts(VOID *deviceObject)
441 NTSTATUS status = STATUS_SUCCESS;
442 BOOLEAN inTransaction = FALSE;
443 FWPM_SUBLAYER OvsTunnelSubLayer;
445 status = FwpmTransactionBegin(gEngineHandle, 0);
446 if (!NT_SUCCESS(status)) {
449 inTransaction = TRUE;
451 RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER));
453 OvsTunnelSubLayer.subLayerKey = OVS_TUNNEL_SUBLAYER;
454 OvsTunnelSubLayer.displayData.name = L"Datagram-Data OVS Sub-Layer";
455 OvsTunnelSubLayer.displayData.description =
456 L"Sub-Layer for use by Datagram-Data OVS callouts";
457 OvsTunnelSubLayer.flags = 0;
458 OvsTunnelSubLayer.weight = FWP_EMPTY; /* auto-weight */
460 status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL);
461 if (!NT_SUCCESS(status)) {
465 /* In order to use this callout a socket must be opened. */
466 status = OvsTunnelRegisterDatagramDataCallouts(&FWPM_LAYER_DATAGRAM_DATA_V4,
467 &OVS_TUNNEL_CALLOUT_V4,
470 if (!NT_SUCCESS(status)) {
474 status = FwpmTransactionCommit(gEngineHandle);
475 if (!NT_SUCCESS(status)){
478 inTransaction = FALSE;
482 if (!NT_SUCCESS(status)) {
484 FwpmTransactionAbort(gEngineHandle);
492 OvsTunnelUnregisterCallouts()
494 FwpsCalloutUnregisterById(gCalloutIdV4);
495 FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER);
496 FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
500 OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
502 UNREFERENCED_PARAMETER(driverObject);
504 OvsTunnelFilterStopThreads();
506 OvsTunnelUnregisterCallouts();
507 OvsTunnelEngineClose(&gEngineHandle);
510 IoDeleteDevice(gDeviceObject);
516 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
518 NTSTATUS status = STATUS_SUCCESS;
519 UNICODE_STRING deviceName;
521 RtlInitUnicodeString(&deviceName,
522 L"\\Device\\OvsTunnelFilter");
524 status = IoCreateDevice(driverObject,
532 if (!NT_SUCCESS(status)){
533 OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.",
538 status = OvsTunnelFilterStartThreads();
539 if (!NT_SUCCESS(status)){
543 status = OvsTunnelEngineOpen(&gEngineHandle);
544 if (!NT_SUCCESS(status)){
548 status = OvsTunnelRegisterCallouts(gDeviceObject);
549 if (!NT_SUCCESS(status)) {
550 OVS_LOG_ERROR("Failed to register callout, status: %x.",
556 if (!NT_SUCCESS(status)){
557 OvsTunnelFilterUninitialize(driverObject);
564 * --------------------------------------------------------------------------
565 * This function adds OVS system provider to the system if the BFE (Base
566 * Filtering Engine) is running.
567 * --------------------------------------------------------------------------
570 OvsTunnelProviderBfeCallback(PVOID context,
571 FWPM_SERVICE_STATE bfeState)
573 HANDLE engineSession = NULL;
575 DBG_UNREFERENCED_PARAMETER(context);
577 if (FWPM_SERVICE_RUNNING == bfeState) {
578 OvsTunnelEngineOpen(&engineSession);
580 OvsTunnelAddSystemProvider(engineSession);
582 OvsTunnelEngineClose(&engineSession);
587 * --------------------------------------------------------------------------
588 * This function registers the OvsTunnelProviderBfeCallback callback that is
589 * called whenever there is a change to the state of base filtering engine.
590 * --------------------------------------------------------------------------
593 OvsSubscribeTunnelProviderBfeStateChanges(PVOID deviceObject)
595 NTSTATUS status = STATUS_SUCCESS;
597 if (!gTunnelProviderBfeHandle) {
598 status = FwpmBfeStateSubscribeChanges(deviceObject,
599 OvsTunnelProviderBfeCallback,
601 &gTunnelProviderBfeHandle);
602 if (!NT_SUCCESS(status)) {
604 "Failed to subscribe BFE tunnel provider callback, status: %x.",
613 * --------------------------------------------------------------------------
614 * This function unregisters the OvsTunnelProviderBfeCallback callback that
615 * was previously registered by OvsSubscribeTunnelProviderBfeStateChanges
617 * --------------------------------------------------------------------------
620 OvsUnsubscribeTunnelProviderBfeStateChanges()
622 NTSTATUS status = STATUS_SUCCESS;
624 if (gTunnelProviderBfeHandle) {
625 status = FwpmBfeStateUnsubscribeChanges(gTunnelProviderBfeHandle);
626 if (!NT_SUCCESS(status)) {
628 "Failed to unsubscribe BFE tunnel provider callback, status: %x.",
631 gTunnelProviderBfeHandle = NULL;
636 * --------------------------------------------------------------------------
637 * This function registers the OVS system provider if the BFE (Base Filtering
638 * Engine) is running.
639 * Otherwise, it will register the OvsTunnelProviderBfeCallback callback.
641 * Note: Before calling FwpmBfeStateGet, the callout driver must call the
642 * FwpmBfeStateSubscribeChanges function to register the callback function
643 * to be called whenever the state of the filter engine changes.
645 * Register WFP system provider call hierarchy:
647 * <OvsCreateDeviceObject>
648 * <OvsRegisterSystemProvider>
649 * <OvsSubscribeTunnelProviderBfeStateChanges>
650 * --> registers OvsTunnelProviderBfeCallback callback
651 * <OvsTunnelProviderBfeCallback>
652 * --> if BFE is running:
653 * <OvsTunnelAddSystemProvider>
654 * --> if BFE is running:
655 * <OvsTunnelAddSystemProvider>
656 * <OvsUnsubscribeTunnelProviderBfeStateChanges>
657 * --> unregisters OvsTunnelProviderBfeCallback callback
659 * --------------------------------------------------------------------------
662 OvsRegisterSystemProvider(PVOID deviceObject)
664 NTSTATUS status = STATUS_SUCCESS;
665 HANDLE engineSession = NULL;
667 status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
668 if (NT_SUCCESS(status)) {
669 if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
670 OvsTunnelEngineOpen(&engineSession);
672 OvsTunnelAddSystemProvider(engineSession);
674 OvsTunnelEngineClose(&engineSession);
676 OvsUnsubscribeTunnelProviderBfeStateChanges();
682 * --------------------------------------------------------------------------
683 * This function removes the OVS system provider and unregisters the
684 * OvsTunnelProviderBfeCallback callback from BFE (Base Filtering Engine).
686 * Unregister WFP system provider call hierarchy:
688 * <OvsDeleteDeviceObject>
689 * <OvsUnregisterSystemProvider>
690 * <OvsTunnelRemoveSystemProvider>
691 * <OvsUnsubscribeTunnelProviderBfeStateChanges>
692 * --> unregisters OvsTunnelProviderBfeCallback callback
694 * --------------------------------------------------------------------------
697 OvsUnregisterSystemProvider()
699 HANDLE engineSession = NULL;
701 OvsTunnelEngineOpen(&engineSession);
703 OvsTunnelRemoveSystemProvider(engineSession);
705 OvsTunnelEngineClose(&engineSession);
707 OvsUnsubscribeTunnelProviderBfeStateChanges();
711 * --------------------------------------------------------------------------
712 * This function initializes the tunnel filter if the BFE is running.
713 * --------------------------------------------------------------------------
716 OvsTunnelInitBfeCallback(PVOID context,
717 FWPM_SERVICE_STATE bfeState)
719 NTSTATUS status = STATUS_SUCCESS;
720 PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) context;
722 if (FWPM_SERVICE_RUNNING == bfeState) {
723 status = OvsTunnelFilterInitialize(driverObject);
724 if (!NT_SUCCESS(status)) {
726 "Failed to initialize tunnel filter, status: %x.",
733 * --------------------------------------------------------------------------
734 * This function registers the OvsTunnelInitBfeCallback callback that is
735 * called whenever there is a change to the state of base filtering engine.
736 * --------------------------------------------------------------------------
739 OvsSubscribeTunnelInitBfeStateChanges(PDRIVER_OBJECT driverObject,
742 NTSTATUS status = STATUS_SUCCESS;
744 if (!gTunnelInitBfeHandle) {
745 status = FwpmBfeStateSubscribeChanges(deviceObject,
746 OvsTunnelInitBfeCallback,
748 &gTunnelInitBfeHandle);
749 if (!NT_SUCCESS(status)) {
751 "Failed to subscribe BFE tunnel init callback, status: %x.",
760 * --------------------------------------------------------------------------
761 * This function unregisters the OvsTunnelInitBfeCallback callback that
762 * was previously registered by OvsSubscribeTunnelInitBfeStateChanges
764 * --------------------------------------------------------------------------
767 OvsUnsubscribeTunnelInitBfeStateChanges()
769 NTSTATUS status = STATUS_SUCCESS;
771 if (gTunnelInitBfeHandle) {
772 status = FwpmBfeStateUnsubscribeChanges(gTunnelInitBfeHandle);
773 if (!NT_SUCCESS(status)) {
775 "Failed to unsubscribe BFE tunnel init callback, status: %x.",
778 gTunnelInitBfeHandle = NULL;
783 * --------------------------------------------------------------------------
784 * This function initializes the OVS tunnel filter if the BFE (Base Filtering
785 * Engine) is running.
786 * Otherwise, it will register the OvsTunnelInitBfeCallback callback.
788 * Note: Before calling FwpmBfeStateGet, the callout driver must call the
789 * FwpmBfeStateSubscribeChanges function to register the callback function
790 * to be called whenever the state of the filter engine changes.
792 * Initialize OVS tunnel filter call hierarchy:
795 * <OvsInitTunnelFilter>
796 * <OvsSubscribeTunnelInitBfeStateChanges>
797 * --> registers OvsTunnelInitBfeCallback callback
798 * <OvsTunnelInitBfeCallback>
799 * --> if BFE is running:
800 * <OvsTunnelFilterInitialize>
802 * <OvsTunnelFilterStartThreads>
803 * <OvsTunnelRegisterCallouts>
804 * --> if BFE is running:
805 * <OvsTunnelFilterInitialize>
807 * <OvsTunnelFilterStartThreads>
808 * <OvsTunnelRegisterCallouts>
809 * <OvsUnsubscribeTunnelInitBfeStateChanges>
810 * --> unregisters OvsTunnelInitBfeCallback callback
812 * --------------------------------------------------------------------------
815 OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject)
817 NTSTATUS status = STATUS_SUCCESS;
819 status = OvsSubscribeTunnelInitBfeStateChanges(driverObject, deviceObject);
820 if (NT_SUCCESS(status)) {
821 if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
822 status = OvsTunnelFilterInitialize(driverObject);
823 if (!NT_SUCCESS(status)) {
824 /* XXX: We need to decide what actions to take in case of
825 * failure to initialize tunnel filter. */
826 ASSERT(status == NDIS_STATUS_SUCCESS);
828 "Failed to initialize tunnel filter, status: %x.",
831 OvsUnsubscribeTunnelInitBfeStateChanges();
839 * --------------------------------------------------------------------------
840 * This function uninitializes the OVS tunnel filter and unregisters the
841 * OvsTunnelInitBfeCallback callback from BFE.
843 * Uninitialize OVS tunnel filter call hierarchy:
846 * <OvsUninitTunnelFilter>
847 * <OvsTunnelFilterUninitialize>
848 * <OvsTunnelFilterStopThreads>
849 * <OvsTunnelUnregisterCallouts>
851 * <OvsUnsubscribeTunnelInitBfeStateChanges>
852 * --> unregisters OvsTunnelInitBfeCallback callback
854 * --------------------------------------------------------------------------
856 VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
858 OvsTunnelFilterUninitialize(driverObject);
859 OvsUnsubscribeTunnelInitBfeStateChanges();
863 OvsTunnelAddFilterEx(HANDLE engineSession,
867 NTSTATUS status = STATUS_SUCCESS;
869 status = OvsTunnelAddFilter(engineSession,
870 L"Datagram-Data OVS Filter (Inbound)",
871 L"address/port for UDP",
873 FWP_DIRECTION_INBOUND,
876 &FWPM_LAYER_DATAGRAM_DATA_V4,
877 &OVS_TUNNEL_CALLOUT_V4,
879 if (!NT_SUCCESS(status)) {
880 OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.",
883 OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.",
884 filterPort, *filterID);
891 OvsTunnelRemoveFilterEx(HANDLE engineSession,
894 NTSTATUS status = STATUS_SUCCESS;
895 BOOLEAN error = TRUE;
899 OVS_LOG_INFO("No tunnel filter to remove.");
903 status = FwpmFilterDeleteById(engineSession, filterID);
904 if (!NT_SUCCESS(status)) {
905 OVS_LOG_ERROR("Failed to remove tunnel with filter ID: %d,\
906 status: %x.", filterID, status);
909 OVS_LOG_INFO("Filter removed, filter ID: %d.",
919 OvsTunnelFilterExecuteAction(HANDLE engineSession,
920 POVS_TUNFLT_REQUEST request)
922 NTSTATUS status = STATUS_SUCCESS;
924 switch (request->operation)
926 case OVS_TUN_FILTER_CREATE:
927 status = OvsTunnelAddFilterEx(engineSession,
929 request->filterID.addID);
931 case OVS_TUN_FILTER_DELETE:
932 status = OvsTunnelRemoveFilterEx(engineSession,
933 request->filterID.delID);
936 status = STATUS_NOT_SUPPORTED;
944 * --------------------------------------------------------------------------
945 * This function pops the whole request entries from the queue and returns the
946 * number of entries through the 'count' parameter. The operation is
947 * synchronized using request list spinlock.
948 * --------------------------------------------------------------------------
951 OvsTunnelFilterRequestPopList(POVS_TUNFLT_REQUEST_LIST listRequests,
955 NdisAcquireSpinLock(&listRequests->spinlock);
957 if (!IsListEmpty(&listRequests->head)) {
958 PLIST_ENTRY PrevEntry;
959 PLIST_ENTRY NextEntry;
961 NextEntry = listRequests->head.Flink;
962 PrevEntry = listRequests->head.Blink;
964 head->Flink = NextEntry;
965 NextEntry->Blink = head;
967 head->Blink = PrevEntry;
968 PrevEntry->Flink = head;
970 *count = listRequests->numEntries;
972 InitializeListHead(&listRequests->head);
973 listRequests->numEntries = 0;
976 NdisReleaseSpinLock(&listRequests->spinlock);
980 * --------------------------------------------------------------------------
981 * This function pushes the received request to the list while holding the
982 * request list spinlock.
983 * --------------------------------------------------------------------------
986 OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests,
987 POVS_TUNFLT_REQUEST request)
989 NdisAcquireSpinLock(&listRequests->spinlock);
991 InsertTailList(&listRequests->head, &(request->entry));
992 listRequests->numEntries++;
994 NdisReleaseSpinLock(&listRequests->spinlock);
998 * --------------------------------------------------------------------------
999 * This function pushes the received request to the corresponding thread
1000 * request queue. The arrival of the new request is signaled to the thread,
1001 * in order to start processing it.
1003 * For a uniform distribution of requests to thread queues, a thread index is
1004 * calculated based on the received destination port.
1005 * --------------------------------------------------------------------------
1008 OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request)
1012 threadIndex = request->port % OVS_TUNFLT_MAX_THREADS;
1014 OvsTunnelFilterRequestPush(
1015 &gTunnelThreadCtx[threadIndex].listRequests,
1018 KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent,
1024 OvsTunnelFilterCompleteRequest(PIRP irp,
1025 PFNTunnelVportPendingOp callback,
1029 UINT32 replyLen = 0;
1032 callback(context, status, &replyLen);
1033 /* Release the context passed to the callback function. */
1034 OvsFreeMemory(context);
1038 OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
1043 OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1045 POVS_TUNFLT_REQUEST request = NULL;
1046 PLIST_ENTRY link = NULL;
1047 PLIST_ENTRY next = NULL;
1049 NTSTATUS status = STATUS_SUCCESS;
1051 BOOLEAN inTransaction = FALSE;
1052 BOOLEAN error = TRUE;
1056 if (!InterlockedCompareExchange(
1057 (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) {
1058 OVS_LOG_INFO("Nothing to do... request list is empty.");
1062 status = FwpmTransactionBegin(threadCtx->engineSession, 0);
1063 if (!NT_SUCCESS(status)) {
1064 OVS_LOG_ERROR("Failed to start transaction, status: %x.",
1068 inTransaction = TRUE;
1070 InitializeListHead(&head);
1071 OvsTunnelFilterRequestPopList(&threadCtx->listRequests, &head, &count);
1073 LIST_FORALL_SAFE(&head, link, next) {
1074 request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry);
1076 status = OvsTunnelFilterExecuteAction(threadCtx->engineSession,
1078 if (!NT_SUCCESS(status)) {
1079 RemoveEntryList(&request->entry);
1082 /* Complete the IRP with the failure status. */
1083 OvsTunnelFilterCompleteRequest(request->irp,
1087 OvsFreeMemory(request);
1095 /* No successful requests were made, so there is no point to commit
1096 * the transaction. */
1100 status = FwpmTransactionCommit(threadCtx->engineSession);
1101 if (!NT_SUCCESS(status)){
1102 OVS_LOG_ERROR("Failed to commit transaction, status: %x.",
1107 inTransaction = FALSE;
1108 } while (inTransaction);
1110 if (inTransaction) {
1111 FwpmTransactionAbort(threadCtx->engineSession);
1112 OVS_LOG_ERROR("Failed to execute request, status: %x.\
1113 Transaction aborted.", status);
1116 /* Complete the requests successfully executed with the transaction commit
1119 request = (POVS_TUNFLT_REQUEST)RemoveHeadList(&head);
1122 OvsTunnelFilterCompleteRequest(request->irp,
1126 OvsFreeMemory(request);
1132 *----------------------------------------------------------------------------
1133 * System thread routine that processes thread's requests queue. The thread
1134 * routine initializes thread's necessary data and waits on two events,
1135 * requestEvent and stopEvent. Whenever a request is pushed to the thread's
1136 * queue, the requestEvent is signaled and the thread routine starts processing
1137 * the arrived requests. When stopEvent is signaled, all subsequent requests
1138 * are completed with STATUS_CANCELED, without being added to the thread's
1139 * queue, and the routine finishes processing all existing requests from the
1140 * queue before uninitializing the thread and exiting.
1141 *----------------------------------------------------------------------------
1143 _Use_decl_annotations_
1145 OvsTunnelFilterThreadProc(PVOID context)
1147 NTSTATUS status = STATUS_SUCCESS;
1148 POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context;
1149 PKEVENT eventArray[2] = { 0 };
1151 BOOLEAN exit = FALSE;
1152 BOOLEAN error = TRUE;
1154 OVS_LOG_INFO("Starting OVS Tunnel system thread %d.",
1155 threadCtx->threadID);
1157 eventArray[0] = &threadCtx->stopEvent;
1158 eventArray[1] = &threadCtx->requestEvent;
1159 count = ARRAY_SIZE(eventArray);
1162 status = OvsTunnelFilterThreadInit(threadCtx);
1163 if (!NT_SUCCESS(status)) {
1164 OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.",
1165 threadCtx->threadID);
1170 status = KeWaitForMultipleObjects(count,
1180 /* Start processing requests. */
1181 OvsTunnelFilterRequestListProcess(threadCtx);
1184 /* Finish processing the received requests and exit. */
1185 OvsTunnelFilterRequestListProcess(threadCtx);
1191 OvsTunnelFilterThreadUninit(threadCtx);
1196 OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.",
1197 threadCtx->threadID);
1199 PsTerminateSystemThread(STATUS_SUCCESS);
1203 OvsTunnelFilterStartThreads()
1205 NTSTATUS status = STATUS_SUCCESS;
1207 for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1208 gTunnelThreadCtx[index].threadID = index;
1210 status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]);
1211 if (!NT_SUCCESS(status)) {
1212 OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index);
1221 OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1223 NTSTATUS status = STATUS_SUCCESS;
1224 HANDLE threadHandle = NULL;
1225 BOOLEAN error = TRUE;
1228 status = PsCreateSystemThread(&threadHandle,
1233 OvsTunnelFilterThreadProc,
1235 if (!NT_SUCCESS(status)) {
1236 OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
1241 ObReferenceObjectByHandle(threadHandle,
1245 &threadCtx->threadObject,
1247 ZwClose(threadHandle);
1248 threadHandle = NULL;
1257 OvsTunnelFilterStopThreads()
1259 /* Signal all threads to stop and ignore all subsequent requests. */
1260 for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1261 OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], TRUE);
1264 /* Wait for all threads to finish processing the requests. */
1265 for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1266 OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], FALSE);
1271 OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
1272 BOOLEAN signalEvent)
1275 /* Signal stop thread event. */
1276 OVS_LOG_INFO("Received stop event for OVS Tunnel system thread %d.",
1277 threadCtx->threadID);
1278 KeSetEvent(&threadCtx->stopEvent, IO_NO_INCREMENT, FALSE);
1280 /* Wait for the tunnel thread to finish. */
1281 KeWaitForSingleObject(threadCtx->threadObject,
1287 ObDereferenceObject(threadCtx->threadObject);
1292 * --------------------------------------------------------------------------
1293 * This function initializes thread's necessary data. Each thread has its own
1294 * session object to the BFE that is used for processing the requests from
1295 * the thread's queue.
1296 * --------------------------------------------------------------------------
1299 OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1301 NTSTATUS status = STATUS_SUCCESS;
1302 BOOLEAN error = TRUE;
1305 /* Create thread's engine session object. */
1306 status = OvsTunnelEngineOpen(&threadCtx->engineSession);
1307 if (!NT_SUCCESS(status)) {
1311 NdisAllocateSpinLock(&threadCtx->listRequests.spinlock);
1313 InitializeListHead(&threadCtx->listRequests.head);
1315 KeInitializeEvent(&threadCtx->stopEvent,
1319 KeInitializeEvent(&threadCtx->requestEvent,
1320 SynchronizationEvent,
1330 * --------------------------------------------------------------------------
1331 * This function uninitializes thread's private data. Thread's engine session
1332 * handle is closed and set to NULL.
1333 * --------------------------------------------------------------------------
1336 OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1338 if (threadCtx->engineSession) {
1339 /* Close thread's FWPM session. */
1340 OvsTunnelEngineClose(&threadCtx->engineSession);
1342 NdisFreeSpinLock(&threadCtx->listRequests.spinlock);
1347 * --------------------------------------------------------------------------
1348 * This function creates a new tunnel filter request and push it to a thread
1349 * queue. If the thread stop event is signaled, the request is completed with
1350 * STATUS_CANCELLED without pushing it to any queue.
1351 * --------------------------------------------------------------------------
1354 OvsTunnelFilterQueueRequest(PIRP irp,
1357 OVS_TUNFLT_OPERATION operation,
1358 PFNTunnelVportPendingOp callback,
1359 PVOID tunnelContext)
1361 POVS_TUNFLT_REQUEST request = NULL;
1362 NTSTATUS status = STATUS_PENDING;
1363 BOOLEAN error = TRUE;
1367 /* Verify if the stop event was signaled. */
1368 if (STATUS_SUCCESS == KeWaitForSingleObject(
1369 &gTunnelThreadCtx[0].stopEvent,
1373 (LARGE_INTEGER *)&timeout)) {
1374 /* The stop event is signaled. Completed the IRP with
1375 * STATUS_CANCELLED. */
1376 status = STATUS_CANCELLED;
1380 if (NULL == filterID) {
1381 OVS_LOG_ERROR("Invalid request.");
1382 status = STATUS_INVALID_PARAMETER;
1386 request = (POVS_TUNFLT_REQUEST) OvsAllocateMemory(sizeof(*request));
1387 if (NULL == request) {
1388 OVS_LOG_ERROR("Failed to allocate list item.");
1389 status = STATUS_INSUFFICIENT_RESOURCES;
1393 request->port = remotePort;
1394 request->operation = operation;
1395 switch (operation) {
1396 case OVS_TUN_FILTER_CREATE:
1397 request->filterID.addID = filterID;
1399 case OVS_TUN_FILTER_DELETE:
1400 request->filterID.delID = *filterID;
1404 request->callback = callback;
1405 request->context = tunnelContext;
1407 OvsTunnelFilterThreadPush(request);
1413 OvsTunnelFilterCompleteRequest(irp, callback, tunnelContext, status);
1415 OvsFreeMemory(request);
1424 * --------------------------------------------------------------------------
1425 * This function adds a new WFP filter for the received port and returns the
1426 * ID of the created WFP filter.
1429 * All necessary calls to the WFP filtering engine must be running at IRQL =
1430 * PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1431 * we register an OVS_TUN_FILTER_CREATE request that will be processed by
1432 * the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1434 * OVS VXLAN port add call hierarchy:
1435 * <OvsNewVportCmdHandler>
1436 * <OvsInitTunnelVport>
1437 * <OvsInitVxlanTunnel>
1438 * <OvsTunelFilterCreate>
1439 * <OvsTunnelFilterQueueRequest>
1440 * --> if thread STOP event is signalled:
1441 * --> Complete request with STATUS_CANCELLED
1443 * <OvsTunnelFilterThreadPush>
1444 * --> add the request to one of tunnel thread queues
1446 * --------------------------------------------------------------------------
1449 OvsTunelFilterCreate(PIRP irp,
1452 PFNTunnelVportPendingOp callback,
1453 PVOID tunnelContext)
1455 return OvsTunnelFilterQueueRequest(irp,
1458 OVS_TUN_FILTER_CREATE,
1464 * --------------------------------------------------------------------------
1465 * This function removes a WFP filter using the received filter ID.
1468 * All necessary calls to the WFP filtering engine must be running at IRQL =
1469 * PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1470 * we register an OVS_TUN_FILTER_DELETE request that will be processed by
1471 * the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1473 * OVS VXLAN port delete call hierarchy:
1474 * <OvsDeleteVportCmdHandler>
1475 * <OvsRemoveAndDeleteVport>
1476 * <OvsCleanupVxlanTunnel>
1477 * <OvsTunelFilterCreate>
1478 * <OvsTunnelFilterQueueRequest>
1479 * --> if thread STOP event is signalled:
1480 * --> Complete request with STATUS_CANCELLED
1482 * <OvsTunnelFilterThreadPush>
1483 * --> add the request to one of tunnel thread queues
1485 * --------------------------------------------------------------------------
1488 OvsTunelFilterDelete(PIRP irp,
1490 PFNTunnelVportPendingOp callback,
1491 PVOID tunnelContext)
1493 return OvsTunnelFilterQueueRequest(irp,
1496 OVS_TUN_FILTER_DELETE,