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)) {
417 if (STATUS_FWP_ALREADY_EXISTS != status) {
418 OVS_LOG_ERROR("Failed to add WFP callout, status: %x.",
422 status = STATUS_SUCCESS;
427 if (!NT_SUCCESS(status)){
428 if (calloutRegistered) {
429 FwpsCalloutUnregisterById(*calloutId);
438 * --------------------------------------------------------------------------
439 * This function registers non-dynamic callouts for intercepting UDP traffic.
440 * Callouts will be removed during un-initializing phase.
441 * --------------------------------------------------------------------------
444 OvsTunnelRegisterCallouts(VOID *deviceObject)
446 NTSTATUS status = STATUS_SUCCESS;
447 BOOLEAN inTransaction = FALSE;
448 FWPM_SUBLAYER OvsTunnelSubLayer;
450 status = FwpmTransactionBegin(gEngineHandle, 0);
451 if (!NT_SUCCESS(status)) {
454 inTransaction = TRUE;
456 RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER));
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 */
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.",
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,
479 if (!NT_SUCCESS(status)) {
483 status = FwpmTransactionCommit(gEngineHandle);
484 if (!NT_SUCCESS(status)){
487 inTransaction = FALSE;
491 if (!NT_SUCCESS(status)) {
493 FwpmTransactionAbort(gEngineHandle);
501 OvsTunnelUnregisterCallouts()
503 FwpsCalloutUnregisterById(gCalloutIdV4);
504 FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER);
505 FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
509 OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
511 UNREFERENCED_PARAMETER(driverObject);
513 OvsTunnelFilterStopThreads();
515 OvsTunnelUnregisterCallouts();
516 OvsTunnelEngineClose(&gEngineHandle);
519 IoDeleteDevice(gDeviceObject);
525 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
527 NTSTATUS status = STATUS_SUCCESS;
528 UNICODE_STRING deviceName;
530 RtlInitUnicodeString(&deviceName,
531 L"\\Device\\OvsTunnelFilter");
533 status = IoCreateDevice(driverObject,
541 if (!NT_SUCCESS(status)){
542 OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.",
547 status = OvsTunnelFilterStartThreads();
548 if (!NT_SUCCESS(status)){
552 status = OvsTunnelEngineOpen(&gEngineHandle);
553 if (!NT_SUCCESS(status)){
557 status = OvsTunnelRegisterCallouts(gDeviceObject);
558 if (!NT_SUCCESS(status)) {
559 OVS_LOG_ERROR("Failed to register callout, status: %x.",
565 if (!NT_SUCCESS(status)){
566 OvsTunnelFilterUninitialize(driverObject);
573 * --------------------------------------------------------------------------
574 * This function adds OVS system provider to the system if the BFE (Base
575 * Filtering Engine) is running.
576 * --------------------------------------------------------------------------
579 OvsTunnelProviderBfeCallback(PVOID context,
580 FWPM_SERVICE_STATE bfeState)
582 HANDLE engineSession = NULL;
584 DBG_UNREFERENCED_PARAMETER(context);
586 if (FWPM_SERVICE_RUNNING == bfeState) {
587 OvsTunnelEngineOpen(&engineSession);
589 OvsTunnelAddSystemProvider(engineSession);
591 OvsTunnelEngineClose(&engineSession);
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 * --------------------------------------------------------------------------
602 OvsSubscribeTunnelProviderBfeStateChanges(PVOID deviceObject)
604 NTSTATUS status = STATUS_SUCCESS;
606 if (!gTunnelProviderBfeHandle) {
607 status = FwpmBfeStateSubscribeChanges(deviceObject,
608 OvsTunnelProviderBfeCallback,
610 &gTunnelProviderBfeHandle);
611 if (!NT_SUCCESS(status)) {
613 "Failed to subscribe BFE tunnel provider callback, status: %x.",
622 * --------------------------------------------------------------------------
623 * This function unregisters the OvsTunnelProviderBfeCallback callback that
624 * was previously registered by OvsSubscribeTunnelProviderBfeStateChanges
626 * --------------------------------------------------------------------------
629 OvsUnsubscribeTunnelProviderBfeStateChanges()
631 NTSTATUS status = STATUS_SUCCESS;
633 if (gTunnelProviderBfeHandle) {
634 status = FwpmBfeStateUnsubscribeChanges(gTunnelProviderBfeHandle);
635 if (!NT_SUCCESS(status)) {
637 "Failed to unsubscribe BFE tunnel provider callback, status: %x.",
640 gTunnelProviderBfeHandle = NULL;
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.
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.
654 * Register WFP system provider call hierarchy:
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
668 * --------------------------------------------------------------------------
671 OvsRegisterSystemProvider(PVOID deviceObject)
673 NTSTATUS status = STATUS_SUCCESS;
674 HANDLE engineSession = NULL;
676 status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
677 if (NT_SUCCESS(status)) {
678 if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
679 OvsTunnelEngineOpen(&engineSession);
681 OvsTunnelAddSystemProvider(engineSession);
683 OvsTunnelEngineClose(&engineSession);
685 OvsUnsubscribeTunnelProviderBfeStateChanges();
691 * --------------------------------------------------------------------------
692 * This function removes the OVS system provider and unregisters the
693 * OvsTunnelProviderBfeCallback callback from BFE (Base Filtering Engine).
695 * Unregister WFP system provider call hierarchy:
697 * <OvsDeleteDeviceObject>
698 * <OvsUnregisterSystemProvider>
699 * <OvsTunnelRemoveSystemProvider>
700 * <OvsUnsubscribeTunnelProviderBfeStateChanges>
701 * --> unregisters OvsTunnelProviderBfeCallback callback
703 * --------------------------------------------------------------------------
706 OvsUnregisterSystemProvider()
708 HANDLE engineSession = NULL;
710 OvsTunnelEngineOpen(&engineSession);
712 OvsTunnelRemoveSystemProvider(engineSession);
714 OvsTunnelEngineClose(&engineSession);
716 OvsUnsubscribeTunnelProviderBfeStateChanges();
720 * --------------------------------------------------------------------------
721 * This function initializes the tunnel filter if the BFE is running.
722 * --------------------------------------------------------------------------
725 OvsTunnelInitBfeCallback(PVOID context,
726 FWPM_SERVICE_STATE bfeState)
728 NTSTATUS status = STATUS_SUCCESS;
729 PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) context;
731 if (FWPM_SERVICE_RUNNING == bfeState) {
732 status = OvsTunnelFilterInitialize(driverObject);
733 if (!NT_SUCCESS(status)) {
735 "Failed to initialize tunnel filter, status: %x.",
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 * --------------------------------------------------------------------------
748 OvsSubscribeTunnelInitBfeStateChanges(PDRIVER_OBJECT driverObject,
751 NTSTATUS status = STATUS_SUCCESS;
753 if (!gTunnelInitBfeHandle) {
754 status = FwpmBfeStateSubscribeChanges(deviceObject,
755 OvsTunnelInitBfeCallback,
757 &gTunnelInitBfeHandle);
758 if (!NT_SUCCESS(status)) {
760 "Failed to subscribe BFE tunnel init callback, status: %x.",
769 * --------------------------------------------------------------------------
770 * This function unregisters the OvsTunnelInitBfeCallback callback that
771 * was previously registered by OvsSubscribeTunnelInitBfeStateChanges
773 * --------------------------------------------------------------------------
776 OvsUnsubscribeTunnelInitBfeStateChanges()
778 NTSTATUS status = STATUS_SUCCESS;
780 if (gTunnelInitBfeHandle) {
781 status = FwpmBfeStateUnsubscribeChanges(gTunnelInitBfeHandle);
782 if (!NT_SUCCESS(status)) {
784 "Failed to unsubscribe BFE tunnel init callback, status: %x.",
787 gTunnelInitBfeHandle = NULL;
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.
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.
801 * Initialize OVS tunnel filter call hierarchy:
804 * <OvsInitTunnelFilter>
805 * <OvsSubscribeTunnelInitBfeStateChanges>
806 * --> registers OvsTunnelInitBfeCallback callback
807 * <OvsTunnelInitBfeCallback>
808 * --> if BFE is running:
809 * <OvsTunnelFilterInitialize>
811 * <OvsTunnelFilterStartThreads>
812 * <OvsTunnelRegisterCallouts>
813 * --> if BFE is running:
814 * <OvsTunnelFilterInitialize>
816 * <OvsTunnelFilterStartThreads>
817 * <OvsTunnelRegisterCallouts>
818 * <OvsUnsubscribeTunnelInitBfeStateChanges>
819 * --> unregisters OvsTunnelInitBfeCallback callback
821 * --------------------------------------------------------------------------
824 OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject)
826 NTSTATUS status = STATUS_SUCCESS;
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);
837 "Failed to initialize tunnel filter, status: %x.",
840 OvsUnsubscribeTunnelInitBfeStateChanges();
848 * --------------------------------------------------------------------------
849 * This function uninitializes the OVS tunnel filter and unregisters the
850 * OvsTunnelInitBfeCallback callback from BFE.
852 * Uninitialize OVS tunnel filter call hierarchy:
855 * <OvsUninitTunnelFilter>
856 * <OvsTunnelFilterUninitialize>
857 * <OvsTunnelFilterStopThreads>
858 * <OvsTunnelUnregisterCallouts>
860 * <OvsUnsubscribeTunnelInitBfeStateChanges>
861 * --> unregisters OvsTunnelInitBfeCallback callback
863 * --------------------------------------------------------------------------
865 VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
867 OvsTunnelFilterUninitialize(driverObject);
868 OvsUnsubscribeTunnelInitBfeStateChanges();
872 OvsTunnelAddFilterEx(HANDLE engineSession,
876 NTSTATUS status = STATUS_SUCCESS;
878 status = OvsTunnelAddFilter(engineSession,
879 L"Datagram-Data OVS Filter (Inbound)",
880 L"address/port for UDP",
882 FWP_DIRECTION_INBOUND,
885 &FWPM_LAYER_DATAGRAM_DATA_V4,
886 &OVS_TUNNEL_CALLOUT_V4,
888 if (!NT_SUCCESS(status)) {
889 OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.",
892 OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.",
893 filterPort, *filterID);
900 OvsTunnelRemoveFilterEx(HANDLE engineSession,
903 NTSTATUS status = STATUS_SUCCESS;
904 BOOLEAN error = TRUE;
908 OVS_LOG_INFO("No tunnel filter to remove.");
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);
918 OVS_LOG_INFO("Filter removed, filter ID: %d.",
928 OvsTunnelFilterExecuteAction(HANDLE engineSession,
929 POVS_TUNFLT_REQUEST request)
931 NTSTATUS status = STATUS_SUCCESS;
933 switch (request->operation)
935 case OVS_TUN_FILTER_CREATE:
936 status = OvsTunnelAddFilterEx(engineSession,
938 request->filterID.addID);
940 case OVS_TUN_FILTER_DELETE:
941 status = OvsTunnelRemoveFilterEx(engineSession,
942 request->filterID.delID);
945 status = STATUS_NOT_SUPPORTED;
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 * --------------------------------------------------------------------------
960 OvsTunnelFilterRequestPopList(POVS_TUNFLT_REQUEST_LIST listRequests,
964 NdisAcquireSpinLock(&listRequests->spinlock);
966 if (!IsListEmpty(&listRequests->head)) {
967 PLIST_ENTRY PrevEntry;
968 PLIST_ENTRY NextEntry;
970 NextEntry = listRequests->head.Flink;
971 PrevEntry = listRequests->head.Blink;
973 head->Flink = NextEntry;
974 NextEntry->Blink = head;
976 head->Blink = PrevEntry;
977 PrevEntry->Flink = head;
979 *count = listRequests->numEntries;
981 InitializeListHead(&listRequests->head);
982 listRequests->numEntries = 0;
985 NdisReleaseSpinLock(&listRequests->spinlock);
989 * --------------------------------------------------------------------------
990 * This function pushes the received request to the list while holding the
991 * request list spinlock.
992 * --------------------------------------------------------------------------
995 OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests,
996 POVS_TUNFLT_REQUEST request)
998 NdisAcquireSpinLock(&listRequests->spinlock);
1000 InsertTailList(&listRequests->head, &(request->entry));
1001 listRequests->numEntries++;
1003 NdisReleaseSpinLock(&listRequests->spinlock);
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.
1012 * For a uniform distribution of requests to thread queues, a thread index is
1013 * calculated based on the received destination port.
1014 * --------------------------------------------------------------------------
1017 OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request)
1021 threadIndex = request->port % OVS_TUNFLT_MAX_THREADS;
1023 OvsTunnelFilterRequestPush(
1024 &gTunnelThreadCtx[threadIndex].listRequests,
1027 KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent,
1033 OvsTunnelFilterCompleteRequest(PIRP irp,
1034 PFNTunnelVportPendingOp callback,
1038 UINT32 replyLen = 0;
1041 callback(context, status, &replyLen);
1042 /* Release the context passed to the callback function. */
1043 OvsFreeMemory(context);
1047 OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
1052 OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1054 POVS_TUNFLT_REQUEST request = NULL;
1055 PLIST_ENTRY link = NULL;
1056 PLIST_ENTRY next = NULL;
1058 NTSTATUS status = STATUS_SUCCESS;
1060 BOOLEAN inTransaction = FALSE;
1061 BOOLEAN error = TRUE;
1065 if (!InterlockedCompareExchange(
1066 (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) {
1067 OVS_LOG_INFO("Nothing to do... request list is empty.");
1071 status = FwpmTransactionBegin(threadCtx->engineSession, 0);
1072 if (!NT_SUCCESS(status)) {
1073 OVS_LOG_ERROR("Failed to start transaction, status: %x.",
1077 inTransaction = TRUE;
1079 InitializeListHead(&head);
1080 OvsTunnelFilterRequestPopList(&threadCtx->listRequests, &head, &count);
1082 LIST_FORALL_SAFE(&head, link, next) {
1083 request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry);
1085 status = OvsTunnelFilterExecuteAction(threadCtx->engineSession,
1087 if (!NT_SUCCESS(status)) {
1088 RemoveEntryList(&request->entry);
1091 /* Complete the IRP with the failure status. */
1092 OvsTunnelFilterCompleteRequest(request->irp,
1096 OvsFreeMemory(request);
1104 /* No successful requests were made, so there is no point to commit
1105 * the transaction. */
1109 status = FwpmTransactionCommit(threadCtx->engineSession);
1110 if (!NT_SUCCESS(status)){
1111 OVS_LOG_ERROR("Failed to commit transaction, status: %x.",
1116 inTransaction = FALSE;
1117 } while (inTransaction);
1119 if (inTransaction) {
1120 FwpmTransactionAbort(threadCtx->engineSession);
1121 OVS_LOG_ERROR("Failed to execute request, status: %x.\
1122 Transaction aborted.", status);
1125 /* Complete the requests successfully executed with the transaction commit
1128 request = (POVS_TUNFLT_REQUEST)RemoveHeadList(&head);
1131 OvsTunnelFilterCompleteRequest(request->irp,
1135 OvsFreeMemory(request);
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 *----------------------------------------------------------------------------
1152 _Use_decl_annotations_
1154 OvsTunnelFilterThreadProc(PVOID context)
1156 NTSTATUS status = STATUS_SUCCESS;
1157 POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context;
1158 PKEVENT eventArray[2] = { 0 };
1160 BOOLEAN exit = FALSE;
1161 BOOLEAN error = TRUE;
1163 OVS_LOG_INFO("Starting OVS Tunnel system thread %d.",
1164 threadCtx->threadID);
1166 eventArray[0] = &threadCtx->stopEvent;
1167 eventArray[1] = &threadCtx->requestEvent;
1168 count = ARRAY_SIZE(eventArray);
1171 status = OvsTunnelFilterThreadInit(threadCtx);
1172 if (!NT_SUCCESS(status)) {
1173 OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.",
1174 threadCtx->threadID);
1179 status = KeWaitForMultipleObjects(count,
1189 /* Start processing requests. */
1190 OvsTunnelFilterRequestListProcess(threadCtx);
1193 /* Finish processing the received requests and exit. */
1194 OvsTunnelFilterRequestListProcess(threadCtx);
1200 OvsTunnelFilterThreadUninit(threadCtx);
1205 OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.",
1206 threadCtx->threadID);
1208 PsTerminateSystemThread(STATUS_SUCCESS);
1212 OvsTunnelFilterStartThreads()
1214 NTSTATUS status = STATUS_SUCCESS;
1216 for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1217 gTunnelThreadCtx[index].threadID = index;
1219 status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]);
1220 if (!NT_SUCCESS(status)) {
1221 OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index);
1230 OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1232 NTSTATUS status = STATUS_SUCCESS;
1233 HANDLE threadHandle = NULL;
1234 BOOLEAN error = TRUE;
1237 status = PsCreateSystemThread(&threadHandle,
1242 OvsTunnelFilterThreadProc,
1244 if (!NT_SUCCESS(status)) {
1245 OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
1250 ObReferenceObjectByHandle(threadHandle,
1254 &threadCtx->threadObject,
1256 ZwClose(threadHandle);
1257 threadHandle = NULL;
1266 OvsTunnelFilterStopThreads()
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);
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);
1280 OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
1281 BOOLEAN 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);
1289 /* Wait for the tunnel thread to finish. */
1290 KeWaitForSingleObject(threadCtx->threadObject,
1296 ObDereferenceObject(threadCtx->threadObject);
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 * --------------------------------------------------------------------------
1308 OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1310 NTSTATUS status = STATUS_SUCCESS;
1311 BOOLEAN error = TRUE;
1314 /* Create thread's engine session object. */
1315 status = OvsTunnelEngineOpen(&threadCtx->engineSession);
1316 if (!NT_SUCCESS(status)) {
1320 NdisAllocateSpinLock(&threadCtx->listRequests.spinlock);
1322 InitializeListHead(&threadCtx->listRequests.head);
1324 KeInitializeEvent(&threadCtx->stopEvent,
1328 KeInitializeEvent(&threadCtx->requestEvent,
1329 SynchronizationEvent,
1339 * --------------------------------------------------------------------------
1340 * This function uninitializes thread's private data. Thread's engine session
1341 * handle is closed and set to NULL.
1342 * --------------------------------------------------------------------------
1345 OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1347 if (threadCtx->engineSession) {
1348 /* Close thread's FWPM session. */
1349 OvsTunnelEngineClose(&threadCtx->engineSession);
1351 NdisFreeSpinLock(&threadCtx->listRequests.spinlock);
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 * --------------------------------------------------------------------------
1363 OvsTunnelFilterQueueRequest(PIRP irp,
1366 OVS_TUNFLT_OPERATION operation,
1367 PFNTunnelVportPendingOp callback,
1368 PVOID tunnelContext)
1370 POVS_TUNFLT_REQUEST request = NULL;
1371 NTSTATUS status = STATUS_PENDING;
1372 BOOLEAN error = TRUE;
1376 /* Verify if the stop event was signaled. */
1377 if (STATUS_SUCCESS == KeWaitForSingleObject(
1378 &gTunnelThreadCtx[0].stopEvent,
1382 (LARGE_INTEGER *)&timeout)) {
1383 /* The stop event is signaled. Completed the IRP with
1384 * STATUS_CANCELLED. */
1385 status = STATUS_CANCELLED;
1389 if (NULL == filterID) {
1390 OVS_LOG_ERROR("Invalid request.");
1391 status = STATUS_INVALID_PARAMETER;
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;
1402 request->port = remotePort;
1403 request->operation = operation;
1404 switch (operation) {
1405 case OVS_TUN_FILTER_CREATE:
1406 request->filterID.addID = filterID;
1408 case OVS_TUN_FILTER_DELETE:
1409 request->filterID.delID = *filterID;
1413 request->callback = callback;
1414 request->context = tunnelContext;
1416 OvsTunnelFilterThreadPush(request);
1422 OvsTunnelFilterCompleteRequest(irp, callback, tunnelContext, status);
1424 OvsFreeMemory(request);
1433 * --------------------------------------------------------------------------
1434 * This function adds a new WFP filter for the received port and returns the
1435 * ID of the created WFP filter.
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.
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
1452 * <OvsTunnelFilterThreadPush>
1453 * --> add the request to one of tunnel thread queues
1455 * --------------------------------------------------------------------------
1458 OvsTunelFilterCreate(PIRP irp,
1461 PFNTunnelVportPendingOp callback,
1462 PVOID tunnelContext)
1464 return OvsTunnelFilterQueueRequest(irp,
1467 OVS_TUN_FILTER_CREATE,
1473 * --------------------------------------------------------------------------
1474 * This function removes a WFP filter using the received filter ID.
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.
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
1491 * <OvsTunnelFilterThreadPush>
1492 * --> add the request to one of tunnel thread queues
1494 * --------------------------------------------------------------------------
1497 OvsTunelFilterDelete(PIRP irp,
1499 PFNTunnelVportPendingOp callback,
1500 PVOID tunnelContext)
1502 return OvsTunnelFilterQueueRequest(irp,
1505 OVS_TUN_FILTER_DELETE,