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 initialization flag. */
151 UINT32 isInitialized;
152 /* Thread's engine session handle. */
153 HANDLE engineSession;
154 /* Reference of the thread object. */
156 /* Requests queue list. */
157 OVS_TUNFLT_REQUEST_LIST listRequests;
158 /* Event signaling that there are requests to process. */
160 /* Event for stopping thread execution. */
162 } OVS_TUNFLT_THREAD_CONTEXT, *POVS_TUNFLT_THREAD_CONTEXT;
164 KSTART_ROUTINE OvsTunnelFilterThreadProc;
166 static NTSTATUS OvsTunnelFilterStartThreads();
167 static NTSTATUS OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
168 static VOID OvsTunnelFilterStopThreads();
169 static VOID OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
170 BOOLEAN signalEvent);
171 static NTSTATUS OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
172 static VOID OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx);
173 static VOID OvsTunnelFilterSetIrpContext(POVS_TUNFLT_REQUEST_LIST listRequests,
174 POVS_TUNFLT_REQUEST request);
175 static VOID OvsTunnelFilterCancelIrp(PDEVICE_OBJECT DeviceObject,
179 * Callout driver global variables
182 /* Pointer to the device object that must be create before we can register our
183 * callout to the base filtering engine. */
184 static PDEVICE_OBJECT gDeviceObject = NULL;
185 /* Handle to an open session to the filter engine that is used for adding
186 * tunnel's callout. */
187 static HANDLE gEngineHandle = NULL;
188 /* A pointer to the received handle that is associated with the registration of
189 * the OvsTunnelProviderBfeCallback callback. */
190 static HANDLE gTunnelProviderBfeHandle = NULL;
191 /* A pointer to the received handle that is associated with the registration of
192 * the OvsTunnelInitBfeCallback callback. */
193 static HANDLE gTunnelInitBfeHandle = NULL;
194 /* Runtime identifier for tunnel's callout which is retrieved at tunnel
195 * initialization phase when the callout is registered. This ID is then used
196 * for removing the callout object from the system at tunnel
197 * uninitialization phase. */
198 static UINT32 gCalloutIdV4 = 0;
199 /* Array used for storing tunnel thread's private data. */
200 static OVS_TUNFLT_THREAD_CONTEXT gTunnelThreadCtx[OVS_TUNFLT_MAX_THREADS] = { 0 };
203 * Callout driver implementation.
207 OvsTunnelEngineOpen(HANDLE *engineSession)
209 NTSTATUS status = STATUS_SUCCESS;
210 FWPM_SESSION session = { 0 };
213 * Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT
214 * errors while waiting to acquire the transaction lock.
216 session.txnWaitTimeoutInMSec = INFINITE;
218 /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
219 status = FwpmEngineOpen(NULL,
224 if (!NT_SUCCESS(status)) {
225 OVS_LOG_ERROR("Failed to open filtering engine session, status: %x.",
233 OvsTunnelEngineClose(HANDLE *engineSession)
235 if (*engineSession) {
236 FwpmEngineClose(*engineSession);
237 *engineSession = NULL;
242 OvsTunnelAddSystemProvider(HANDLE engineSession)
244 NTSTATUS status = STATUS_SUCCESS;
245 BOOLEAN inTransaction = FALSE;
246 FWPM_PROVIDER provider = { 0 };
249 status = FwpmTransactionBegin(engineSession, 0);
250 if (!NT_SUCCESS(status)) {
253 inTransaction = TRUE;
255 memset(&provider, 0, sizeof(provider));
256 provider.providerKey = OVS_TUNNEL_PROVIDER_KEY;
257 provider.displayData.name = OVS_TUNNEL_PROVIDER_NAME;
258 provider.displayData.description = OVS_TUNNEL_PROVIDER_DESC;
260 * Since we always want the provider to be present, it's easiest to add
261 * it as persistent object during driver load.
263 provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
265 status = FwpmProviderAdd(engineSession,
268 if (!NT_SUCCESS(status)) {
269 if (STATUS_FWP_ALREADY_EXISTS != status) {
270 OVS_LOG_ERROR("Failed to add WFP provider, status: %x.",
276 status = FwpmTransactionCommit(engineSession);
277 if (!NT_SUCCESS(status)) {
281 inTransaction = FALSE;
282 } while (inTransaction);
285 FwpmTransactionAbort(engineSession);
290 OvsTunnelRemoveSystemProvider(HANDLE engineSession)
292 NTSTATUS status = STATUS_SUCCESS;
293 BOOLEAN inTransaction = FALSE;
296 status = FwpmTransactionBegin(engineSession, 0);
297 if (!NT_SUCCESS(status)) {
300 inTransaction = TRUE;
302 status = FwpmProviderDeleteByKey(engineSession,
303 &OVS_TUNNEL_PROVIDER_KEY);
304 if (!NT_SUCCESS(status)) {
308 status = FwpmTransactionCommit(engineSession);
309 if (!NT_SUCCESS(status)) {
313 inTransaction = FALSE;
314 } while (inTransaction);
317 FwpmTransactionAbort(engineSession);
322 OvsTunnelAddFilter(HANDLE engineSession,
324 const PWSTR filterDesc,
326 FWP_DIRECTION direction,
328 const GUID *filterKey,
329 const GUID *layerKey,
330 const GUID *calloutKey,
333 NTSTATUS status = STATUS_SUCCESS;
334 FWPM_FILTER filter = {0};
335 FWPM_FILTER_CONDITION filterConditions[3] = {0};
339 filter.filterKey = *filterKey;
341 filter.layerKey = *layerKey;
342 filter.displayData.name = (wchar_t*)filterName;
343 filter.displayData.description = (wchar_t*)filterDesc;
345 filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
346 filter.action.calloutKey = *calloutKey;
347 filter.filterCondition = filterConditions;
348 filter.subLayerKey = OVS_TUNNEL_SUBLAYER;
349 filter.weight.type = FWP_EMPTY; // auto-weight.
350 filter.rawContext = context;
354 filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_DIRECTION;
355 filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
356 filterConditions[conditionIndex].conditionValue.type = FWP_UINT32;
357 filterConditions[conditionIndex].conditionValue.uint32 = direction;
361 filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
362 filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
363 filterConditions[conditionIndex].conditionValue.type = FWP_UINT16;
364 filterConditions[conditionIndex].conditionValue.uint16 = remotePort;
368 filter.numFilterConditions = conditionIndex;
370 status = FwpmFilterAdd(engineSession,
379 * --------------------------------------------------------------------------
380 * This function registers callouts for intercepting UDP traffic at WFP
381 * FWPM_LAYER_DATAGRAM_DATA_V4 layer.
382 * --------------------------------------------------------------------------
385 OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
386 const GUID *calloutKey,
390 NTSTATUS status = STATUS_SUCCESS;
392 FWPS_CALLOUT sCallout = {0};
393 FWPM_CALLOUT mCallout = {0};
395 FWPM_DISPLAY_DATA displayData = {0};
397 BOOLEAN calloutRegistered = FALSE;
399 sCallout.calloutKey = *calloutKey;
400 sCallout.classifyFn = OvsTunnelClassify;
401 sCallout.notifyFn = OvsTunnelNotify;
403 /* Currently we don't associate a context with the flow */
404 sCallout.flowDeleteFn = OvsTunnelFlowDelete;
405 sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
408 status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId);
409 if (!NT_SUCCESS(status)) {
412 calloutRegistered = TRUE;
414 displayData.name = L"Datagram-Data OVS Callout";
415 displayData.description = L"Proxies destination address/port for UDP";
417 mCallout.calloutKey = *calloutKey;
418 mCallout.displayData = displayData;
419 mCallout.applicableLayer = *layerKey;
421 status = FwpmCalloutAdd(gEngineHandle, &mCallout, NULL, NULL);
422 if (!NT_SUCCESS(status)) {
423 if (STATUS_FWP_ALREADY_EXISTS != status) {
424 OVS_LOG_ERROR("Failed to add WFP callout, status: %x.",
428 status = STATUS_SUCCESS;
433 if (!NT_SUCCESS(status)){
434 if (calloutRegistered) {
435 FwpsCalloutUnregisterById(*calloutId);
444 * --------------------------------------------------------------------------
445 * This function registers non-dynamic callouts for intercepting UDP traffic.
446 * Callouts will be removed during un-initializing phase.
447 * --------------------------------------------------------------------------
450 OvsTunnelRegisterCallouts(VOID *deviceObject)
452 NTSTATUS status = STATUS_SUCCESS;
453 BOOLEAN inTransaction = FALSE;
454 FWPM_SUBLAYER OvsTunnelSubLayer;
456 status = FwpmTransactionBegin(gEngineHandle, 0);
457 if (!NT_SUCCESS(status)) {
460 inTransaction = TRUE;
462 RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER));
464 OvsTunnelSubLayer.subLayerKey = OVS_TUNNEL_SUBLAYER;
465 OvsTunnelSubLayer.displayData.name = L"Datagram-Data OVS Sub-Layer";
466 OvsTunnelSubLayer.displayData.description =
467 L"Sub-Layer for use by Datagram-Data OVS callouts";
468 OvsTunnelSubLayer.flags = 0;
469 OvsTunnelSubLayer.weight = FWP_EMPTY; /* auto-weight */
471 status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL);
472 if (!NT_SUCCESS(status)) {
473 if (STATUS_FWP_ALREADY_EXISTS != status) {
474 OVS_LOG_ERROR("Failed to add WFP sublayer, status: %x.",
480 /* In order to use this callout a socket must be opened. */
481 status = OvsTunnelRegisterDatagramDataCallouts(&FWPM_LAYER_DATAGRAM_DATA_V4,
482 &OVS_TUNNEL_CALLOUT_V4,
485 if (!NT_SUCCESS(status)) {
489 status = FwpmTransactionCommit(gEngineHandle);
490 if (!NT_SUCCESS(status)){
493 inTransaction = FALSE;
497 if (!NT_SUCCESS(status)) {
499 FwpmTransactionAbort(gEngineHandle);
507 OvsTunnelUnregisterCallouts()
509 FwpsCalloutUnregisterById(gCalloutIdV4);
510 FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER);
511 FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
515 OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
517 UNREFERENCED_PARAMETER(driverObject);
519 OvsTunnelFilterStopThreads();
521 OvsTunnelUnregisterCallouts();
522 OvsTunnelEngineClose(&gEngineHandle);
525 IoDeleteDevice(gDeviceObject);
531 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
533 NTSTATUS status = STATUS_SUCCESS;
534 UNICODE_STRING deviceName;
536 RtlInitUnicodeString(&deviceName,
537 L"\\Device\\OvsTunnelFilter");
539 status = IoCreateDevice(driverObject,
547 if (!NT_SUCCESS(status)){
548 OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.",
553 status = OvsTunnelFilterStartThreads();
554 if (!NT_SUCCESS(status)){
558 status = OvsTunnelEngineOpen(&gEngineHandle);
559 if (!NT_SUCCESS(status)){
563 status = OvsTunnelRegisterCallouts(gDeviceObject);
564 if (!NT_SUCCESS(status)) {
565 OVS_LOG_ERROR("Failed to register callout, status: %x.",
571 if (!NT_SUCCESS(status)){
572 OvsTunnelFilterUninitialize(driverObject);
579 * --------------------------------------------------------------------------
580 * This function adds OVS system provider to the system if the BFE (Base
581 * Filtering Engine) is running.
582 * --------------------------------------------------------------------------
585 OvsTunnelProviderBfeCallback(PVOID context,
586 FWPM_SERVICE_STATE bfeState)
588 HANDLE engineSession = NULL;
590 DBG_UNREFERENCED_PARAMETER(context);
592 if (FWPM_SERVICE_RUNNING == bfeState) {
593 OvsTunnelEngineOpen(&engineSession);
595 OvsTunnelAddSystemProvider(engineSession);
597 OvsTunnelEngineClose(&engineSession);
602 * --------------------------------------------------------------------------
603 * This function registers the OvsTunnelProviderBfeCallback callback that is
604 * called whenever there is a change to the state of base filtering engine.
605 * --------------------------------------------------------------------------
608 OvsSubscribeTunnelProviderBfeStateChanges(PVOID deviceObject)
610 NTSTATUS status = STATUS_SUCCESS;
612 if (!gTunnelProviderBfeHandle) {
613 status = FwpmBfeStateSubscribeChanges(deviceObject,
614 OvsTunnelProviderBfeCallback,
616 &gTunnelProviderBfeHandle);
617 if (!NT_SUCCESS(status)) {
619 "Failed to subscribe BFE tunnel provider callback, status: %x.",
628 * --------------------------------------------------------------------------
629 * This function unregisters the OvsTunnelProviderBfeCallback callback that
630 * was previously registered by OvsSubscribeTunnelProviderBfeStateChanges
632 * --------------------------------------------------------------------------
635 OvsUnsubscribeTunnelProviderBfeStateChanges()
637 NTSTATUS status = STATUS_SUCCESS;
639 if (gTunnelProviderBfeHandle) {
640 status = FwpmBfeStateUnsubscribeChanges(gTunnelProviderBfeHandle);
641 if (!NT_SUCCESS(status)) {
643 "Failed to unsubscribe BFE tunnel provider callback, status: %x.",
646 gTunnelProviderBfeHandle = NULL;
651 * --------------------------------------------------------------------------
652 * This function registers the OVS system provider if the BFE (Base Filtering
653 * Engine) is running.
654 * Otherwise, it will register the OvsTunnelProviderBfeCallback callback.
656 * Note: Before calling FwpmBfeStateGet, the callout driver must call the
657 * FwpmBfeStateSubscribeChanges function to register the callback function
658 * to be called whenever the state of the filter engine changes.
660 * Register WFP system provider call hierarchy:
662 * <OvsCreateDeviceObject>
663 * <OvsRegisterSystemProvider>
664 * <OvsSubscribeTunnelProviderBfeStateChanges>
665 * --> registers OvsTunnelProviderBfeCallback callback
666 * <OvsTunnelProviderBfeCallback>
667 * --> if BFE is running:
668 * <OvsTunnelAddSystemProvider>
669 * --> if BFE is running:
670 * <OvsTunnelAddSystemProvider>
671 * <OvsUnsubscribeTunnelProviderBfeStateChanges>
672 * --> unregisters OvsTunnelProviderBfeCallback callback
674 * --------------------------------------------------------------------------
677 OvsRegisterSystemProvider(PVOID deviceObject)
679 NTSTATUS status = STATUS_SUCCESS;
680 HANDLE engineSession = NULL;
682 status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
683 if (NT_SUCCESS(status)) {
684 if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
685 OvsTunnelEngineOpen(&engineSession);
687 OvsTunnelAddSystemProvider(engineSession);
689 OvsTunnelEngineClose(&engineSession);
691 OvsUnsubscribeTunnelProviderBfeStateChanges();
697 * --------------------------------------------------------------------------
698 * This function removes the OVS system provider and unregisters the
699 * OvsTunnelProviderBfeCallback callback from BFE (Base Filtering Engine).
701 * Unregister WFP system provider call hierarchy:
703 * <OvsDeleteDeviceObject>
704 * <OvsUnregisterSystemProvider>
705 * <OvsTunnelRemoveSystemProvider>
706 * <OvsUnsubscribeTunnelProviderBfeStateChanges>
707 * --> unregisters OvsTunnelProviderBfeCallback callback
709 * --------------------------------------------------------------------------
712 OvsUnregisterSystemProvider()
714 HANDLE engineSession = NULL;
716 OvsTunnelEngineOpen(&engineSession);
718 OvsTunnelRemoveSystemProvider(engineSession);
720 OvsTunnelEngineClose(&engineSession);
722 OvsUnsubscribeTunnelProviderBfeStateChanges();
726 * --------------------------------------------------------------------------
727 * This function initializes the tunnel filter if the BFE is running.
728 * --------------------------------------------------------------------------
731 OvsTunnelInitBfeCallback(PVOID context,
732 FWPM_SERVICE_STATE bfeState)
734 NTSTATUS status = STATUS_SUCCESS;
735 PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) context;
737 if (FWPM_SERVICE_RUNNING == bfeState) {
738 status = OvsTunnelFilterInitialize(driverObject);
739 if (!NT_SUCCESS(status)) {
741 "Failed to initialize tunnel filter, status: %x.",
748 * --------------------------------------------------------------------------
749 * This function registers the OvsTunnelInitBfeCallback callback that is
750 * called whenever there is a change to the state of base filtering engine.
751 * --------------------------------------------------------------------------
754 OvsSubscribeTunnelInitBfeStateChanges(PDRIVER_OBJECT driverObject,
757 NTSTATUS status = STATUS_SUCCESS;
759 if (!gTunnelInitBfeHandle) {
760 status = FwpmBfeStateSubscribeChanges(deviceObject,
761 OvsTunnelInitBfeCallback,
763 &gTunnelInitBfeHandle);
764 if (!NT_SUCCESS(status)) {
766 "Failed to subscribe BFE tunnel init callback, status: %x.",
775 * --------------------------------------------------------------------------
776 * This function unregisters the OvsTunnelInitBfeCallback callback that
777 * was previously registered by OvsSubscribeTunnelInitBfeStateChanges
779 * --------------------------------------------------------------------------
782 OvsUnsubscribeTunnelInitBfeStateChanges()
784 NTSTATUS status = STATUS_SUCCESS;
786 if (gTunnelInitBfeHandle) {
787 status = FwpmBfeStateUnsubscribeChanges(gTunnelInitBfeHandle);
788 if (!NT_SUCCESS(status)) {
790 "Failed to unsubscribe BFE tunnel init callback, status: %x.",
793 gTunnelInitBfeHandle = NULL;
798 * --------------------------------------------------------------------------
799 * This function initializes the OVS tunnel filter if the BFE (Base Filtering
800 * Engine) is running.
801 * Otherwise, it will register the OvsTunnelInitBfeCallback callback.
803 * Note: Before calling FwpmBfeStateGet, the callout driver must call the
804 * FwpmBfeStateSubscribeChanges function to register the callback function
805 * to be called whenever the state of the filter engine changes.
807 * Initialize OVS tunnel filter call hierarchy:
810 * <OvsInitTunnelFilter>
811 * <OvsSubscribeTunnelInitBfeStateChanges>
812 * --> registers OvsTunnelInitBfeCallback callback
813 * <OvsTunnelInitBfeCallback>
814 * --> if BFE is running:
815 * <OvsTunnelFilterInitialize>
817 * <OvsTunnelFilterStartThreads>
818 * <OvsTunnelRegisterCallouts>
819 * --> if BFE is running:
820 * <OvsTunnelFilterInitialize>
822 * <OvsTunnelFilterStartThreads>
823 * <OvsTunnelRegisterCallouts>
824 * <OvsUnsubscribeTunnelInitBfeStateChanges>
825 * --> unregisters OvsTunnelInitBfeCallback callback
827 * --------------------------------------------------------------------------
830 OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject)
832 NTSTATUS status = STATUS_SUCCESS;
835 status = OvsSubscribeTunnelInitBfeStateChanges(driverObject, deviceObject);
836 if (NT_SUCCESS(status)) {
837 if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
838 status = OvsTunnelFilterInitialize(driverObject);
839 if (!NT_SUCCESS(status)) {
840 /* XXX: We need to decide what actions to take in case of
841 * failure to initialize tunnel filter. */
842 ASSERT(status == NDIS_STATUS_SUCCESS);
844 "Failed to initialize tunnel filter, status: %x.",
847 OvsUnsubscribeTunnelInitBfeStateChanges();
851 status = OvsTunnelFilterInitialize(driverObject);
858 * --------------------------------------------------------------------------
859 * This function uninitializes the OVS tunnel filter and unregisters the
860 * OvsTunnelInitBfeCallback callback from BFE.
862 * Uninitialize OVS tunnel filter call hierarchy:
865 * <OvsUninitTunnelFilter>
866 * <OvsTunnelFilterUninitialize>
867 * <OvsTunnelFilterStopThreads>
868 * <OvsTunnelUnregisterCallouts>
870 * <OvsUnsubscribeTunnelInitBfeStateChanges>
871 * --> unregisters OvsTunnelInitBfeCallback callback
873 * --------------------------------------------------------------------------
875 VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
877 OvsTunnelFilterUninitialize(driverObject);
878 OvsUnsubscribeTunnelInitBfeStateChanges();
882 OvsTunnelAddFilterEx(HANDLE engineSession,
886 NTSTATUS status = STATUS_SUCCESS;
888 status = OvsTunnelAddFilter(engineSession,
889 L"Datagram-Data OVS Filter (Inbound)",
890 L"address/port for UDP",
892 FWP_DIRECTION_INBOUND,
895 &FWPM_LAYER_DATAGRAM_DATA_V4,
896 &OVS_TUNNEL_CALLOUT_V4,
898 if (!NT_SUCCESS(status)) {
899 OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.",
902 OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.",
903 filterPort, *filterID);
910 OvsTunnelRemoveFilterEx(HANDLE engineSession,
913 NTSTATUS status = STATUS_SUCCESS;
914 BOOLEAN error = TRUE;
918 OVS_LOG_INFO("No tunnel filter to remove.");
922 status = FwpmFilterDeleteById(engineSession, filterID);
923 if (!NT_SUCCESS(status)) {
924 OVS_LOG_ERROR("Failed to remove tunnel with filter ID: %d,\
925 status: %x.", filterID, status);
928 OVS_LOG_INFO("Filter removed, filter ID: %d.",
938 OvsTunnelFilterExecuteAction(HANDLE engineSession,
939 POVS_TUNFLT_REQUEST request)
941 NTSTATUS status = STATUS_SUCCESS;
943 switch (request->operation)
945 case OVS_TUN_FILTER_CREATE:
946 status = OvsTunnelAddFilterEx(engineSession,
948 request->filterID.addID);
950 case OVS_TUN_FILTER_DELETE:
951 status = OvsTunnelRemoveFilterEx(engineSession,
952 request->filterID.delID);
955 status = STATUS_NOT_SUPPORTED;
963 * --------------------------------------------------------------------------
964 * This function pops the head request from the queue while holding the
965 * queue lock. If the request has already been cancelled or is about to be
966 * cancelled, the function retrieves the next valid request.
968 * Returns a pointer to the OVS_TUNFLT_REQUEST_LIST request object retrieved
970 * --------------------------------------------------------------------------
973 OvsTunnelFilterRequestPop(POVS_TUNFLT_REQUEST_LIST listRequests)
975 POVS_TUNFLT_REQUEST request = NULL;
976 PLIST_ENTRY link, next, head;
978 NdisAcquireSpinLock(&listRequests->spinlock);
980 if (!IsListEmpty(&listRequests->head)) {
981 head = &listRequests->head;
982 LIST_FORALL_SAFE(head, link, next) {
983 PDRIVER_CANCEL oldCancelRoutine;
985 request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry);
987 oldCancelRoutine = IoSetCancelRoutine(request->irp, NULL);
988 if (oldCancelRoutine == NULL) {
990 * The Cancel routine for the current IRP is running. The
991 * request is to be completed by the Cancel routine. Leave
992 * this request alone and go to the next one.
997 * The Cancel routine cannot run now and cannot already have
998 * started to run. This request can be processed.
1003 RemoveEntryList(&request->entry);
1004 listRequests->numEntries--;
1009 NdisReleaseSpinLock(&listRequests->spinlock);
1015 * --------------------------------------------------------------------------
1016 * This function pushes the received request to the queue, marks the IRP as
1017 * pending and sets its Cancel routine, while holding the queue lock.
1019 * Returns STATUS_CANCELLED if the IRP has already been cancelled. Otherwise,
1020 * STATUS_SUCCESS is returned.
1021 * --------------------------------------------------------------------------
1024 OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests,
1025 POVS_TUNFLT_REQUEST request)
1027 NTSTATUS status = STATUS_SUCCESS;
1028 PIRP irp = request->irp;
1029 PDRIVER_CANCEL oldCancelRoutine;
1030 BOOLEAN cancelled = FALSE;
1032 NdisAcquireSpinLock(&listRequests->spinlock);
1036 * Mark the IRP pending to indicate that the request may complete on
1037 * a different thread.
1039 IoMarkIrpPending(irp);
1042 * Set the Cancel routine for the pending IRP, before checking the
1045 oldCancelRoutine = IoSetCancelRoutine(irp, OvsTunnelFilterCancelIrp);
1046 ASSERT(oldCancelRoutine == NULL);
1050 * The IRP has already been cancelled.
1051 * Determine wheather the Cancel routine has started to run.
1053 oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
1054 if (oldCancelRoutine) {
1056 * The I/O Manager has not called the Cancel routine and it
1057 * won't be called anymore, because we just set it to NULL.
1058 * Return STATUS_CANCELLED and complete the request after
1059 * releasing the lock.
1061 status = STATUS_CANCELLED;
1065 * The Cancel routine has already started to run, but it is
1066 * blocked while it waits for the queue lock. Release the lock
1067 * and return STATUS_SUCCESS to avoid completing the request.
1068 * It will be completed in the Cancel routine.
1073 * The IRP has not been cancelled, so set its context used in the
1076 OvsTunnelFilterSetIrpContext(listRequests, request);
1081 InsertTailList(&listRequests->head, &(request->entry));
1082 listRequests->numEntries++;
1085 NdisReleaseSpinLock(&listRequests->spinlock);
1091 * --------------------------------------------------------------------------
1092 * This function pushes the received request to the corresponding thread
1093 * request queue. The arrival of the new request is signaled to the thread,
1094 * in order to start processing it.
1097 * If the thread is not initialized, no operation is performed.
1099 * For a uniform distribution of requests to thread queues, a thread index is
1100 * calculated based on the received destination port.
1101 * --------------------------------------------------------------------------
1104 OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request)
1106 NTSTATUS status = STATUS_REQUEST_ABORTED;
1107 UINT32 count = OVS_TUNFLT_MAX_THREADS;
1110 threadIndex = request->port % OVS_TUNFLT_MAX_THREADS;
1113 if (gTunnelThreadCtx[threadIndex].isInitialized) {
1115 status = OvsTunnelFilterRequestPush(
1116 &gTunnelThreadCtx[threadIndex].listRequests,
1119 if (NT_SUCCESS(status)) {
1120 KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent,
1127 OVS_LOG_INFO("OVS tunnel filter thread %d not initialized.",
1131 threadIndex = (threadIndex + 1) % OVS_TUNFLT_MAX_THREADS;
1138 OvsTunnelFilterCompleteRequest(PIRP irp,
1139 PFNTunnelVportPendingOp callback,
1143 UINT32 replyLen = 0;
1146 callback(context, status, &replyLen);
1147 /* Release the context passed to the callback function. */
1148 OvsFreeMemory(context);
1152 OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
1157 OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1159 POVS_TUNFLT_REQUEST request = NULL;
1160 NTSTATUS status = STATUS_SUCCESS;
1161 BOOLEAN inTransaction = FALSE;
1164 if (!InterlockedCompareExchange(
1165 (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) {
1166 OVS_LOG_INFO("Nothing to do... request list is empty.");
1170 status = FwpmTransactionBegin(threadCtx->engineSession, 0);
1171 if (!NT_SUCCESS(status)) {
1172 OVS_LOG_ERROR("Failed to start transaction, status: %x.",
1176 inTransaction = TRUE;
1179 (request = OvsTunnelFilterRequestPop(&threadCtx->listRequests))) {
1181 status = OvsTunnelFilterExecuteAction(threadCtx->engineSession,
1184 /* Complete the IRP with the last operation status. */
1185 OvsTunnelFilterCompleteRequest(request->irp,
1190 OvsFreeMemory(request);
1194 status = FwpmTransactionCommit(threadCtx->engineSession);
1195 if (!NT_SUCCESS(status)) {
1196 OVS_LOG_ERROR("Failed to commit transaction, status: %x.",
1201 inTransaction = FALSE;
1202 } while (inTransaction);
1204 if (inTransaction) {
1205 FwpmTransactionAbort(threadCtx->engineSession);
1206 OVS_LOG_ERROR("Failed to execute request, status: %x.\
1207 Transaction aborted.", status);
1212 *----------------------------------------------------------------------------
1213 * System thread routine that processes thread's requests queue. The thread
1214 * routine initializes thread's necessary data and waits on two events,
1215 * requestEvent and stopEvent. Whenever a request is pushed to the thread's
1216 * queue, the requestEvent is signaled and the thread routine starts processing
1217 * the arrived requests. When stopEvent is signaled, all subsequent requests
1218 * are completed with STATUS_CANCELED, without being added to the thread's
1219 * queue, and the routine finishes processing all existing requests from the
1220 * queue before uninitializing the thread and exiting.
1221 *----------------------------------------------------------------------------
1223 _Use_decl_annotations_
1225 OvsTunnelFilterThreadProc(PVOID context)
1227 NTSTATUS status = STATUS_SUCCESS;
1228 POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context;
1229 PKEVENT eventArray[2] = { 0 };
1231 BOOLEAN exit = FALSE;
1232 BOOLEAN error = TRUE;
1234 OVS_LOG_INFO("Starting OVS Tunnel system thread %d.",
1235 threadCtx->threadID);
1237 eventArray[0] = &threadCtx->stopEvent;
1238 eventArray[1] = &threadCtx->requestEvent;
1239 count = ARRAY_SIZE(eventArray);
1242 status = OvsTunnelFilterThreadInit(threadCtx);
1243 if (!NT_SUCCESS(status)) {
1244 OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.",
1245 threadCtx->threadID);
1250 status = KeWaitForMultipleObjects(count,
1260 /* Start processing requests. */
1261 OvsTunnelFilterRequestListProcess(threadCtx);
1264 /* Finish processing the remaining requests and exit. */
1265 OvsTunnelFilterRequestListProcess(threadCtx);
1271 OvsTunnelFilterThreadUninit(threadCtx);
1276 OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.",
1277 threadCtx->threadID);
1279 PsTerminateSystemThread(STATUS_SUCCESS);
1283 OvsTunnelFilterStartThreads()
1285 NTSTATUS status = STATUS_SUCCESS;
1287 for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1288 gTunnelThreadCtx[index].threadID = index;
1290 status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]);
1291 if (!NT_SUCCESS(status)) {
1292 OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index);
1301 OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1303 NTSTATUS status = STATUS_SUCCESS;
1304 HANDLE threadHandle = NULL;
1305 BOOLEAN error = TRUE;
1308 status = PsCreateSystemThread(&threadHandle,
1313 OvsTunnelFilterThreadProc,
1315 if (!NT_SUCCESS(status)) {
1316 OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.",
1321 ObReferenceObjectByHandle(threadHandle,
1325 &threadCtx->threadObject,
1327 ZwClose(threadHandle);
1328 threadHandle = NULL;
1337 OvsTunnelFilterStopThreads()
1339 /* Signal all threads to stop and ignore all subsequent requests. */
1340 for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1341 OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], TRUE);
1344 /* Wait for all threads to finish processing the requests. */
1345 for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) {
1346 OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], FALSE);
1351 OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx,
1352 BOOLEAN signalEvent)
1354 if (threadCtx->isInitialized) {
1357 /* Signal stop thread event. */
1358 OVS_LOG_INFO("Received stop event for OVS Tunnel system thread %d.",
1359 threadCtx->threadID);
1360 KeSetEvent(&threadCtx->stopEvent, IO_NO_INCREMENT, FALSE);
1362 /* Wait for the tunnel thread to finish. */
1363 KeWaitForSingleObject(threadCtx->threadObject,
1369 ObDereferenceObject(threadCtx->threadObject);
1375 * --------------------------------------------------------------------------
1376 * This function initializes thread's necessary data. Each thread has its own
1377 * session object to the BFE that is used for processing the requests from
1378 * the thread's queue.
1379 * --------------------------------------------------------------------------
1382 OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1384 NTSTATUS status = STATUS_SUCCESS;
1385 BOOLEAN error = TRUE;
1388 /* Create thread's engine session object. */
1389 status = OvsTunnelEngineOpen(&threadCtx->engineSession);
1390 if (!NT_SUCCESS(status)) {
1394 NdisAllocateSpinLock(&threadCtx->listRequests.spinlock);
1396 InitializeListHead(&threadCtx->listRequests.head);
1398 KeInitializeEvent(&threadCtx->stopEvent,
1402 KeInitializeEvent(&threadCtx->requestEvent,
1403 SynchronizationEvent,
1406 threadCtx->isInitialized = TRUE;
1415 * --------------------------------------------------------------------------
1416 * This function uninitializes thread's private data. Thread's engine session
1417 * handle is closed and set to NULL.
1418 * --------------------------------------------------------------------------
1421 OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx)
1423 if (threadCtx->engineSession) {
1424 /* Close thread's FWPM session. */
1425 OvsTunnelEngineClose(&threadCtx->engineSession);
1427 NdisFreeSpinLock(&threadCtx->listRequests.spinlock);
1429 threadCtx->isInitialized = FALSE;
1434 * --------------------------------------------------------------------------
1435 * This function creates a new tunnel filter request and push it to a thread
1436 * queue. If the thread stop event is signaled, the request is completed with
1437 * STATUS_REQUEST_ABORTED without pushing it to any queue.
1438 * --------------------------------------------------------------------------
1441 OvsTunnelFilterQueueRequest(PIRP irp,
1444 OVS_TUNFLT_OPERATION operation,
1445 PFNTunnelVportPendingOp callback,
1446 PVOID tunnelContext)
1448 POVS_TUNFLT_REQUEST request = NULL;
1449 NTSTATUS status = STATUS_PENDING;
1450 NTSTATUS result = STATUS_SUCCESS;
1451 BOOLEAN error = TRUE;
1455 /* Verify if the stop event was signaled. */
1456 if (STATUS_SUCCESS == KeWaitForSingleObject(
1457 &gTunnelThreadCtx[0].stopEvent,
1461 (LARGE_INTEGER *)&timeout)) {
1462 /* The stop event is signaled. Completed the IRP with
1463 * STATUS_REQUEST_ABORTED. */
1464 status = STATUS_REQUEST_ABORTED;
1468 if (NULL == filterID) {
1469 OVS_LOG_ERROR("Invalid request.");
1470 status = STATUS_INVALID_PARAMETER;
1474 request = (POVS_TUNFLT_REQUEST)
1475 OvsAllocateMemoryWithTag(sizeof(*request),
1476 OVS_TUNFLT_POOL_TAG);
1477 if (NULL == request) {
1478 OVS_LOG_ERROR("Failed to allocate list item.");
1479 status = STATUS_INSUFFICIENT_RESOURCES;
1483 request->port = remotePort;
1484 request->operation = operation;
1485 switch (operation) {
1486 case OVS_TUN_FILTER_CREATE:
1487 request->filterID.addID = filterID;
1489 case OVS_TUN_FILTER_DELETE:
1490 request->filterID.delID = *filterID;
1494 request->callback = callback;
1495 request->context = tunnelContext;
1497 result = OvsTunnelFilterThreadPush(request);
1498 if (!NT_SUCCESS(result)) {
1507 OvsTunnelFilterCompleteRequest(irp,
1512 OvsFreeMemory(request);
1521 * --------------------------------------------------------------------------
1522 * This function adds a new WFP filter for the received port and returns the
1523 * ID of the created WFP filter.
1526 * All necessary calls to the WFP filtering engine must be running at IRQL =
1527 * PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1528 * we register an OVS_TUN_FILTER_CREATE request that will be processed by
1529 * the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1531 * OVS VXLAN port add call hierarchy:
1532 * <OvsNewVportCmdHandler>
1533 * <OvsInitTunnelVport>
1534 * <OvsInitVxlanTunnel>
1535 * <OvsTunnelFilterCreate>
1536 * <OvsTunnelFilterQueueRequest>
1537 * --> if thread STOP event is signalled:
1538 * --> Complete request with STATUS_CANCELLED
1540 * <OvsTunnelFilterThreadPush>
1541 * --> add the request to one of tunnel thread queues
1543 * --------------------------------------------------------------------------
1546 OvsTunnelFilterCreate(PIRP irp,
1549 PFNTunnelVportPendingOp callback,
1550 PVOID tunnelContext)
1552 return OvsTunnelFilterQueueRequest(irp,
1555 OVS_TUN_FILTER_CREATE,
1561 * --------------------------------------------------------------------------
1562 * This function removes a WFP filter using the received filter ID.
1565 * All necessary calls to the WFP filtering engine must be running at IRQL =
1566 * PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL,
1567 * we register an OVS_TUN_FILTER_DELETE request that will be processed by
1568 * the tunnel filter thread routine at IRQL = PASSIVE_LEVEL.
1570 * OVS VXLAN port delete call hierarchy:
1571 * <OvsDeleteVportCmdHandler>
1572 * <OvsRemoveAndDeleteVport>
1573 * <OvsCleanupVxlanTunnel>
1574 * <OvsTunnelFilterDelete>
1575 * <OvsTunnelFilterQueueRequest>
1576 * --> if thread STOP event is signalled:
1577 * --> Complete request with STATUS_CANCELLED
1579 * <OvsTunnelFilterThreadPush>
1580 * --> add the request to one of tunnel thread queues
1582 * --------------------------------------------------------------------------
1585 OvsTunnelFilterDelete(PIRP irp,
1587 PFNTunnelVportPendingOp callback,
1588 PVOID tunnelContext)
1590 return OvsTunnelFilterQueueRequest(irp,
1593 OVS_TUN_FILTER_DELETE,
1599 * --------------------------------------------------------------------------
1600 * This function sets the context for the IRP. The context is used by the
1601 * Cancel routine, in order to identify the request object, corresponding to
1602 * the IRP, to be completed and to have access to the queue lock to remove
1603 * the request link from the queue.
1604 * --------------------------------------------------------------------------
1607 OvsTunnelFilterSetIrpContext(POVS_TUNFLT_REQUEST_LIST listRequests,
1608 POVS_TUNFLT_REQUEST request)
1610 PIRP irp = request->irp;
1613 /* Set the IRP's DriverContext to be used for later. */
1614 irp->Tail.Overlay.DriverContext[0] = (PVOID)request;
1615 irp->Tail.Overlay.DriverContext[1] = (PVOID)listRequests;
1620 * --------------------------------------------------------------------------
1621 * This function is the Cancel routine to be called by the I/O Manager in the
1622 * case when the IRP is cancelled.
1623 * --------------------------------------------------------------------------
1626 OvsTunnelFilterCancelIrp(PDEVICE_OBJECT DeviceObject,
1629 POVS_TUNFLT_REQUEST request =
1630 (POVS_TUNFLT_REQUEST)irp->Tail.Overlay.DriverContext[0];
1631 POVS_TUNFLT_REQUEST_LIST listRequests =
1632 (POVS_TUNFLT_REQUEST_LIST)irp->Tail.Overlay.DriverContext[1];
1634 DBG_UNREFERENCED_PARAMETER(DeviceObject);
1636 /* Release the global cancel spinlock. */
1637 IoReleaseCancelSpinLock(irp->CancelIrql);
1639 /* Clear the cancel routine from the IRP. */
1640 IoSetCancelRoutine(irp, NULL);
1642 NdisAcquireSpinLock(&listRequests->spinlock);
1644 /* Remove the request from the corresponding tunnel filter thread queue. */
1645 RemoveEntryList(&request->entry);
1646 listRequests->numEntries--;
1648 NdisReleaseSpinLock(&listRequests->spinlock);
1650 /* We are done with this IRP, so complete it with STATUS_CANCELLED. */
1651 OvsTunnelFilterCompleteRequest(request->irp,
1656 OvsFreeMemory(request);