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
52 * The provider name should always match the provider string from the install
55 #define OVS_TUNNEL_PROVIDER_NAME L"Open vSwitch"
58 * The provider description should always contain the OVS service description
59 * string from the install file.
61 #define OVS_TUNNEL_PROVIDER_DESC L"Open vSwitch Extension tunnel provider"
63 /* The session name isn't required but it's useful for diagnostics. */
64 #define OVS_TUNNEL_SESSION_NAME L"OVS tunnel session"
66 /* Configurable parameters (addresses and ports are in host order) */
67 UINT16 configNewDestPort = VXLAN_UDP_PORT;
70 * Callout and sublayer GUIDs
72 // b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8
74 OVS_TUNNEL_CALLOUT_V4,
78 0x8b, 0x39, 0xbd, 0x3f, 0xfc, 0x85, 0x5f, 0xf8
81 /* 0104fd7e-c825-414e-94c9-f0d525bbc169 */
87 0x94, 0xc9, 0xf0, 0xd5, 0x25, 0xbb, 0xc1, 0x69
90 /* 6fc957d7-14e7-47c7-812b-4668be994ba1 */
92 OVS_TUNNEL_PROVIDER_KEY,
96 0x81, 0x2b, 0x46, 0x68, 0xbe, 0x99, 0x4b, 0xa1
99 /* bfd4814c-9650-4de3-a536-1eedb9e9ba6a */
101 OVS_TUNNEL_FILTER_KEY,
105 0xa5, 0x36, 0x1e, 0xed, 0xb9, 0xe9, 0xba, 0x6a
109 * Callout driver global variables
111 PDEVICE_OBJECT gDeviceObject;
113 HANDLE gEngineHandle = NULL;
114 HANDLE gTunnelProviderBfeHandle = NULL;
115 HANDLE gTunnelInitBfeHandle = NULL;
119 /* Callout driver implementation */
122 OvsTunnelEngineOpen(HANDLE *handle)
124 NTSTATUS status = STATUS_SUCCESS;
125 FWPM_SESSION session = { 0 };
127 /* The session name isn't required but may be useful for diagnostics. */
128 session.displayData.name = OVS_TUNNEL_SESSION_NAME;
130 * Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT
131 * errors while waiting to acquire the transaction lock.
133 session.txnWaitTimeoutInMSec = INFINITE;
134 session.flags = FWPM_SESSION_FLAG_DYNAMIC;
136 /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
137 status = FwpmEngineOpen(NULL,
142 if (!NT_SUCCESS(status)) {
143 OVS_LOG_ERROR("Fail to open filtering engine session, status: %x.",
151 OvsTunnelEngineClose(HANDLE *handle)
154 FwpmEngineClose(*handle);
160 OvsTunnelAddSystemProvider(HANDLE handle)
162 NTSTATUS status = STATUS_SUCCESS;
163 BOOLEAN inTransaction = FALSE;
164 FWPM_PROVIDER provider = { 0 };
167 status = FwpmTransactionBegin(handle, 0);
168 if (!NT_SUCCESS(status)) {
171 inTransaction = TRUE;
173 memset(&provider, 0, sizeof(provider));
174 provider.providerKey = OVS_TUNNEL_PROVIDER_KEY;
175 provider.displayData.name = OVS_TUNNEL_PROVIDER_NAME;
176 provider.displayData.description = OVS_TUNNEL_PROVIDER_DESC;
178 * Since we always want the provider to be present, it's easiest to add
179 * it as persistent object during driver load.
181 provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
183 status = FwpmProviderAdd(handle,
186 if (!NT_SUCCESS(status)) {
187 if (STATUS_FWP_ALREADY_EXISTS != status) {
188 OVS_LOG_ERROR("Failed to add WFP provider, status: %x.",
194 status = FwpmTransactionCommit(handle);
195 if (!NT_SUCCESS(status)) {
199 inTransaction = FALSE;
200 } while (inTransaction);
203 FwpmTransactionAbort(handle);
208 OvsTunnelRemoveSystemProvider(HANDLE handle)
210 NTSTATUS status = STATUS_SUCCESS;
211 BOOLEAN inTransaction = FALSE;
214 status = FwpmTransactionBegin(handle, 0);
215 if (!NT_SUCCESS(status)) {
218 inTransaction = TRUE;
220 status = FwpmProviderDeleteByKey(handle,
221 &OVS_TUNNEL_PROVIDER_KEY);
222 if (!NT_SUCCESS(status)) {
226 status = FwpmTransactionCommit(handle);
227 if (!NT_SUCCESS(status)) {
231 inTransaction = FALSE;
232 } while (inTransaction);
235 FwpmTransactionAbort(handle);
240 OvsTunnelAddFilter(PWSTR filterName,
241 const PWSTR filterDesc,
243 FWP_DIRECTION direction,
245 const GUID *filterKey,
246 const GUID *layerKey,
247 const GUID *calloutKey)
249 NTSTATUS status = STATUS_SUCCESS;
250 FWPM_FILTER filter = {0};
251 FWPM_FILTER_CONDITION filterConditions[3] = {0};
254 UNREFERENCED_PARAMETER(remotePort);
255 UNREFERENCED_PARAMETER(direction);
257 filter.filterKey = *filterKey;
258 filter.layerKey = *layerKey;
259 filter.displayData.name = (wchar_t*)filterName;
260 filter.displayData.description = (wchar_t*)filterDesc;
262 filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
263 filter.action.calloutKey = *calloutKey;
264 filter.filterCondition = filterConditions;
265 filter.subLayerKey = OVS_TUNNEL_SUBLAYER;
266 filter.weight.type = FWP_EMPTY; // auto-weight.
267 filter.rawContext = context;
271 filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_DIRECTION;
272 filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
273 filterConditions[conditionIndex].conditionValue.type = FWP_UINT32;
274 filterConditions[conditionIndex].conditionValue.uint32 = direction;
278 filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
279 filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
280 filterConditions[conditionIndex].conditionValue.type = FWP_UINT16;
281 filterConditions[conditionIndex].conditionValue.uint16 = remotePort;
285 filter.numFilterConditions = conditionIndex;
287 status = FwpmFilterAdd(gEngineHandle,
296 OvsTunnelRemoveFilter(const GUID *filterKey,
297 const GUID *sublayerKey)
299 NTSTATUS status = STATUS_SUCCESS;
300 BOOLEAN inTransaction = FALSE;
303 status = FwpmTransactionBegin(gEngineHandle, 0);
304 if (!NT_SUCCESS(status)) {
308 inTransaction = TRUE;
311 * We have to delete the filter first since it references the
312 * sublayer. If we tried to delete the sublayer first, it would fail
313 * with FWP_ERR_IN_USE.
315 status = FwpmFilterDeleteByKey(gEngineHandle,
317 if (!NT_SUCCESS(status)) {
321 status = FwpmSubLayerDeleteByKey(gEngineHandle,
323 if (!NT_SUCCESS(status)) {
327 status = FwpmTransactionCommit(gEngineHandle);
328 if (!NT_SUCCESS(status)){
332 inTransaction = FALSE;
333 } while (inTransaction);
336 FwpmTransactionAbort(gEngineHandle);
342 * --------------------------------------------------------------------------
343 * This function registers callouts and filters that intercept UDP traffic at
344 * WFP FWPM_LAYER_DATAGRAM_DATA_V4
345 * --------------------------------------------------------------------------
348 OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
349 const GUID *calloutKey,
353 NTSTATUS status = STATUS_SUCCESS;
355 FWPS_CALLOUT sCallout = {0};
356 FWPM_CALLOUT mCallout = {0};
358 FWPM_DISPLAY_DATA displayData = {0};
360 BOOLEAN calloutRegistered = FALSE;
362 sCallout.calloutKey = *calloutKey;
363 sCallout.classifyFn = OvsTunnelClassify;
364 sCallout.notifyFn = OvsTunnelNotify;
366 /* Currently we don't associate a context with the flow */
367 sCallout.flowDeleteFn = OvsTunnelFlowDelete;
368 sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
371 status = FwpsCalloutRegister(deviceObject,
375 if (!NT_SUCCESS(status)) {
378 calloutRegistered = TRUE;
380 displayData.name = L"Datagram-Data OVS Callout";
381 displayData.description = L"Proxies destination address/port for UDP";
383 mCallout.calloutKey = *calloutKey;
384 mCallout.displayData = displayData;
385 mCallout.applicableLayer = *layerKey;
387 status = FwpmCalloutAdd(gEngineHandle,
392 if (!NT_SUCCESS(status)) {
396 status = OvsTunnelAddFilter(L"Datagram-Data OVS Filter (Inbound)",
397 L"address/port for UDP",
399 FWP_DIRECTION_INBOUND,
401 &OVS_TUNNEL_FILTER_KEY,
407 if (!NT_SUCCESS(status)){
408 if (calloutRegistered) {
409 FwpsCalloutUnregisterById(*calloutId);
418 * --------------------------------------------------------------------------
419 * This function registers dynamic callouts and filters that intercept UDP
420 * Callouts and filters will be removed during De-Initialize.
421 * --------------------------------------------------------------------------
424 OvsTunnelRegisterCallouts(VOID *deviceObject)
426 NTSTATUS status = STATUS_SUCCESS;
427 FWPM_SUBLAYER OvsTunnelSubLayer;
429 BOOLEAN engineOpened = FALSE;
430 BOOLEAN inTransaction = FALSE;
432 status = OvsTunnelEngineOpen(&gEngineHandle);
433 if (!NT_SUCCESS(status)) {
438 status = FwpmTransactionBegin(gEngineHandle, 0);
439 if (!NT_SUCCESS(status)) {
442 inTransaction = TRUE;
444 RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER));
446 OvsTunnelSubLayer.subLayerKey = OVS_TUNNEL_SUBLAYER;
447 OvsTunnelSubLayer.displayData.name = L"Datagram-Data OVS Sub-Layer";
448 OvsTunnelSubLayer.displayData.description =
449 L"Sub-Layer for use by Datagram-Data OVS callouts";
450 OvsTunnelSubLayer.flags = 0;
451 OvsTunnelSubLayer.weight = FWP_EMPTY; /* auto-weight */
453 status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL);
454 if (!NT_SUCCESS(status)) {
458 /* In order to use this callout a socket must be opened. */
459 status = OvsTunnelRegisterDatagramDataCallouts(&FWPM_LAYER_DATAGRAM_DATA_V4,
460 &OVS_TUNNEL_CALLOUT_V4,
463 if (!NT_SUCCESS(status)) {
467 status = FwpmTransactionCommit(gEngineHandle);
468 if (!NT_SUCCESS(status)){
471 inTransaction = FALSE;
475 if (!NT_SUCCESS(status)) {
477 FwpmTransactionAbort(gEngineHandle);
480 OvsTunnelEngineClose(&gEngineHandle);
488 OvsTunnelUnregisterCallouts(VOID)
490 OvsTunnelRemoveFilter(&OVS_TUNNEL_FILTER_KEY,
491 &OVS_TUNNEL_SUBLAYER);
492 FwpsCalloutUnregisterById(gCalloutIdV4);
493 FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
494 OvsTunnelEngineClose(&gEngineHandle);
498 OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
500 UNREFERENCED_PARAMETER(driverObject);
502 OvsTunnelUnregisterCallouts();
503 IoDeleteDevice(gDeviceObject);
508 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
510 NTSTATUS status = STATUS_SUCCESS;
511 UNICODE_STRING deviceName;
513 RtlInitUnicodeString(&deviceName,
514 L"\\Device\\OvsTunnelFilter");
516 status = IoCreateDevice(driverObject,
524 if (!NT_SUCCESS(status)){
528 status = OvsTunnelRegisterCallouts(gDeviceObject);
532 if (!NT_SUCCESS(status)){
533 if (gEngineHandle != NULL) {
534 OvsTunnelUnregisterCallouts();
538 IoDeleteDevice(gDeviceObject);
546 OvsTunnelProviderBfeCallback(PVOID context,
547 FWPM_SERVICE_STATE bfeState)
549 HANDLE handle = NULL;
551 DBG_UNREFERENCED_PARAMETER(context);
553 if (FWPM_SERVICE_RUNNING == bfeState) {
554 OvsTunnelEngineOpen(&handle);
556 OvsTunnelAddSystemProvider(handle);
558 OvsTunnelEngineClose(&handle);
563 OvsSubscribeTunnelProviderBfeStateChanges(PVOID deviceObject)
565 NTSTATUS status = STATUS_SUCCESS;
567 if (!gTunnelProviderBfeHandle) {
568 status = FwpmBfeStateSubscribeChanges(deviceObject,
569 OvsTunnelProviderBfeCallback,
571 &gTunnelProviderBfeHandle);
572 if (!NT_SUCCESS(status)) {
574 "Failed to subscribe BFE tunnel provider callback, status: %x.",
583 OvsUnsubscribeTunnelProviderBfeStateChanges()
585 NTSTATUS status = STATUS_SUCCESS;
587 if (gTunnelProviderBfeHandle) {
588 status = FwpmBfeStateUnsubscribeChanges(gTunnelProviderBfeHandle);
589 if (!NT_SUCCESS(status)) {
591 "Failed to unsubscribe BFE tunnel provider callback, status: %x.",
594 gTunnelProviderBfeHandle = NULL;
599 OvsRegisterSystemProvider(PVOID deviceObject)
601 NTSTATUS status = STATUS_SUCCESS;
602 HANDLE handle = NULL;
604 status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject);
605 if (NT_SUCCESS(status)) {
606 if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
607 OvsTunnelEngineOpen(&handle);
609 OvsTunnelAddSystemProvider(handle);
611 OvsTunnelEngineClose(&handle);
613 OvsUnsubscribeTunnelProviderBfeStateChanges();
618 VOID OvsUnregisterSystemProvider()
620 HANDLE handle = NULL;
622 OvsTunnelEngineOpen(&handle);
624 OvsTunnelRemoveSystemProvider(handle);
626 OvsTunnelEngineClose(&handle);
628 OvsUnsubscribeTunnelProviderBfeStateChanges();
632 OvsTunnelInitBfeCallback(PVOID context,
633 FWPM_SERVICE_STATE bfeState)
635 NTSTATUS status = STATUS_SUCCESS;
636 PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) context;
638 if (FWPM_SERVICE_RUNNING == bfeState) {
639 status = OvsTunnelFilterInitialize(driverObject);
640 if (!NT_SUCCESS(status)) {
642 "Failed to initialize tunnel filter, status: %x.",
649 OvsSubscribeTunnelInitBfeStateChanges(PDRIVER_OBJECT driverObject,
652 NTSTATUS status = STATUS_SUCCESS;
654 if (!gTunnelInitBfeHandle) {
655 status = FwpmBfeStateSubscribeChanges(deviceObject,
656 OvsTunnelInitBfeCallback,
658 &gTunnelInitBfeHandle);
659 if (!NT_SUCCESS(status)) {
661 "Failed to subscribe BFE tunnel init callback, status: %x.",
670 OvsUnsubscribeTunnelInitBfeStateChanges()
672 NTSTATUS status = STATUS_SUCCESS;
674 if (gTunnelInitBfeHandle) {
675 status = FwpmBfeStateUnsubscribeChanges(gTunnelInitBfeHandle);
676 if (!NT_SUCCESS(status)) {
678 "Failed to unsubscribe BFE tunnel init callback, status: %x.",
681 gTunnelInitBfeHandle = NULL;
686 OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject)
688 NTSTATUS status = STATUS_SUCCESS;
690 status = OvsSubscribeTunnelInitBfeStateChanges(driverObject, deviceObject);
691 if (NT_SUCCESS(status)) {
692 if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
693 status = OvsTunnelFilterInitialize(driverObject);
694 if (!NT_SUCCESS(status)) {
695 /* XXX: We need to decide what actions to take in case of
696 * failure to initialize tunnel filter. */
697 ASSERT(status == NDIS_STATUS_SUCCESS);
699 "Failed to initialize tunnel filter, status: %x.",
702 OvsUnsubscribeTunnelInitBfeStateChanges();
709 VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject)
711 OvsTunnelFilterUninitialize(driverObject);
712 OvsUnsubscribeTunnelInitBfeStateChanges();