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 gBfeSubscriptionHandle = NULL;
118 /* Callout driver implementation */
121 OvsTunnelEngineOpen(HANDLE *handle)
123 NTSTATUS status = STATUS_SUCCESS;
124 FWPM_SESSION session = { 0 };
126 /* The session name isn't required but may be useful for diagnostics. */
127 session.displayData.name = OVS_TUNNEL_SESSION_NAME;
129 * Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT
130 * errors while waiting to acquire the transaction lock.
132 session.txnWaitTimeoutInMSec = INFINITE;
133 session.flags = FWPM_SESSION_FLAG_DYNAMIC;
135 /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
136 status = FwpmEngineOpen(NULL,
141 if (!NT_SUCCESS(status)) {
142 OVS_LOG_ERROR("Fail to open filtering engine session, status: %x.",
150 OvsTunnelEngineClose(HANDLE *handle)
153 FwpmEngineClose(*handle);
159 OvsTunnelAddSystemProvider(HANDLE handle)
161 NTSTATUS status = STATUS_SUCCESS;
162 BOOLEAN inTransaction = FALSE;
163 FWPM_PROVIDER provider = { 0 };
166 status = FwpmTransactionBegin(handle, 0);
167 if (!NT_SUCCESS(status)) {
170 inTransaction = TRUE;
172 memset(&provider, 0, sizeof(provider));
173 provider.providerKey = OVS_TUNNEL_PROVIDER_KEY;
174 provider.displayData.name = OVS_TUNNEL_PROVIDER_NAME;
175 provider.displayData.description = OVS_TUNNEL_PROVIDER_DESC;
177 * Since we always want the provider to be present, it's easiest to add
178 * it as persistent object during driver load.
180 provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
182 status = FwpmProviderAdd(handle,
185 if (!NT_SUCCESS(status)) {
186 if (STATUS_FWP_ALREADY_EXISTS != status) {
187 OVS_LOG_ERROR("Failed to add WFP provider, status: %x.",
193 status = FwpmTransactionCommit(handle);
194 if (!NT_SUCCESS(status)) {
198 inTransaction = FALSE;
199 } while (inTransaction);
202 FwpmTransactionAbort(handle);
207 OvsTunnelRemoveSystemProvider(HANDLE handle)
209 NTSTATUS status = STATUS_SUCCESS;
210 BOOLEAN inTransaction = FALSE;
213 status = FwpmTransactionBegin(handle, 0);
214 if (!NT_SUCCESS(status)) {
217 inTransaction = TRUE;
219 status = FwpmProviderDeleteByKey(handle,
220 &OVS_TUNNEL_PROVIDER_KEY);
221 if (!NT_SUCCESS(status)) {
225 status = FwpmTransactionCommit(handle);
226 if (!NT_SUCCESS(status)) {
230 inTransaction = FALSE;
231 } while (inTransaction);
234 FwpmTransactionAbort(handle);
239 OvsTunnelAddFilter(PWSTR filterName,
240 const PWSTR filterDesc,
242 FWP_DIRECTION direction,
244 const GUID *filterKey,
245 const GUID *layerKey,
246 const GUID *calloutKey)
248 NTSTATUS status = STATUS_SUCCESS;
249 FWPM_FILTER filter = {0};
250 FWPM_FILTER_CONDITION filterConditions[3] = {0};
253 UNREFERENCED_PARAMETER(remotePort);
254 UNREFERENCED_PARAMETER(direction);
256 filter.filterKey = *filterKey;
257 filter.layerKey = *layerKey;
258 filter.displayData.name = (wchar_t*)filterName;
259 filter.displayData.description = (wchar_t*)filterDesc;
261 filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
262 filter.action.calloutKey = *calloutKey;
263 filter.filterCondition = filterConditions;
264 filter.subLayerKey = OVS_TUNNEL_SUBLAYER;
265 filter.weight.type = FWP_EMPTY; // auto-weight.
266 filter.rawContext = context;
270 filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_DIRECTION;
271 filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
272 filterConditions[conditionIndex].conditionValue.type = FWP_UINT32;
273 filterConditions[conditionIndex].conditionValue.uint32 = direction;
277 filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
278 filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
279 filterConditions[conditionIndex].conditionValue.type = FWP_UINT16;
280 filterConditions[conditionIndex].conditionValue.uint16 = remotePort;
284 filter.numFilterConditions = conditionIndex;
286 status = FwpmFilterAdd(gEngineHandle,
295 OvsTunnelRemoveFilter(const GUID *filterKey,
296 const GUID *sublayerKey)
298 NTSTATUS status = STATUS_SUCCESS;
299 BOOLEAN inTransaction = FALSE;
302 status = FwpmTransactionBegin(gEngineHandle, 0);
303 if (!NT_SUCCESS(status)) {
307 inTransaction = TRUE;
310 * We have to delete the filter first since it references the
311 * sublayer. If we tried to delete the sublayer first, it would fail
312 * with FWP_ERR_IN_USE.
314 status = FwpmFilterDeleteByKey(gEngineHandle,
316 if (!NT_SUCCESS(status)) {
320 status = FwpmSubLayerDeleteByKey(gEngineHandle,
322 if (!NT_SUCCESS(status)) {
326 status = FwpmTransactionCommit(gEngineHandle);
327 if (!NT_SUCCESS(status)){
331 inTransaction = FALSE;
332 } while (inTransaction);
335 FwpmTransactionAbort(gEngineHandle);
341 * --------------------------------------------------------------------------
342 * This function registers callouts and filters that intercept UDP traffic at
343 * WFP FWPM_LAYER_DATAGRAM_DATA_V4
344 * --------------------------------------------------------------------------
347 OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
348 const GUID *calloutKey,
352 NTSTATUS status = STATUS_SUCCESS;
354 FWPS_CALLOUT sCallout = {0};
355 FWPM_CALLOUT mCallout = {0};
357 FWPM_DISPLAY_DATA displayData = {0};
359 BOOLEAN calloutRegistered = FALSE;
361 sCallout.calloutKey = *calloutKey;
362 sCallout.classifyFn = OvsTunnelClassify;
363 sCallout.notifyFn = OvsTunnelNotify;
365 /* Currently we don't associate a context with the flow */
366 sCallout.flowDeleteFn = OvsTunnelFlowDelete;
367 sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
370 status = FwpsCalloutRegister(deviceObject,
374 if (!NT_SUCCESS(status)) {
377 calloutRegistered = TRUE;
379 displayData.name = L"Datagram-Data OVS Callout";
380 displayData.description = L"Proxies destination address/port for UDP";
382 mCallout.calloutKey = *calloutKey;
383 mCallout.displayData = displayData;
384 mCallout.applicableLayer = *layerKey;
386 status = FwpmCalloutAdd(gEngineHandle,
391 if (!NT_SUCCESS(status)) {
395 status = OvsTunnelAddFilter(L"Datagram-Data OVS Filter (Inbound)",
396 L"address/port for UDP",
398 FWP_DIRECTION_INBOUND,
400 &OVS_TUNNEL_FILTER_KEY,
406 if (!NT_SUCCESS(status)){
407 if (calloutRegistered) {
408 FwpsCalloutUnregisterById(*calloutId);
417 * --------------------------------------------------------------------------
418 * This function registers dynamic callouts and filters that intercept UDP
419 * Callouts and filters will be removed during De-Initialize.
420 * --------------------------------------------------------------------------
423 OvsTunnelRegisterCallouts(VOID *deviceObject)
425 NTSTATUS status = STATUS_SUCCESS;
426 FWPM_SUBLAYER OvsTunnelSubLayer;
428 BOOLEAN engineOpened = FALSE;
429 BOOLEAN inTransaction = FALSE;
431 status = OvsTunnelEngineOpen(&gEngineHandle);
432 if (!NT_SUCCESS(status)) {
437 status = FwpmTransactionBegin(gEngineHandle, 0);
438 if (!NT_SUCCESS(status)) {
441 inTransaction = TRUE;
443 RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER));
445 OvsTunnelSubLayer.subLayerKey = OVS_TUNNEL_SUBLAYER;
446 OvsTunnelSubLayer.displayData.name = L"Datagram-Data OVS Sub-Layer";
447 OvsTunnelSubLayer.displayData.description =
448 L"Sub-Layer for use by Datagram-Data OVS callouts";
449 OvsTunnelSubLayer.flags = 0;
450 OvsTunnelSubLayer.weight = FWP_EMPTY; /* auto-weight */
452 * Link all objects to the tunnel provider. When multiple providers are
453 * installed on a computer, this makes it easy to determine who added what.
455 OvsTunnelSubLayer.providerKey = (GUID*) &OVS_TUNNEL_PROVIDER_KEY;
457 status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL);
458 if (!NT_SUCCESS(status)) {
462 /* In order to use this callout a socket must be opened. */
463 status = OvsTunnelRegisterDatagramDataCallouts(&FWPM_LAYER_DATAGRAM_DATA_V4,
464 &OVS_TUNNEL_CALLOUT_V4,
467 if (!NT_SUCCESS(status)) {
471 status = FwpmTransactionCommit(gEngineHandle);
472 if (!NT_SUCCESS(status)){
475 inTransaction = FALSE;
479 if (!NT_SUCCESS(status)) {
481 FwpmTransactionAbort(gEngineHandle);
484 OvsTunnelEngineClose(&gEngineHandle);
492 OvsTunnelUnregisterCallouts(VOID)
494 OvsTunnelRemoveFilter(&OVS_TUNNEL_FILTER_KEY,
495 &OVS_TUNNEL_SUBLAYER);
496 FwpsCalloutUnregisterById(gCalloutIdV4);
497 FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
498 OvsTunnelEngineClose(&gEngineHandle);
502 OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
504 UNREFERENCED_PARAMETER(driverObject);
506 OvsTunnelUnregisterCallouts();
507 IoDeleteDevice(gDeviceObject);
512 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
514 NTSTATUS status = STATUS_SUCCESS;
515 UNICODE_STRING deviceName;
517 RtlInitUnicodeString(&deviceName,
518 L"\\Device\\OvsTunnelFilter");
520 status = IoCreateDevice(driverObject,
528 if (!NT_SUCCESS(status)){
532 status = OvsTunnelRegisterCallouts(gDeviceObject);
536 if (!NT_SUCCESS(status)){
537 if (gEngineHandle != NULL) {
538 OvsTunnelUnregisterCallouts();
542 IoDeleteDevice(gDeviceObject);
550 OvsBfeStateChangeCallback(PVOID context,
551 FWPM_SERVICE_STATE bfeState)
553 HANDLE handle = NULL;
555 DBG_UNREFERENCED_PARAMETER(context);
557 if (FWPM_SERVICE_RUNNING == bfeState) {
558 OvsTunnelEngineOpen(&handle);
560 OvsTunnelAddSystemProvider(handle);
562 OvsTunnelEngineClose(&handle);
567 OvsSubscribeBfeStateChanges(PVOID deviceObject)
569 NTSTATUS status = STATUS_SUCCESS;
571 if (!gBfeSubscriptionHandle) {
572 status = FwpmBfeStateSubscribeChanges(deviceObject,
573 OvsBfeStateChangeCallback,
575 &gBfeSubscriptionHandle);
576 if (!NT_SUCCESS(status)) {
578 "Failed to open subscribe BFE state change callback, status: %x.",
587 OvsUnsubscribeBfeStateChanges()
589 NTSTATUS status = STATUS_SUCCESS;
591 if (gBfeSubscriptionHandle) {
592 status = FwpmBfeStateUnsubscribeChanges(gBfeSubscriptionHandle);
593 if (!NT_SUCCESS(status)) {
595 "Failed to open unsubscribe BFE state change callback, status: %x.",
598 gBfeSubscriptionHandle = NULL;
602 VOID OvsRegisterSystemProvider(PVOID deviceObject)
604 NTSTATUS status = STATUS_SUCCESS;
605 HANDLE handle = NULL;
607 status = OvsSubscribeBfeStateChanges(deviceObject);
608 if (NT_SUCCESS(status)) {
609 if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
610 OvsTunnelEngineOpen(&handle);
612 OvsTunnelAddSystemProvider(handle);
614 OvsTunnelEngineClose(&handle);
616 OvsUnsubscribeBfeStateChanges();
621 VOID OvsUnregisterSystemProvider()
623 HANDLE handle = NULL;
625 OvsTunnelEngineOpen(&handle);
627 OvsTunnelRemoveSystemProvider(handle);
629 OvsTunnelEngineClose(&handle);
631 OvsUnsubscribeBfeStateChanges();