datapath-windows: NetLink kernel side, Event subscription and notification
[cascardo/ovs.git] / datapath-windows / ovsext / Datapath.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 /*
18  * XXX: OVS_USE_NL_INTERFACE is being used to keep the legacy DPIF interface
19  * alive while we transition over to the netlink based interface.
20  * OVS_USE_NL_INTERFACE = 0 => legacy inteface to use with dpif-windows.c
21  * OVS_USE_NL_INTERFACE = 1 => netlink inteface to use with ported dpif-linux.c
22  */
23 #if defined OVS_USE_NL_INTERFACE && OVS_USE_NL_INTERFACE == 1
24
25 #include "precomp.h"
26 #include "Datapath.h"
27 #include "Jhash.h"
28 #include "Switch.h"
29 #include "Vport.h"
30 #include "Event.h"
31 #include "User.h"
32 #include "PacketIO.h"
33 #include "NetProto.h"
34 #include "Flow.h"
35 #include "User.h"
36
37 #ifdef OVS_DBG_MOD
38 #undef OVS_DBG_MOD
39 #endif
40 #define OVS_DBG_MOD OVS_DBG_DATAPATH
41 #include "Debug.h"
42
43 #define NETLINK_FAMILY_NAME_LEN 48
44
45
46 /*
47  * Netlink messages are grouped by family (aka type), and each family supports
48  * a set of commands, and can be passed both from kernel -> userspace or
49  * vice-versa. To call into the kernel, userspace uses a device operation which
50  * is outside of a netlink message.
51  *
52  * Each command results in the invocation of a handler function to implement the
53  * request functionality.
54  *
55  * Expectedly, only certain combinations of (device operation, netlink family,
56  * command) are valid.
57  *
58  * Here, we implement the basic infrastructure to perform validation on the
59  * incoming message, version checking, and also to invoke the corresponding
60  * handler to do the heavy-lifting.
61  */
62
63 /*
64  * Handler for a given netlink command. Not all the parameters are used by all
65  * the handlers.
66  */
67 typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
68                                     UINT32 *replyLen);
69
70 typedef struct _NETLINK_CMD {
71     UINT16 cmd;
72     NetlinkCmdHandler *handler;
73     UINT32 supportedDevOp;      /* Supported device operations. */
74     BOOLEAN validateDpIndex;    /* Does command require a valid DP argument. */
75 } NETLINK_CMD, *PNETLINK_CMD;
76
77 /* A netlink family is a group of commands. */
78 typedef struct _NETLINK_FAMILY {
79     CHAR *name;
80     UINT32 id;
81     UINT8 version;
82     UINT8 pad;
83     UINT16 maxAttr;
84     NETLINK_CMD *cmds;          /* Array of netlink commands and handlers. */
85     UINT16 opsCount;
86 } NETLINK_FAMILY, *PNETLINK_FAMILY;
87
88 /*
89  * Device operations to tag netlink commands with. This is a bitmask since it is
90  * possible that a particular command can be invoked via different device
91  * operations.
92  */
93 #define OVS_READ_DEV_OP          (1 << 0)
94 #define OVS_WRITE_DEV_OP         (1 << 1)
95 #define OVS_TRANSACTION_DEV_OP   (1 << 2)
96
97 /* Handlers for the various netlink commands. */
98 static NetlinkCmdHandler OvsGetPidCmdHandler,
99                          OvsGetDpCmdHandler,
100                          OvsPendEventCmdHandler,
101                          OvsSubscribeEventCmdHandler;
102
103 /*
104  * The various netlink families, along with the supported commands. Most of
105  * these families and commands are part of the openvswitch specification for a
106  * netlink datapath. In addition, each platform can implement a few families
107  * and commands as extensions.
108  */
109
110 /* Netlink control family: this is a Windows specific family. */
111 NETLINK_CMD nlControlFamilyCmdOps[] = {
112     { .cmd             = OVS_CTRL_CMD_WIN_GET_PID,
113       .handler         = OvsGetPidCmdHandler,
114       .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
115       .validateDpIndex = FALSE,
116     },
117     { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
118       .handler = OvsPendEventCmdHandler,
119       .supportedDevOp = OVS_WRITE_DEV_OP,
120       .validateDpIndex = TRUE,
121     },
122     { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
123       .handler = OvsSubscribeEventCmdHandler,
124       .supportedDevOp = OVS_WRITE_DEV_OP,
125       .validateDpIndex = TRUE,
126     }
127 };
128
129 NETLINK_FAMILY nlControlFamilyOps = {
130     .name     = OVS_WIN_CONTROL_FAMILY,
131     .id       = OVS_WIN_NL_CTRL_FAMILY_ID,
132     .version  = OVS_WIN_CONTROL_VERSION,
133     .maxAttr  = OVS_WIN_CONTROL_ATTR_MAX,
134     .cmds     = nlControlFamilyCmdOps,
135     .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
136 };
137
138 /* Netlink datapath family. */
139 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
140     { .cmd             = OVS_DP_CMD_GET,
141       .handler         = OvsGetDpCmdHandler,
142       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
143       .validateDpIndex = FALSE
144     }
145 };
146
147 NETLINK_FAMILY nlDatapathFamilyOps = {
148     .name     = OVS_DATAPATH_FAMILY,
149     .id       = OVS_WIN_NL_DATAPATH_FAMILY_ID,
150     .version  = OVS_DATAPATH_VERSION,
151     .maxAttr  = OVS_DP_ATTR_MAX,
152     .cmds     = nlDatapathFamilyCmdOps,
153     .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
154 };
155
156 /* Netlink packet family. */
157 /* XXX: Add commands here. */
158 NETLINK_FAMILY nlPacketFamilyOps = {
159     .name     = OVS_PACKET_FAMILY,
160     .id       = OVS_WIN_NL_PACKET_FAMILY_ID,
161     .version  = OVS_PACKET_VERSION,
162     .maxAttr  = OVS_PACKET_ATTR_MAX,
163     .cmds     = NULL, /* XXX: placeholder. */
164     .opsCount = 0
165 };
166
167 /* Netlink vport family. */
168 /* XXX: Add commands here. */
169 NETLINK_FAMILY nlVportFamilyOps = {
170     .name     = OVS_VPORT_FAMILY,
171     .id       = OVS_WIN_NL_VPORT_FAMILY_ID,
172     .version  = OVS_VPORT_VERSION,
173     .maxAttr  = OVS_VPORT_ATTR_MAX,
174     .cmds     = NULL, /* XXX: placeholder. */
175     .opsCount = 0
176 };
177
178 /* Netlink flow family. */
179 /* XXX: Add commands here. */
180 NETLINK_FAMILY nlFLowFamilyOps = {
181     .name     = OVS_FLOW_FAMILY,
182     .id       = OVS_WIN_NL_FLOW_FAMILY_ID,
183     .version  = OVS_FLOW_VERSION,
184     .maxAttr  = OVS_FLOW_ATTR_MAX,
185     .cmds     = NULL, /* XXX: placeholder. */
186     .opsCount = 0
187 };
188
189 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
190                                    UINT32 bufferLength,
191                                    UINT32 requiredLength,
192                                    PVOID *buffer);
193 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
194                                    POVS_OPEN_INSTANCE instance,
195                                    POVS_MESSAGE ovsMsg,
196                                    NETLINK_FAMILY *nlFamilyOps);
197 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
198                                         NETLINK_FAMILY *nlFamilyOps,
199                                         UINT32 *replyLen);
200 static NTSTATUS OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx);
201
202
203 /* Handles to the device object for communication with userspace. */
204 NDIS_HANDLE gOvsDeviceHandle;
205 PDEVICE_OBJECT gOvsDeviceObject;
206
207 _Dispatch_type_(IRP_MJ_CREATE)
208 _Dispatch_type_(IRP_MJ_CLOSE)
209 DRIVER_DISPATCH OvsOpenCloseDevice;
210
211 _Dispatch_type_(IRP_MJ_CLEANUP)
212 DRIVER_DISPATCH OvsCleanupDevice;
213
214 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
215 DRIVER_DISPATCH OvsDeviceControl;
216
217 #ifdef ALLOC_PRAGMA
218 #pragma alloc_text(INIT, OvsCreateDeviceObject)
219 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
220 #pragma alloc_text(PAGE, OvsCleanupDevice)
221 #pragma alloc_text(PAGE, OvsDeviceControl)
222 #endif // ALLOC_PRAGMA
223
224 /*
225  * We might hit this limit easily since userspace opens a netlink descriptor for
226  * each thread, and at least one descriptor per vport. Revisit this later.
227  */
228 #define OVS_MAX_OPEN_INSTANCES 512
229 #define OVS_SYSTEM_DP_NAME     "ovs-system"
230
231 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
232 UINT32 ovsNumberOfOpenInstances;
233 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
234
235 NDIS_SPIN_LOCK ovsCtrlLockObj;
236 PNDIS_SPIN_LOCK gOvsCtrlLock;
237
238
239 VOID
240 OvsInit()
241 {
242     gOvsCtrlLock = &ovsCtrlLockObj;
243     NdisAllocateSpinLock(gOvsCtrlLock);
244     OvsInitEventQueue();
245     OvsUserInit();
246 }
247
248 VOID
249 OvsCleanup()
250 {
251     OvsCleanupEventQueue();
252     if (gOvsCtrlLock) {
253         NdisFreeSpinLock(gOvsCtrlLock);
254         gOvsCtrlLock = NULL;
255     }
256     OvsUserCleanup();
257 }
258
259 VOID
260 OvsAcquireCtrlLock()
261 {
262     NdisAcquireSpinLock(gOvsCtrlLock);
263 }
264
265 VOID
266 OvsReleaseCtrlLock()
267 {
268     NdisReleaseSpinLock(gOvsCtrlLock);
269 }
270
271
272 /*
273  * --------------------------------------------------------------------------
274  * Creates the communication device between user and kernel, and also
275  * initializes the data associated data structures.
276  * --------------------------------------------------------------------------
277  */
278 NDIS_STATUS
279 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
280 {
281     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
282     UNICODE_STRING deviceName;
283     UNICODE_STRING symbolicDeviceName;
284     PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
285     NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
286     OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
287
288     RtlZeroMemory(dispatchTable,
289                   (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
290     dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
291     dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
292     dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
293     dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
294
295     NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
296     NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
297
298     RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
299
300     OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
301                            NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
302                            NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
303                            sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
304
305     deviceAttributes.DeviceName = &deviceName;
306     deviceAttributes.SymbolicName = &symbolicDeviceName;
307     deviceAttributes.MajorFunctions = dispatchTable;
308     deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
309
310     status = NdisRegisterDeviceEx(ovsExtDriverHandle,
311                                   &deviceAttributes,
312                                   &gOvsDeviceObject,
313                                   &gOvsDeviceHandle);
314     if (status != NDIS_STATUS_SUCCESS) {
315         POVS_DEVICE_EXTENSION ovsExt =
316             (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
317         ASSERT(gOvsDeviceObject != NULL);
318         ASSERT(gOvsDeviceHandle != NULL);
319
320         if (ovsExt) {
321             ovsExt->numberOpenInstance = 0;
322         }
323     } else {
324         /* Initialize the associated data structures. */
325         OvsInit();
326     }
327     OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
328     return status;
329 }
330
331
332 VOID
333 OvsDeleteDeviceObject()
334 {
335     if (gOvsDeviceHandle) {
336 #ifdef DBG
337         POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
338                     NdisGetDeviceReservedExtension(gOvsDeviceObject);
339         if (ovsExt) {
340             ASSERT(ovsExt->numberOpenInstance == 0);
341         }
342 #endif
343
344         ASSERT(gOvsDeviceObject);
345         NdisDeregisterDeviceEx(gOvsDeviceHandle);
346         gOvsDeviceHandle = NULL;
347         gOvsDeviceObject = NULL;
348     }
349     OvsCleanup();
350 }
351
352 POVS_OPEN_INSTANCE
353 OvsGetOpenInstance(PFILE_OBJECT fileObject,
354                    UINT32 dpNo)
355 {
356     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
357     ASSERT(instance);
358     ASSERT(instance->fileObject == fileObject);
359     if (gOvsSwitchContext == NULL ||
360         gOvsSwitchContext->dpNo != dpNo) {
361         return NULL;
362     }
363     return instance;
364 }
365
366
367 POVS_OPEN_INSTANCE
368 OvsFindOpenInstance(PFILE_OBJECT fileObject)
369 {
370     UINT32 i, j;
371     for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
372                        j < ovsNumberOfOpenInstances; i++) {
373         if (ovsOpenInstanceArray[i]) {
374             if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
375                 return ovsOpenInstanceArray[i];
376             }
377             j++;
378         }
379     }
380     return NULL;
381 }
382
383 NTSTATUS
384 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
385                    PFILE_OBJECT fileObject)
386 {
387     POVS_OPEN_INSTANCE instance =
388         (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
389     UINT32 i;
390
391     if (instance == NULL) {
392         return STATUS_NO_MEMORY;
393     }
394     OvsAcquireCtrlLock();
395     ASSERT(OvsFindOpenInstance(fileObject) == NULL);
396
397     if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
398         OvsReleaseCtrlLock();
399         OvsFreeMemory(instance);
400         return STATUS_INSUFFICIENT_RESOURCES;
401     }
402     RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
403
404     for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
405         if (ovsOpenInstanceArray[i] == NULL) {
406             ovsOpenInstanceArray[i] = instance;
407             instance->cookie = i;
408             break;
409         }
410     }
411     ASSERT(i < OVS_MAX_OPEN_INSTANCES);
412     instance->fileObject = fileObject;
413     ASSERT(fileObject->FsContext == NULL);
414     instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
415     if (instance->pid == 0) {
416         /* XXX: check for rollover. */
417     }
418     fileObject->FsContext = instance;
419     OvsReleaseCtrlLock();
420     return STATUS_SUCCESS;
421 }
422
423 static VOID
424 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
425 {
426     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
427     ASSERT(instance);
428     ASSERT(fileObject == instance->fileObject);
429     OvsCleanupEvent(instance);
430     OvsCleanupPacketQueue(instance);
431 }
432
433 VOID
434 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
435 {
436     POVS_OPEN_INSTANCE instance;
437     ASSERT(fileObject->FsContext);
438     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
439     ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
440
441     OvsAcquireCtrlLock();
442     fileObject->FsContext = NULL;
443     ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
444     ovsOpenInstanceArray[instance->cookie] = NULL;
445     OvsReleaseCtrlLock();
446     ASSERT(instance->eventQueue == NULL);
447     ASSERT (instance->packetQueue == NULL);
448     OvsFreeMemory(instance);
449 }
450
451 NTSTATUS
452 OvsCompleteIrpRequest(PIRP irp,
453                       ULONG_PTR infoPtr,
454                       NTSTATUS status)
455 {
456     irp->IoStatus.Information = infoPtr;
457     irp->IoStatus.Status = status;
458     IoCompleteRequest(irp, IO_NO_INCREMENT);
459     return status;
460 }
461
462
463 NTSTATUS
464 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
465                    PIRP irp)
466 {
467     PIO_STACK_LOCATION irpSp;
468     NTSTATUS status = STATUS_SUCCESS;
469     PFILE_OBJECT fileObject;
470     POVS_DEVICE_EXTENSION ovsExt =
471         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
472
473     ASSERT(deviceObject == gOvsDeviceObject);
474     ASSERT(ovsExt != NULL);
475
476     irpSp = IoGetCurrentIrpStackLocation(irp);
477     fileObject = irpSp->FileObject;
478     OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
479                   deviceObject, fileObject,
480                   ovsExt->numberOpenInstance);
481
482     switch (irpSp->MajorFunction) {
483     case IRP_MJ_CREATE:
484         status = OvsAddOpenInstance(ovsExt, fileObject);
485         if (STATUS_SUCCESS == status) {
486             InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
487         }
488         break;
489     case IRP_MJ_CLOSE:
490         ASSERT(ovsExt->numberOpenInstance > 0);
491         OvsRemoveOpenInstance(fileObject);
492         InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
493         break;
494     default:
495         ASSERT(0);
496     }
497     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
498 }
499
500 _Use_decl_annotations_
501 NTSTATUS
502 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
503                  PIRP irp)
504 {
505
506     PIO_STACK_LOCATION irpSp;
507     PFILE_OBJECT fileObject;
508
509     NTSTATUS status = STATUS_SUCCESS;
510 #ifdef DBG
511     POVS_DEVICE_EXTENSION ovsExt =
512         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
513     if (ovsExt) {
514         ASSERT(ovsExt->numberOpenInstance > 0);
515     }
516 #else
517     UNREFERENCED_PARAMETER(deviceObject);
518 #endif
519     ASSERT(deviceObject == gOvsDeviceObject);
520     irpSp = IoGetCurrentIrpStackLocation(irp);
521     fileObject = irpSp->FileObject;
522
523     ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
524
525     OvsCleanupOpenInstance(fileObject);
526
527     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
528 }
529
530
531 /*
532  * --------------------------------------------------------------------------
533  * IOCTL function handler for the device.
534  * --------------------------------------------------------------------------
535  */
536 NTSTATUS
537 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
538                  PIRP irp)
539 {
540
541     PIO_STACK_LOCATION irpSp;
542     NTSTATUS status = STATUS_SUCCESS;
543     PFILE_OBJECT fileObject;
544     PVOID inputBuffer = NULL;
545     PVOID outputBuffer = NULL;
546     UINT32 inputBufferLen, outputBufferLen;
547     UINT32 code, replyLen = 0;
548     POVS_OPEN_INSTANCE instance;
549     UINT32 devOp;
550     OVS_MESSAGE ovsMsgReadOp;
551     POVS_MESSAGE ovsMsg;
552     NETLINK_FAMILY *nlFamilyOps;
553     OVS_USER_PARAMS_CONTEXT usrParamsCtx;
554
555 #ifdef DBG
556     POVS_DEVICE_EXTENSION ovsExt =
557         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
558     ASSERT(deviceObject == gOvsDeviceObject);
559     ASSERT(ovsExt);
560     ASSERT(ovsExt->numberOpenInstance > 0);
561 #else
562     UNREFERENCED_PARAMETER(deviceObject);
563 #endif
564
565     irpSp = IoGetCurrentIrpStackLocation(irp);
566
567     ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
568     ASSERT(irpSp->FileObject != NULL);
569
570     fileObject = irpSp->FileObject;
571     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
572     code = irpSp->Parameters.DeviceIoControl.IoControlCode;
573     inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
574     outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
575     inputBuffer = irp->AssociatedIrp.SystemBuffer;
576
577     /* Concurrent netlink operations are not supported. */
578     if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
579         status = STATUS_RESOURCE_IN_USE;
580         goto done;
581     }
582
583     /*
584      * Validate the input/output buffer arguments depending on the type of the
585      * operation.
586      */
587     switch (code) {
588     case OVS_IOCTL_TRANSACT:
589         /* Input buffer is mandatory, output buffer is optional. */
590         if (outputBufferLen != 0) {
591             status = MapIrpOutputBuffer(irp, outputBufferLen,
592                                         sizeof *ovsMsg, &outputBuffer);
593             if (status != STATUS_SUCCESS) {
594                 goto done;
595             }
596             ASSERT(outputBuffer);
597         }
598
599         if (inputBufferLen < sizeof (*ovsMsg)) {
600             status = STATUS_NDIS_INVALID_LENGTH;
601             goto done;
602         }
603
604         ovsMsg = inputBuffer;
605         devOp = OVS_TRANSACTION_DEV_OP;
606         break;
607
608     case OVS_IOCTL_READ:
609         /* Output buffer is mandatory. */
610         if (outputBufferLen != 0) {
611             status = MapIrpOutputBuffer(irp, outputBufferLen,
612                                         sizeof *ovsMsg, &outputBuffer);
613             if (status != STATUS_SUCCESS) {
614                 goto done;
615             }
616             ASSERT(outputBuffer);
617         } else {
618             status = STATUS_NDIS_INVALID_LENGTH;
619             goto done;
620         }
621
622         /*
623          * Operate in the mode that read ioctl is similar to ReadFile(). This
624          * might change as the userspace code gets implemented.
625          */
626         inputBuffer = NULL;
627         inputBufferLen = 0;
628
629         /*
630          * For implementing read (ioctl or otherwise), we need to store some
631          * state in the instance to indicate the command that started the dump
632          * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
633          * that 'ovsMsgReadOp' is needed only in this function to call into the
634          * appropraite handler. The handler itself can access the state in the
635          * instance.
636          *
637          * In the absence of a dump start, return 0 bytes.
638          */
639         if (instance->dumpState.ovsMsg == NULL) {
640             replyLen = 0;
641             status = STATUS_SUCCESS;
642             goto done;
643         }
644         RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
645                       sizeof (ovsMsgReadOp));
646
647         /* Create an NL message for consumption. */
648         ovsMsg = &ovsMsgReadOp;
649         devOp = OVS_READ_DEV_OP;
650
651         break;
652
653     case OVS_IOCTL_WRITE:
654         /* Input buffer is mandatory. */
655         if (inputBufferLen < sizeof (*ovsMsg)) {
656             status = STATUS_NDIS_INVALID_LENGTH;
657             goto done;
658         }
659
660         ovsMsg = inputBuffer;
661         devOp = OVS_WRITE_DEV_OP;
662         break;
663
664     default:
665         status = STATUS_INVALID_DEVICE_REQUEST;
666         goto done;
667     }
668
669     ASSERT(ovsMsg);
670     switch (ovsMsg->nlMsg.nlmsgType) {
671     case OVS_WIN_NL_CTRL_FAMILY_ID:
672         nlFamilyOps = &nlControlFamilyOps;
673         break;
674     case OVS_WIN_NL_DATAPATH_FAMILY_ID:
675         nlFamilyOps = &nlDatapathFamilyOps;
676         break;
677     case OVS_WIN_NL_PACKET_FAMILY_ID:
678     case OVS_WIN_NL_FLOW_FAMILY_ID:
679     case OVS_WIN_NL_VPORT_FAMILY_ID:
680         status = STATUS_NOT_IMPLEMENTED;
681         goto done;
682
683     default:
684         status = STATUS_INVALID_PARAMETER;
685         goto done;
686     }
687
688     /*
689      * For read operation, the netlink command has already been validated
690      * previously.
691      */
692     if (devOp != OVS_READ_DEV_OP) {
693         status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
694         if (status != STATUS_SUCCESS) {
695             goto done;
696         }
697     }
698
699     InitUserParamsCtx(irp, instance, devOp, ovsMsg,
700                       inputBuffer, inputBufferLen,
701                       outputBuffer, outputBufferLen,
702                       &usrParamsCtx);
703
704     status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
705
706 done:
707     KeMemoryBarrier();
708     instance->inUse = 0;
709     return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
710 }
711
712
713 /*
714  * --------------------------------------------------------------------------
715  * Function to validate a netlink command. Only certain combinations of
716  * (device operation, netlink family, command) are valid.
717  *
718  * XXX: Take policy into consideration.
719  * --------------------------------------------------------------------------
720  */
721 static NTSTATUS
722 ValidateNetlinkCmd(UINT32 devOp,
723                    POVS_OPEN_INSTANCE instance,
724                    POVS_MESSAGE ovsMsg,
725                    NETLINK_FAMILY *nlFamilyOps)
726 {
727     NTSTATUS status = STATUS_INVALID_PARAMETER;
728     UINT16 i;
729
730     for (i = 0; i < nlFamilyOps->opsCount; i++) {
731         if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
732             /* Validate if the command is valid for the device operation. */
733             if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
734                 status = STATUS_INVALID_PARAMETER;
735                 goto done;
736             }
737
738             /* Validate the version. */
739             if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
740                 status = STATUS_INVALID_PARAMETER;
741                 goto done;
742             }
743
744             /* Validate the DP for commands that require a DP. */
745             if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
746                 OvsAcquireCtrlLock();
747                 if (ovsMsg->ovsHdr.dp_ifindex !=
748                     (INT)gOvsSwitchContext->dpNo) {
749                     status = STATUS_INVALID_PARAMETER;
750                     OvsReleaseCtrlLock();
751                     goto done;
752                 }
753                 OvsReleaseCtrlLock();
754             }
755
756             /* Validate the PID. */
757             if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
758                 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
759                     status = STATUS_INVALID_PARAMETER;
760                     goto done;
761                 }
762             }
763
764             status = STATUS_SUCCESS;
765             break;
766         }
767     }
768
769 done:
770     return status;
771 }
772
773 /*
774  * --------------------------------------------------------------------------
775  * Function to invoke the netlink command handler.
776  * --------------------------------------------------------------------------
777  */
778 static NTSTATUS
779 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
780                         NETLINK_FAMILY *nlFamilyOps,
781                         UINT32 *replyLen)
782 {
783     NTSTATUS status = STATUS_INVALID_PARAMETER;
784     UINT16 i;
785
786     for (i = 0; i < nlFamilyOps->opsCount; i++) {
787         if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
788             NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
789             ASSERT(handler);
790             if (handler) {
791                 status = handler(usrParamsCtx, replyLen);
792             }
793             break;
794         }
795     }
796
797     return status;
798 }
799
800
801 /*
802  * --------------------------------------------------------------------------
803  *  Each handle on the device is assigned a unique PID when the handle is
804  *  created. On platforms that support netlink natively, the PID is available
805  *  to userspace when the netlink socket is created. However, without native
806  *  netlink support on Windows, OVS datapath generates the PID and lets the
807  *  userspace query it.
808  *
809  *  This function implements the query.
810  * --------------------------------------------------------------------------
811  */
812 static NTSTATUS
813 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
814                     UINT32 *replyLen)
815 {
816     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
817     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
818
819     if (usrParamsCtx->outputLength >= sizeof *msgOut) {
820         POVS_OPEN_INSTANCE instance =
821             (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
822
823         RtlZeroMemory(msgOut, sizeof *msgOut);
824         msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
825         msgOut->nlMsg.nlmsgPid = instance->pid;
826         *replyLen = sizeof *msgOut;
827         /* XXX: We might need to return the DP index as well. */
828     } else {
829         return STATUS_NDIS_INVALID_LENGTH;
830     }
831
832     return STATUS_SUCCESS;
833 }
834
835 /*
836  * --------------------------------------------------------------------------
837  * Utility function to fill up information about the datapath in a reply to
838  * userspace.
839  * Assumes that 'gOvsCtrlLock' lock is acquired.
840  * --------------------------------------------------------------------------
841  */
842 static NTSTATUS
843 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
844               POVS_MESSAGE msgIn,
845               PNL_BUFFER nlBuf)
846 {
847     BOOLEAN writeOk;
848     OVS_MESSAGE msgOutTmp;
849     OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
850     PNL_MSG_HDR nlMsg;
851
852     /* XXX: Add API for nlBuf->bufRemLen. */
853     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
854
855     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
856     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
857     msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
858     msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
859
860     msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
861     msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
862     msgOutTmp.genlMsg.reserved = 0;
863
864     msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
865
866     writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
867     if (writeOk) {
868         writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
869                                      OVS_SYSTEM_DP_NAME);
870     }
871     if (writeOk) {
872         OVS_DP_STATS dpStats;
873
874         dpStats.n_hit = datapath->hits;
875         dpStats.n_missed = datapath->misses;
876         dpStats.n_lost = datapath->lost;
877         dpStats.n_flows = datapath->nFlows;
878         writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
879                                      (PCHAR)&dpStats, sizeof dpStats);
880     }
881     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
882     nlMsg->nlmsgLen = NlBufSize(nlBuf);
883
884     return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
885 }
886
887
888 /*
889  * --------------------------------------------------------------------------
890  * Handler for queueing an IRP used for event notification. The IRP is
891  * completed when a port state changes. STATUS_PENDING is returned on
892  * success. User mode keep a pending IRP at all times.
893  * --------------------------------------------------------------------------
894  */
895 static NTSTATUS
896 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
897                        UINT32 *replyLen)
898 {
899     NDIS_STATUS status;
900
901     UNREFERENCED_PARAMETER(replyLen);
902
903     POVS_OPEN_INSTANCE instance =
904         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
905     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
906     OVS_EVENT_POLL poll;
907
908     poll.dpNo = msgIn->ovsHdr.dp_ifindex;
909     status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
910                                &poll, sizeof poll);
911     return status;
912 }
913
914 /*
915  * --------------------------------------------------------------------------
916  *  Handler for the subscription for the event queue
917  * --------------------------------------------------------------------------
918  */
919 static NTSTATUS
920 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
921                             UINT32 *replyLen)
922 {
923     NDIS_STATUS status;
924     OVS_EVENT_SUBSCRIBE request;
925     BOOLEAN rc;
926     UINT8 join;
927     PNL_ATTR attrs[2];
928     const NL_POLICY policy[] =  {
929         [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
930         [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
931         };
932
933     UNREFERENCED_PARAMETER(replyLen);
934
935     POVS_OPEN_INSTANCE instance =
936         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
937     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
938
939     rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),policy, attrs, 2);
940     if (!rc) {
941         status = STATUS_INVALID_PARAMETER;
942         goto done;
943     }
944
945     /* XXX Ignore the MC group for now */
946     join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
947     request.dpNo = msgIn->ovsHdr.dp_ifindex;
948     request.subscribe = join;
949     request.mask = OVS_EVENT_MASK_ALL;
950
951     status = OvsSubscribeEventIoctl(instance->fileObject, &request,
952                                     sizeof request);
953 done:
954     return status;
955 }
956
957
958 /*
959  * --------------------------------------------------------------------------
960  *  Handler for the get dp command. The function handles the initial call to
961  *  setup the dump state, as well as subsequent calls to continue dumping data.
962  * --------------------------------------------------------------------------
963  */
964 static NTSTATUS
965 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
966                    UINT32 *replyLen)
967 {
968     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
969     POVS_OPEN_INSTANCE instance =
970         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
971
972     if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
973         *replyLen = 0;
974         OvsSetupDumpStart(usrParamsCtx);
975     } else {
976         NL_BUFFER nlBuf;
977         NTSTATUS status;
978         POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
979
980         ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
981
982         if (instance->dumpState.ovsMsg == NULL) {
983             ASSERT(FALSE);
984             return STATUS_INVALID_DEVICE_STATE;
985         }
986
987         /* Dump state must have been deleted after previous dump operation. */
988         ASSERT(instance->dumpState.index[0] == 0);
989         /* Output buffer has been validated while validating read dev op. */
990         ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
991
992         NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
993                   usrParamsCtx->outputLength);
994
995         OvsAcquireCtrlLock();
996         if (!gOvsSwitchContext) {
997             /* Treat this as a dump done. */
998             OvsReleaseCtrlLock();
999             *replyLen = 0;
1000             FreeUserDumpState(instance);
1001             return STATUS_SUCCESS;
1002         }
1003         status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1004         OvsReleaseCtrlLock();
1005
1006         if (status != STATUS_SUCCESS) {
1007             *replyLen = 0;
1008             FreeUserDumpState(instance);
1009             return status;
1010         }
1011
1012         /* Increment the dump index. */
1013         instance->dumpState.index[0] = 1;
1014         *replyLen = msgOut->nlMsg.nlmsgLen;
1015
1016         /* Free up the dump state, since there's no more data to continue. */
1017         FreeUserDumpState(instance);
1018     }
1019
1020     return STATUS_SUCCESS;
1021 }
1022
1023 static NTSTATUS
1024 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1025 {
1026     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1027     POVS_OPEN_INSTANCE instance =
1028         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1029
1030     /* input buffer has been validated while validating write dev op. */
1031     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1032
1033     /* A write operation that does not indicate dump start is invalid. */
1034     if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1035         return STATUS_INVALID_PARAMETER;
1036     }
1037     /* XXX: Handle other NLM_F_* flags in the future. */
1038
1039     /*
1040      * This operation should be setting up the dump state. If there's any
1041      * previous state, clear it up so as to set it up afresh.
1042      */
1043     if (instance->dumpState.ovsMsg != NULL) {
1044         FreeUserDumpState(instance);
1045     }
1046
1047     return InitUserDumpState(instance, msgIn);
1048 }
1049
1050 /*
1051  * --------------------------------------------------------------------------
1052  *  Utility function to map the output buffer in an IRP. The buffer is assumed
1053  *  to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1054  * --------------------------------------------------------------------------
1055  */
1056 static NTSTATUS
1057 MapIrpOutputBuffer(PIRP irp,
1058                    UINT32 bufferLength,
1059                    UINT32 requiredLength,
1060                    PVOID *buffer)
1061 {
1062     ASSERT(irp);
1063     ASSERT(buffer);
1064     ASSERT(bufferLength);
1065     ASSERT(requiredLength);
1066     if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1067         return STATUS_INVALID_PARAMETER;
1068     }
1069
1070     if (bufferLength < requiredLength) {
1071         return STATUS_NDIS_INVALID_LENGTH;
1072     }
1073     if (irp->MdlAddress == NULL) {
1074         return STATUS_INVALID_PARAMETER;
1075     }
1076     *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1077                                            NormalPagePriority);
1078     if (*buffer == NULL) {
1079         return STATUS_INSUFFICIENT_RESOURCES;
1080     }
1081
1082     return STATUS_SUCCESS;
1083 }
1084
1085 #endif /* OVS_USE_NL_INTERFACE */