datapath-windows: Removed gOvsCtrlLock global spinlock
[cascardo/ovs.git] / datapath-windows / ovsext / TunnelFilter.c
1 /*
2  * Copyright (c) 2014 VMware, Inc.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "precomp.h"
18
19 #pragma warning(push)
20 #pragma warning(disable:4201)       // unnamed struct/union
21 #ifdef OVS_DBG_MOD
22 #undef OVS_DBG_MOD
23 #endif
24 #define OVS_DBG_MOD OVS_DBG_TUNFLT
25 #include "Debug.h"
26
27
28 #include <fwpsk.h>
29
30 #pragma warning(pop)
31
32 #include <fwpmk.h>
33 #include <ws2ipdef.h>
34 #include <in6addr.h>
35 #include <ip2string.h>
36
37 #include "Tunnel.h"
38 #include "Switch.h"
39 #include "Vport.h"
40 #include "Event.h"
41 #include "User.h"
42 #include "Vxlan.h"
43
44
45 #define INITGUID
46 #include <guiddef.h>
47
48 /* Infinite timeout */
49 #define INFINITE                        0xFFFFFFFF
50
51 /*
52  * The provider name should always match the provider string from the install
53  * file.
54  */
55 #define OVS_TUNNEL_PROVIDER_NAME        L"Open vSwitch"
56
57 /*
58  * The provider description should always contain the OVS service description
59  * string from the install file.
60  */
61 #define OVS_TUNNEL_PROVIDER_DESC        L"Open vSwitch Extension tunnel provider"
62
63 /* The session name isn't required but it's useful for diagnostics. */
64 #define OVS_TUNNEL_SESSION_NAME         L"OVS tunnel session"
65
66 /* Configurable parameters (addresses and ports are in host order) */
67 UINT16   configNewDestPort = VXLAN_UDP_PORT;
68
69 /*
70  * Callout and sublayer GUIDs
71  */
72 // b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8
73 DEFINE_GUID(
74     OVS_TUNNEL_CALLOUT_V4,
75     0xb16b0a6e,
76     0x2b2a,
77     0x41a3,
78     0x8b, 0x39, 0xbd, 0x3f, 0xfc, 0x85, 0x5f, 0xf8
79     );
80
81 /* 0104fd7e-c825-414e-94c9-f0d525bbc169 */
82 DEFINE_GUID(
83     OVS_TUNNEL_SUBLAYER,
84     0x0104fd7e,
85     0xc825,
86     0x414e,
87     0x94, 0xc9, 0xf0, 0xd5, 0x25, 0xbb, 0xc1, 0x69
88     );
89
90 /* 6fc957d7-14e7-47c7-812b-4668be994ba1 */
91 DEFINE_GUID(
92     OVS_TUNNEL_PROVIDER_KEY,
93     0x6fc957d7,
94     0x14e7,
95     0x47c7,
96     0x81, 0x2b, 0x46, 0x68, 0xbe, 0x99, 0x4b, 0xa1
97     );
98
99 /* bfd4814c-9650-4de3-a536-1eedb9e9ba6a */
100 DEFINE_GUID(
101     OVS_TUNNEL_FILTER_KEY,
102     0xbfd4814c,
103     0x9650,
104     0x4de3,
105     0xa5, 0x36, 0x1e, 0xed, 0xb9, 0xe9, 0xba, 0x6a
106     );
107
108 /*
109  * Callout driver global variables
110  */
111 PDEVICE_OBJECT gDeviceObject;
112
113 HANDLE gEngineHandle = NULL;
114 HANDLE gBfeSubscriptionHandle = NULL;
115 UINT32 gCalloutIdV4;
116
117
118 /* Callout driver implementation */
119
120 NTSTATUS
121 OvsTunnelEngineOpen(HANDLE *handle)
122 {
123     NTSTATUS status = STATUS_SUCCESS;
124     FWPM_SESSION session = { 0 };
125
126     /* The session name isn't required but may be useful for diagnostics. */
127     session.displayData.name = OVS_TUNNEL_SESSION_NAME;
128     /*
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.
131     */
132     session.txnWaitTimeoutInMSec = INFINITE;
133     session.flags = FWPM_SESSION_FLAG_DYNAMIC;
134
135     /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */
136     status = FwpmEngineOpen(NULL,
137                             RPC_C_AUTHN_DEFAULT,
138                             NULL,
139                             &session,
140                             handle);
141     if (!NT_SUCCESS(status)) {
142         OVS_LOG_ERROR("Fail to open filtering engine session, status: %x.",
143                       status);
144     }
145
146     return status;
147 }
148
149 VOID
150 OvsTunnelEngineClose(HANDLE *handle)
151 {
152     if (*handle) {
153         FwpmEngineClose(*handle);
154         *handle = NULL;
155     }
156 }
157
158 VOID
159 OvsTunnelAddSystemProvider(HANDLE handle)
160 {
161     NTSTATUS status = STATUS_SUCCESS;
162     BOOLEAN inTransaction = FALSE;
163     FWPM_PROVIDER provider = { 0 };
164
165     do {
166         status = FwpmTransactionBegin(handle, 0);
167         if (!NT_SUCCESS(status)) {
168             break;
169         }
170         inTransaction = TRUE;
171
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;
176         /*
177          * Since we always want the provider to be present, it's easiest to add
178          * it as persistent object during driver load.
179          */
180         provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT;
181
182         status = FwpmProviderAdd(handle,
183                                  &provider,
184                                  NULL);
185         if (!NT_SUCCESS(status)) {
186             if (STATUS_FWP_ALREADY_EXISTS != status) {
187                 OVS_LOG_ERROR("Failed to add WFP provider, status: %x.",
188                               status);
189                 break;
190             }
191         }
192
193         status = FwpmTransactionCommit(handle);
194         if (!NT_SUCCESS(status)) {
195             break;
196         }
197
198         inTransaction = FALSE;
199     } while (inTransaction);
200
201     if (inTransaction){
202         FwpmTransactionAbort(handle);
203     }
204 }
205
206 VOID
207 OvsTunnelRemoveSystemProvider(HANDLE handle)
208 {
209     NTSTATUS status = STATUS_SUCCESS;
210     BOOLEAN inTransaction = FALSE;
211
212     do {
213         status = FwpmTransactionBegin(handle, 0);
214         if (!NT_SUCCESS(status)) {
215             break;
216         }
217         inTransaction = TRUE;
218
219         status = FwpmProviderDeleteByKey(handle,
220                                          &OVS_TUNNEL_PROVIDER_KEY);
221         if (!NT_SUCCESS(status)) {
222             break;
223         }
224
225         status = FwpmTransactionCommit(handle);
226         if (!NT_SUCCESS(status)) {
227             break;
228         }
229
230         inTransaction = FALSE;
231     } while (inTransaction);
232
233     if (inTransaction){
234         FwpmTransactionAbort(handle);
235     }
236 }
237
238 NTSTATUS
239 OvsTunnelAddFilter(PWSTR filterName,
240                    const PWSTR filterDesc,
241                    USHORT remotePort,
242                    FWP_DIRECTION direction,
243                    UINT64 context,
244                    const GUID *filterKey,
245                    const GUID *layerKey,
246                    const GUID *calloutKey)
247 {
248     NTSTATUS status = STATUS_SUCCESS;
249     FWPM_FILTER filter = {0};
250     FWPM_FILTER_CONDITION filterConditions[3] = {0};
251     UINT conditionIndex;
252
253     UNREFERENCED_PARAMETER(remotePort);
254     UNREFERENCED_PARAMETER(direction);
255
256     filter.filterKey = *filterKey;
257     filter.layerKey = *layerKey;
258     filter.displayData.name = (wchar_t*)filterName;
259     filter.displayData.description = (wchar_t*)filterDesc;
260
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;
267
268     conditionIndex = 0;
269
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;
274
275     conditionIndex++;
276
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;
281
282     conditionIndex++;
283
284     filter.numFilterConditions = conditionIndex;
285
286     status = FwpmFilterAdd(gEngineHandle,
287                            &filter,
288                            NULL,
289                            NULL);
290
291     return status;
292 }
293
294 NTSTATUS
295 OvsTunnelRemoveFilter(const GUID *filterKey,
296                       const GUID *sublayerKey)
297 {
298     NTSTATUS status = STATUS_SUCCESS;
299     BOOLEAN inTransaction = FALSE;
300
301     do {
302         status = FwpmTransactionBegin(gEngineHandle, 0);
303         if (!NT_SUCCESS(status)) {
304             break;
305         }
306
307         inTransaction = TRUE;
308
309         /*
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.
313          */
314         status = FwpmFilterDeleteByKey(gEngineHandle,
315                                        filterKey);
316         if (!NT_SUCCESS(status)) {
317             break;
318         }
319
320         status = FwpmSubLayerDeleteByKey(gEngineHandle,
321                                          sublayerKey);
322         if (!NT_SUCCESS(status)) {
323             break;
324         }
325
326         status = FwpmTransactionCommit(gEngineHandle);
327         if (!NT_SUCCESS(status)){
328             break;
329         }
330
331         inTransaction = FALSE;
332     } while (inTransaction);
333
334     if (inTransaction) {
335         FwpmTransactionAbort(gEngineHandle);
336     }
337     return status;
338 }
339
340 /*
341  * --------------------------------------------------------------------------
342  * This function registers callouts and filters that intercept UDP traffic at
343  * WFP FWPM_LAYER_DATAGRAM_DATA_V4
344  * --------------------------------------------------------------------------
345  */
346 NTSTATUS
347 OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey,
348                                       const GUID *calloutKey,
349                                       VOID *deviceObject,
350                                       UINT32 *calloutId)
351 {
352     NTSTATUS status = STATUS_SUCCESS;
353
354     FWPS_CALLOUT sCallout = {0};
355     FWPM_CALLOUT mCallout = {0};
356
357     FWPM_DISPLAY_DATA displayData = {0};
358
359     BOOLEAN calloutRegistered = FALSE;
360
361     sCallout.calloutKey = *calloutKey;
362     sCallout.classifyFn = OvsTunnelClassify;
363     sCallout.notifyFn = OvsTunnelNotify;
364 #if FLOW_CONTEXT
365     /* Currently we don't associate a context with the flow */
366     sCallout.flowDeleteFn = OvsTunnelFlowDelete;
367     sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
368 #endif
369
370     status = FwpsCalloutRegister(deviceObject,
371                                  &sCallout,
372                                  calloutId);
373
374     if (!NT_SUCCESS(status)) {
375         goto Exit;
376     }
377     calloutRegistered = TRUE;
378
379     displayData.name = L"Datagram-Data OVS Callout";
380     displayData.description = L"Proxies destination address/port for UDP";
381
382     mCallout.calloutKey = *calloutKey;
383     mCallout.displayData = displayData;
384     mCallout.applicableLayer = *layerKey;
385
386     status = FwpmCalloutAdd(gEngineHandle,
387                             &mCallout,
388                             NULL,
389                             NULL);
390
391     if (!NT_SUCCESS(status)) {
392         goto Exit;
393     }
394
395     status = OvsTunnelAddFilter(L"Datagram-Data OVS Filter (Inbound)",
396                                 L"address/port for UDP",
397                                 configNewDestPort,
398                                 FWP_DIRECTION_INBOUND,
399                                 0,
400                                 &OVS_TUNNEL_FILTER_KEY,
401                                 layerKey,
402                                 calloutKey);
403
404 Exit:
405
406     if (!NT_SUCCESS(status)){
407         if (calloutRegistered) {
408             FwpsCalloutUnregisterById(*calloutId);
409             *calloutId = 0;
410         }
411     }
412
413     return status;
414 }
415
416 /*
417  * --------------------------------------------------------------------------
418  * This function registers dynamic callouts and filters that intercept UDP
419  * Callouts and filters will be removed during De-Initialize.
420  * --------------------------------------------------------------------------
421  */
422 NTSTATUS
423 OvsTunnelRegisterCallouts(VOID *deviceObject)
424 {
425     NTSTATUS status = STATUS_SUCCESS;
426     FWPM_SUBLAYER OvsTunnelSubLayer;
427
428     BOOLEAN engineOpened = FALSE;
429     BOOLEAN inTransaction = FALSE;
430
431     status = OvsTunnelEngineOpen(&gEngineHandle);
432     if (!NT_SUCCESS(status)) {
433         goto Exit;
434     }
435     engineOpened = TRUE;
436
437     status = FwpmTransactionBegin(gEngineHandle, 0);
438     if (!NT_SUCCESS(status)) {
439         goto Exit;
440     }
441     inTransaction = TRUE;
442
443     RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER));
444
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 */
451     /*
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.
454      */
455     OvsTunnelSubLayer.providerKey = (GUID*) &OVS_TUNNEL_PROVIDER_KEY;
456
457     status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL);
458     if (!NT_SUCCESS(status)) {
459         goto Exit;
460     }
461
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,
465                                                    deviceObject,
466                                                    &gCalloutIdV4);
467     if (!NT_SUCCESS(status)) {
468         goto Exit;
469     }
470
471     status = FwpmTransactionCommit(gEngineHandle);
472     if (!NT_SUCCESS(status)){
473         goto Exit;
474     }
475     inTransaction = FALSE;
476
477 Exit:
478
479     if (!NT_SUCCESS(status)) {
480         if (inTransaction) {
481             FwpmTransactionAbort(gEngineHandle);
482         }
483         if (engineOpened) {
484             OvsTunnelEngineClose(&gEngineHandle);
485         }
486     }
487
488     return status;
489 }
490
491 VOID
492 OvsTunnelUnregisterCallouts(VOID)
493 {
494     OvsTunnelRemoveFilter(&OVS_TUNNEL_FILTER_KEY,
495                           &OVS_TUNNEL_SUBLAYER);
496     FwpsCalloutUnregisterById(gCalloutIdV4);
497     FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4);
498     OvsTunnelEngineClose(&gEngineHandle);
499 }
500
501 VOID
502 OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject)
503 {
504     UNREFERENCED_PARAMETER(driverObject);
505
506     OvsTunnelUnregisterCallouts();
507     IoDeleteDevice(gDeviceObject);
508 }
509
510
511 NTSTATUS
512 OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject)
513 {
514     NTSTATUS status = STATUS_SUCCESS;
515     UNICODE_STRING deviceName;
516
517     RtlInitUnicodeString(&deviceName,
518                          L"\\Device\\OvsTunnelFilter");
519
520     status = IoCreateDevice(driverObject,
521                             0,
522                             &deviceName,
523                             FILE_DEVICE_NETWORK,
524                             0,
525                             FALSE,
526                             &gDeviceObject);
527
528     if (!NT_SUCCESS(status)){
529         goto Exit;
530     }
531
532     status = OvsTunnelRegisterCallouts(gDeviceObject);
533
534 Exit:
535
536     if (!NT_SUCCESS(status)){
537         if (gEngineHandle != NULL) {
538             OvsTunnelUnregisterCallouts();
539         }
540
541         if (gDeviceObject) {
542             IoDeleteDevice(gDeviceObject);
543         }
544     }
545
546     return status;
547 }
548
549 VOID NTAPI
550 OvsBfeStateChangeCallback(PVOID context,
551                           FWPM_SERVICE_STATE bfeState)
552 {
553     HANDLE handle = NULL;
554
555     DBG_UNREFERENCED_PARAMETER(context);
556
557     if (FWPM_SERVICE_RUNNING == bfeState) {
558         OvsTunnelEngineOpen(&handle);
559         if (handle) {
560             OvsTunnelAddSystemProvider(handle);
561         }
562         OvsTunnelEngineClose(&handle);
563     }
564 }
565
566 NTSTATUS
567 OvsSubscribeBfeStateChanges(PVOID deviceObject)
568 {
569     NTSTATUS status = STATUS_SUCCESS;
570
571     if (!gBfeSubscriptionHandle) {
572         status = FwpmBfeStateSubscribeChanges(deviceObject,
573                                               OvsBfeStateChangeCallback,
574                                               NULL,
575                                               &gBfeSubscriptionHandle);
576         if (!NT_SUCCESS(status)) {
577             OVS_LOG_ERROR(
578                 "Failed to open subscribe BFE state change callback, status: %x.",
579                 status);
580         }
581     }
582
583     return status;
584 }
585
586 VOID
587 OvsUnsubscribeBfeStateChanges()
588 {
589     NTSTATUS status = STATUS_SUCCESS;
590
591     if (gBfeSubscriptionHandle) {
592         status = FwpmBfeStateUnsubscribeChanges(gBfeSubscriptionHandle);
593         if (!NT_SUCCESS(status)) {
594             OVS_LOG_ERROR(
595                 "Failed to open unsubscribe BFE state change callback, status: %x.",
596                 status);
597         }
598         gBfeSubscriptionHandle = NULL;
599     }
600 }
601
602 VOID OvsRegisterSystemProvider(PVOID deviceObject)
603 {
604     NTSTATUS status = STATUS_SUCCESS;
605     HANDLE handle = NULL;
606
607     status = OvsSubscribeBfeStateChanges(deviceObject);
608     if (NT_SUCCESS(status)) {
609         if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) {
610             OvsTunnelEngineOpen(&handle);
611             if (handle) {
612                 OvsTunnelAddSystemProvider(handle);
613             }
614             OvsTunnelEngineClose(&handle);
615
616             OvsUnsubscribeBfeStateChanges();
617         }
618     }
619 }
620
621 VOID OvsUnregisterSystemProvider()
622 {
623     HANDLE handle = NULL;
624
625     OvsTunnelEngineOpen(&handle);
626     if (handle) {
627         OvsTunnelRemoveSystemProvider(handle);
628     }
629     OvsTunnelEngineClose(&handle);
630
631     OvsUnsubscribeBfeStateChanges();
632 }