datapath-windows: Add stub handlers for netdev commands
[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 /* Handlers for the various netlink commands. */
89 static NetlinkCmdHandler OvsGetPidCmdHandler,
90                          OvsPendEventCmdHandler,
91                          OvsSubscribeEventCmdHandler,
92                          OvsReadEventCmdHandler,
93                          OvsGetDpCmdHandler,
94                          OvsSetDpCmdHandler,
95                          OvsGetVportCmdHandler;
96
97 NetlinkCmdHandler        OvsGetNetdevCmdHandler;
98
99 static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
100                                        UINT32 *replyLen);
101 static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
102                                 UINT32 *replyLen);
103 static NTSTATUS HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
104                                     UINT32 *replyLen);
105
106 /*
107  * The various netlink families, along with the supported commands. Most of
108  * these families and commands are part of the openvswitch specification for a
109  * netlink datapath. In addition, each platform can implement a few families
110  * and commands as extensions.
111  */
112
113 /* Netlink control family: this is a Windows specific family. */
114 NETLINK_CMD nlControlFamilyCmdOps[] = {
115     { .cmd             = OVS_CTRL_CMD_WIN_GET_PID,
116       .handler         = OvsGetPidCmdHandler,
117       .supportedDevOp  = OVS_TRANSACTION_DEV_OP,
118       .validateDpIndex = FALSE,
119     },
120     { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ,
121       .handler = OvsPendEventCmdHandler,
122       .supportedDevOp = OVS_WRITE_DEV_OP,
123       .validateDpIndex = TRUE,
124     },
125     { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ,
126       .handler = OvsSubscribeEventCmdHandler,
127       .supportedDevOp = OVS_WRITE_DEV_OP,
128       .validateDpIndex = TRUE,
129     },
130     { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY,
131       .handler = OvsReadEventCmdHandler,
132       .supportedDevOp = OVS_READ_EVENT_DEV_OP,
133       .validateDpIndex = FALSE,
134     }
135 };
136
137 NETLINK_FAMILY nlControlFamilyOps = {
138     .name     = OVS_WIN_CONTROL_FAMILY,
139     .id       = OVS_WIN_NL_CTRL_FAMILY_ID,
140     .version  = OVS_WIN_CONTROL_VERSION,
141     .maxAttr  = OVS_WIN_CONTROL_ATTR_MAX,
142     .cmds     = nlControlFamilyCmdOps,
143     .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps)
144 };
145
146 /* Netlink datapath family. */
147 NETLINK_CMD nlDatapathFamilyCmdOps[] = {
148     { .cmd             = OVS_DP_CMD_GET,
149       .handler         = OvsGetDpCmdHandler,
150       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
151                          OVS_TRANSACTION_DEV_OP,
152       .validateDpIndex = FALSE
153     },
154     { .cmd             = OVS_DP_CMD_SET,
155       .handler         = OvsSetDpCmdHandler,
156       .supportedDevOp  = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
157                          OVS_TRANSACTION_DEV_OP,
158       .validateDpIndex = TRUE
159     }
160 };
161
162 NETLINK_FAMILY nlDatapathFamilyOps = {
163     .name     = OVS_DATAPATH_FAMILY,
164     .id       = OVS_WIN_NL_DATAPATH_FAMILY_ID,
165     .version  = OVS_DATAPATH_VERSION,
166     .maxAttr  = OVS_DP_ATTR_MAX,
167     .cmds     = nlDatapathFamilyCmdOps,
168     .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps)
169 };
170
171 /* Netlink packet family. */
172 /* XXX: Add commands here. */
173 NETLINK_FAMILY nlPacketFamilyOps = {
174     .name     = OVS_PACKET_FAMILY,
175     .id       = OVS_WIN_NL_PACKET_FAMILY_ID,
176     .version  = OVS_PACKET_VERSION,
177     .maxAttr  = OVS_PACKET_ATTR_MAX,
178     .cmds     = NULL, /* XXX: placeholder. */
179     .opsCount = 0
180 };
181
182 /* Netlink vport family. */
183 NETLINK_CMD nlVportFamilyCmdOps[] = {
184     { .cmd = OVS_VPORT_CMD_GET,
185       .handler = OvsGetVportCmdHandler,
186       .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP |
187                         OVS_TRANSACTION_DEV_OP,
188       .validateDpIndex = TRUE
189     }
190 };
191
192 NETLINK_FAMILY nlVportFamilyOps = {
193     .name     = OVS_VPORT_FAMILY,
194     .id       = OVS_WIN_NL_VPORT_FAMILY_ID,
195     .version  = OVS_VPORT_VERSION,
196     .maxAttr  = OVS_VPORT_ATTR_MAX,
197     .cmds     = nlVportFamilyCmdOps,
198     .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps)
199 };
200
201 /* Netlink flow family. */
202
203 NETLINK_CMD nlFlowFamilyCmdOps[] = {
204     { .cmd              = OVS_FLOW_CMD_NEW,
205       .handler          = OvsFlowNlCmdHandler,
206       .supportedDevOp   = OVS_TRANSACTION_DEV_OP,
207       .validateDpIndex  = TRUE
208     },
209     { .cmd              = OVS_FLOW_CMD_SET,
210       .handler          = OvsFlowNlCmdHandler,
211       .supportedDevOp   = OVS_TRANSACTION_DEV_OP,
212       .validateDpIndex  = TRUE
213     },
214     { .cmd              = OVS_FLOW_CMD_DEL,
215       .handler          = OvsFlowNlCmdHandler,
216       .supportedDevOp   = OVS_TRANSACTION_DEV_OP,
217       .validateDpIndex  = TRUE
218     },
219     { .cmd              = OVS_FLOW_CMD_GET,
220       .handler          = OvsFlowNlGetCmdHandler,
221       .supportedDevOp   = OVS_TRANSACTION_DEV_OP |
222                           OVS_WRITE_DEV_OP | OVS_READ_DEV_OP,
223       .validateDpIndex  = TRUE
224     },
225 };
226
227 NETLINK_FAMILY nlFLowFamilyOps = {
228     .name     = OVS_FLOW_FAMILY,
229     .id       = OVS_WIN_NL_FLOW_FAMILY_ID,
230     .version  = OVS_FLOW_VERSION,
231     .maxAttr  = OVS_FLOW_ATTR_MAX,
232     .cmds     = nlFlowFamilyCmdOps,
233     .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps)
234 };
235
236 /* Netlink netdev family. */
237 NETLINK_CMD nlNetdevFamilyCmdOps[] = {
238     { .cmd = OVS_WIN_NETDEV_CMD_GET,
239       .handler = OvsGetNetdevCmdHandler,
240       .supportedDevOp = OVS_TRANSACTION_DEV_OP,
241       .validateDpIndex = FALSE
242     },
243 };
244
245 NETLINK_FAMILY nlNetdevFamilyOps = {
246     .name     = OVS_WIN_NETDEV_FAMILY,
247     .id       = OVS_WIN_NL_NETDEV_FAMILY_ID,
248     .version  = OVS_WIN_NETDEV_VERSION,
249     .maxAttr  = OVS_WIN_NETDEV_ATTR_MAX,
250     .cmds     = nlNetdevFamilyCmdOps,
251     .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps)
252 };
253
254 static NTSTATUS MapIrpOutputBuffer(PIRP irp,
255                                    UINT32 bufferLength,
256                                    UINT32 requiredLength,
257                                    PVOID *buffer);
258 static NTSTATUS ValidateNetlinkCmd(UINT32 devOp,
259                                    POVS_OPEN_INSTANCE instance,
260                                    POVS_MESSAGE ovsMsg,
261                                    NETLINK_FAMILY *nlFamilyOps);
262 static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
263                                         NETLINK_FAMILY *nlFamilyOps,
264                                         UINT32 *replyLen);
265
266 /* Handles to the device object for communication with userspace. */
267 NDIS_HANDLE gOvsDeviceHandle;
268 PDEVICE_OBJECT gOvsDeviceObject;
269
270 _Dispatch_type_(IRP_MJ_CREATE)
271 _Dispatch_type_(IRP_MJ_CLOSE)
272 DRIVER_DISPATCH OvsOpenCloseDevice;
273
274 _Dispatch_type_(IRP_MJ_CLEANUP)
275 DRIVER_DISPATCH OvsCleanupDevice;
276
277 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
278 DRIVER_DISPATCH OvsDeviceControl;
279
280 #ifdef ALLOC_PRAGMA
281 #pragma alloc_text(INIT, OvsCreateDeviceObject)
282 #pragma alloc_text(PAGE, OvsOpenCloseDevice)
283 #pragma alloc_text(PAGE, OvsCleanupDevice)
284 #pragma alloc_text(PAGE, OvsDeviceControl)
285 #endif // ALLOC_PRAGMA
286
287 /*
288  * We might hit this limit easily since userspace opens a netlink descriptor for
289  * each thread, and at least one descriptor per vport. Revisit this later.
290  */
291 #define OVS_MAX_OPEN_INSTANCES 512
292 #define OVS_SYSTEM_DP_NAME     "ovs-system"
293
294 POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES];
295 UINT32 ovsNumberOfOpenInstances;
296 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
297
298 NDIS_SPIN_LOCK ovsCtrlLockObj;
299 PNDIS_SPIN_LOCK gOvsCtrlLock;
300
301
302 VOID
303 OvsInit()
304 {
305     gOvsCtrlLock = &ovsCtrlLockObj;
306     NdisAllocateSpinLock(gOvsCtrlLock);
307     OvsInitEventQueue();
308     OvsUserInit();
309 }
310
311 VOID
312 OvsCleanup()
313 {
314     OvsCleanupEventQueue();
315     if (gOvsCtrlLock) {
316         NdisFreeSpinLock(gOvsCtrlLock);
317         gOvsCtrlLock = NULL;
318     }
319     OvsUserCleanup();
320 }
321
322 VOID
323 OvsAcquireCtrlLock()
324 {
325     NdisAcquireSpinLock(gOvsCtrlLock);
326 }
327
328 VOID
329 OvsReleaseCtrlLock()
330 {
331     NdisReleaseSpinLock(gOvsCtrlLock);
332 }
333
334
335 /*
336  * --------------------------------------------------------------------------
337  * Creates the communication device between user and kernel, and also
338  * initializes the data associated data structures.
339  * --------------------------------------------------------------------------
340  */
341 NDIS_STATUS
342 OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle)
343 {
344     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
345     UNICODE_STRING deviceName;
346     UNICODE_STRING symbolicDeviceName;
347     PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
348     NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes;
349     OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle);
350
351     RtlZeroMemory(dispatchTable,
352                   (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH));
353     dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice;
354     dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice;
355     dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice;
356     dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl;
357
358     NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT);
359     NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS);
360
361     RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
362
363     OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header,
364                            NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
365                            NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
366                            sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES));
367
368     deviceAttributes.DeviceName = &deviceName;
369     deviceAttributes.SymbolicName = &symbolicDeviceName;
370     deviceAttributes.MajorFunctions = dispatchTable;
371     deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION);
372
373     status = NdisRegisterDeviceEx(ovsExtDriverHandle,
374                                   &deviceAttributes,
375                                   &gOvsDeviceObject,
376                                   &gOvsDeviceHandle);
377     if (status != NDIS_STATUS_SUCCESS) {
378         POVS_DEVICE_EXTENSION ovsExt =
379             (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject);
380         ASSERT(gOvsDeviceObject != NULL);
381         ASSERT(gOvsDeviceHandle != NULL);
382
383         if (ovsExt) {
384             ovsExt->numberOpenInstance = 0;
385         }
386     } else {
387         /* Initialize the associated data structures. */
388         OvsInit();
389     }
390     OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject);
391     return status;
392 }
393
394
395 VOID
396 OvsDeleteDeviceObject()
397 {
398     if (gOvsDeviceHandle) {
399 #ifdef DBG
400         POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)
401                     NdisGetDeviceReservedExtension(gOvsDeviceObject);
402         if (ovsExt) {
403             ASSERT(ovsExt->numberOpenInstance == 0);
404         }
405 #endif
406
407         ASSERT(gOvsDeviceObject);
408         NdisDeregisterDeviceEx(gOvsDeviceHandle);
409         gOvsDeviceHandle = NULL;
410         gOvsDeviceObject = NULL;
411     }
412     OvsCleanup();
413 }
414
415 POVS_OPEN_INSTANCE
416 OvsGetOpenInstance(PFILE_OBJECT fileObject,
417                    UINT32 dpNo)
418 {
419     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
420     ASSERT(instance);
421     ASSERT(instance->fileObject == fileObject);
422     if (gOvsSwitchContext == NULL ||
423         gOvsSwitchContext->dpNo != dpNo) {
424         return NULL;
425     }
426     return instance;
427 }
428
429
430 POVS_OPEN_INSTANCE
431 OvsFindOpenInstance(PFILE_OBJECT fileObject)
432 {
433     UINT32 i, j;
434     for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES &&
435                        j < ovsNumberOfOpenInstances; i++) {
436         if (ovsOpenInstanceArray[i]) {
437             if (ovsOpenInstanceArray[i]->fileObject == fileObject) {
438                 return ovsOpenInstanceArray[i];
439             }
440             j++;
441         }
442     }
443     return NULL;
444 }
445
446 NTSTATUS
447 OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt,
448                    PFILE_OBJECT fileObject)
449 {
450     POVS_OPEN_INSTANCE instance =
451         (POVS_OPEN_INSTANCE) OvsAllocateMemory(sizeof (OVS_OPEN_INSTANCE));
452     UINT32 i;
453
454     if (instance == NULL) {
455         return STATUS_NO_MEMORY;
456     }
457     OvsAcquireCtrlLock();
458     ASSERT(OvsFindOpenInstance(fileObject) == NULL);
459
460     if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) {
461         OvsReleaseCtrlLock();
462         OvsFreeMemory(instance);
463         return STATUS_INSUFFICIENT_RESOURCES;
464     }
465     RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE));
466
467     for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) {
468         if (ovsOpenInstanceArray[i] == NULL) {
469             ovsOpenInstanceArray[i] = instance;
470             ovsNumberOfOpenInstances++;
471             instance->cookie = i;
472             break;
473         }
474     }
475     ASSERT(i < OVS_MAX_OPEN_INSTANCES);
476     instance->fileObject = fileObject;
477     ASSERT(fileObject->FsContext == NULL);
478     instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount);
479     if (instance->pid == 0) {
480         /* XXX: check for rollover. */
481     }
482     fileObject->FsContext = instance;
483     OvsReleaseCtrlLock();
484     return STATUS_SUCCESS;
485 }
486
487 static VOID
488 OvsCleanupOpenInstance(PFILE_OBJECT fileObject)
489 {
490     POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
491     ASSERT(instance);
492     ASSERT(fileObject == instance->fileObject);
493     OvsCleanupEvent(instance);
494     OvsCleanupPacketQueue(instance);
495 }
496
497 VOID
498 OvsRemoveOpenInstance(PFILE_OBJECT fileObject)
499 {
500     POVS_OPEN_INSTANCE instance;
501     ASSERT(fileObject->FsContext);
502     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
503     ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES);
504
505     OvsAcquireCtrlLock();
506     fileObject->FsContext = NULL;
507     ASSERT(ovsOpenInstanceArray[instance->cookie] == instance);
508     ovsOpenInstanceArray[instance->cookie] = NULL;
509     ovsNumberOfOpenInstances--;
510     OvsReleaseCtrlLock();
511     ASSERT(instance->eventQueue == NULL);
512     ASSERT (instance->packetQueue == NULL);
513     OvsFreeMemory(instance);
514 }
515
516 NTSTATUS
517 OvsCompleteIrpRequest(PIRP irp,
518                       ULONG_PTR infoPtr,
519                       NTSTATUS status)
520 {
521     irp->IoStatus.Information = infoPtr;
522     irp->IoStatus.Status = status;
523     IoCompleteRequest(irp, IO_NO_INCREMENT);
524     return status;
525 }
526
527
528 NTSTATUS
529 OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject,
530                    PIRP irp)
531 {
532     PIO_STACK_LOCATION irpSp;
533     NTSTATUS status = STATUS_SUCCESS;
534     PFILE_OBJECT fileObject;
535     POVS_DEVICE_EXTENSION ovsExt =
536         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
537
538     ASSERT(deviceObject == gOvsDeviceObject);
539     ASSERT(ovsExt != NULL);
540
541     irpSp = IoGetCurrentIrpStackLocation(irp);
542     fileObject = irpSp->FileObject;
543     OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u",
544                   deviceObject, fileObject,
545                   ovsExt->numberOpenInstance);
546
547     switch (irpSp->MajorFunction) {
548     case IRP_MJ_CREATE:
549         status = OvsAddOpenInstance(ovsExt, fileObject);
550         if (STATUS_SUCCESS == status) {
551             InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance);
552         }
553         break;
554     case IRP_MJ_CLOSE:
555         ASSERT(ovsExt->numberOpenInstance > 0);
556         OvsRemoveOpenInstance(fileObject);
557         InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance);
558         break;
559     default:
560         ASSERT(0);
561     }
562     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
563 }
564
565 _Use_decl_annotations_
566 NTSTATUS
567 OvsCleanupDevice(PDEVICE_OBJECT deviceObject,
568                  PIRP irp)
569 {
570
571     PIO_STACK_LOCATION irpSp;
572     PFILE_OBJECT fileObject;
573
574     NTSTATUS status = STATUS_SUCCESS;
575 #ifdef DBG
576     POVS_DEVICE_EXTENSION ovsExt =
577         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
578     if (ovsExt) {
579         ASSERT(ovsExt->numberOpenInstance > 0);
580     }
581 #else
582     UNREFERENCED_PARAMETER(deviceObject);
583 #endif
584     ASSERT(deviceObject == gOvsDeviceObject);
585     irpSp = IoGetCurrentIrpStackLocation(irp);
586     fileObject = irpSp->FileObject;
587
588     ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP);
589
590     OvsCleanupOpenInstance(fileObject);
591
592     return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status);
593 }
594
595
596 /*
597  * --------------------------------------------------------------------------
598  * IOCTL function handler for the device.
599  * --------------------------------------------------------------------------
600  */
601 NTSTATUS
602 OvsDeviceControl(PDEVICE_OBJECT deviceObject,
603                  PIRP irp)
604 {
605
606     PIO_STACK_LOCATION irpSp;
607     NTSTATUS status = STATUS_SUCCESS;
608     PFILE_OBJECT fileObject;
609     PVOID inputBuffer = NULL;
610     PVOID outputBuffer = NULL;
611     UINT32 inputBufferLen, outputBufferLen;
612     UINT32 code, replyLen = 0;
613     POVS_OPEN_INSTANCE instance;
614     UINT32 devOp;
615     OVS_MESSAGE ovsMsgReadOp;
616     POVS_MESSAGE ovsMsg;
617     NETLINK_FAMILY *nlFamilyOps;
618     OVS_USER_PARAMS_CONTEXT usrParamsCtx;
619
620 #ifdef DBG
621     POVS_DEVICE_EXTENSION ovsExt =
622         (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject);
623     ASSERT(deviceObject == gOvsDeviceObject);
624     ASSERT(ovsExt);
625     ASSERT(ovsExt->numberOpenInstance > 0);
626 #else
627     UNREFERENCED_PARAMETER(deviceObject);
628 #endif
629
630     irpSp = IoGetCurrentIrpStackLocation(irp);
631
632     ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
633     ASSERT(irpSp->FileObject != NULL);
634
635     fileObject = irpSp->FileObject;
636     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
637     code = irpSp->Parameters.DeviceIoControl.IoControlCode;
638     inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
639     outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
640     inputBuffer = irp->AssociatedIrp.SystemBuffer;
641
642     /* Concurrent netlink operations are not supported. */
643     if (InterlockedCompareExchange((LONG volatile *)&instance->inUse, 1, 0)) {
644         status = STATUS_RESOURCE_IN_USE;
645         goto done;
646     }
647
648     /*
649      * Validate the input/output buffer arguments depending on the type of the
650      * operation.
651      */
652     switch (code) {
653     case OVS_IOCTL_TRANSACT:
654         /* Input buffer is mandatory, output buffer is optional. */
655         if (outputBufferLen != 0) {
656             status = MapIrpOutputBuffer(irp, outputBufferLen,
657                                         sizeof *ovsMsg, &outputBuffer);
658             if (status != STATUS_SUCCESS) {
659                 goto done;
660             }
661             ASSERT(outputBuffer);
662         }
663
664         if (inputBufferLen < sizeof (*ovsMsg)) {
665             status = STATUS_NDIS_INVALID_LENGTH;
666             goto done;
667         }
668
669         ovsMsg = inputBuffer;
670         devOp = OVS_TRANSACTION_DEV_OP;
671         break;
672
673     case OVS_IOCTL_READ_EVENT:
674         /* This IOCTL is used to read events */
675         if (outputBufferLen != 0) {
676             status = MapIrpOutputBuffer(irp, outputBufferLen,
677                                         sizeof *ovsMsg, &outputBuffer);
678             if (status != STATUS_SUCCESS) {
679                 goto done;
680             }
681             ASSERT(outputBuffer);
682         } else {
683             status = STATUS_NDIS_INVALID_LENGTH;
684             goto done;
685         }
686         inputBuffer = NULL;
687         inputBufferLen = 0;
688
689         ovsMsg = &ovsMsgReadOp;
690         ovsMsg->nlMsg.nlmsgType = OVS_WIN_NL_CTRL_FAMILY_ID;
691         /* An "artificial" command so we can use NL family function table*/
692         ovsMsg->genlMsg.cmd = OVS_CTRL_CMD_EVENT_NOTIFY;
693         devOp = OVS_READ_DEV_OP;
694         break;
695
696     case OVS_IOCTL_READ:
697         /* Output buffer is mandatory. */
698         if (outputBufferLen != 0) {
699             status = MapIrpOutputBuffer(irp, outputBufferLen,
700                                         sizeof *ovsMsg, &outputBuffer);
701             if (status != STATUS_SUCCESS) {
702                 goto done;
703             }
704             ASSERT(outputBuffer);
705         } else {
706             status = STATUS_NDIS_INVALID_LENGTH;
707             goto done;
708         }
709
710         /*
711          * Operate in the mode that read ioctl is similar to ReadFile(). This
712          * might change as the userspace code gets implemented.
713          */
714         inputBuffer = NULL;
715         inputBufferLen = 0;
716
717         /*
718          * For implementing read (ioctl or otherwise), we need to store some
719          * state in the instance to indicate the command that started the dump
720          * operation. The state can setup 'ovsMsgReadOp' appropriately. Note
721          * that 'ovsMsgReadOp' is needed only in this function to call into the
722          * appropraite handler. The handler itself can access the state in the
723          * instance.
724          *
725          * In the absence of a dump start, return 0 bytes.
726          */
727         if (instance->dumpState.ovsMsg == NULL) {
728             replyLen = 0;
729             status = STATUS_SUCCESS;
730             goto done;
731         }
732         RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg,
733                       sizeof (ovsMsgReadOp));
734
735         /* Create an NL message for consumption. */
736         ovsMsg = &ovsMsgReadOp;
737         devOp = OVS_READ_DEV_OP;
738
739         break;
740
741     case OVS_IOCTL_WRITE:
742         /* Input buffer is mandatory. */
743         if (inputBufferLen < sizeof (*ovsMsg)) {
744             status = STATUS_NDIS_INVALID_LENGTH;
745             goto done;
746         }
747
748         ovsMsg = inputBuffer;
749         devOp = OVS_WRITE_DEV_OP;
750         break;
751
752     default:
753         status = STATUS_INVALID_DEVICE_REQUEST;
754         goto done;
755     }
756
757     ASSERT(ovsMsg);
758     switch (ovsMsg->nlMsg.nlmsgType) {
759     case OVS_WIN_NL_CTRL_FAMILY_ID:
760         nlFamilyOps = &nlControlFamilyOps;
761         break;
762     case OVS_WIN_NL_DATAPATH_FAMILY_ID:
763         nlFamilyOps = &nlDatapathFamilyOps;
764         break;
765     case OVS_WIN_NL_FLOW_FAMILY_ID:
766          nlFamilyOps = &nlFLowFamilyOps;
767          break;
768     case OVS_WIN_NL_PACKET_FAMILY_ID:
769         status = STATUS_NOT_IMPLEMENTED;
770         goto done;
771     case OVS_WIN_NL_VPORT_FAMILY_ID:
772         nlFamilyOps = &nlVportFamilyOps;
773         break;
774     case OVS_WIN_NL_NETDEV_FAMILY_ID:
775         nlFamilyOps = &nlNetdevFamilyOps;
776         break;
777     default:
778         status = STATUS_INVALID_PARAMETER;
779         goto done;
780     }
781
782     /*
783      * For read operation, the netlink command has already been validated
784      * previously.
785      */
786     if (devOp != OVS_READ_DEV_OP) {
787         status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps);
788         if (status != STATUS_SUCCESS) {
789             goto done;
790         }
791     }
792
793     InitUserParamsCtx(irp, instance, devOp, ovsMsg,
794                       inputBuffer, inputBufferLen,
795                       outputBuffer, outputBufferLen,
796                       &usrParamsCtx);
797
798     status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen);
799
800 done:
801     KeMemoryBarrier();
802     instance->inUse = 0;
803     return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status);
804 }
805
806
807 /*
808  * --------------------------------------------------------------------------
809  * Function to validate a netlink command. Only certain combinations of
810  * (device operation, netlink family, command) are valid.
811  * --------------------------------------------------------------------------
812  */
813 static NTSTATUS
814 ValidateNetlinkCmd(UINT32 devOp,
815                    POVS_OPEN_INSTANCE instance,
816                    POVS_MESSAGE ovsMsg,
817                    NETLINK_FAMILY *nlFamilyOps)
818 {
819     NTSTATUS status = STATUS_INVALID_PARAMETER;
820     UINT16 i;
821
822     for (i = 0; i < nlFamilyOps->opsCount; i++) {
823         if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) {
824             /* Validate if the command is valid for the device operation. */
825             if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) {
826                 status = STATUS_INVALID_PARAMETER;
827                 goto done;
828             }
829
830             /* Validate the version. */
831             if (nlFamilyOps->version > ovsMsg->genlMsg.version) {
832                 status = STATUS_INVALID_PARAMETER;
833                 goto done;
834             }
835
836             /* Validate the DP for commands that require a DP. */
837             if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) {
838                 OvsAcquireCtrlLock();
839                 if (ovsMsg->ovsHdr.dp_ifindex !=
840                     (INT)gOvsSwitchContext->dpNo) {
841                     status = STATUS_INVALID_PARAMETER;
842                     OvsReleaseCtrlLock();
843                     goto done;
844                 }
845                 OvsReleaseCtrlLock();
846             }
847
848             /* Validate the PID. */
849             if (ovsMsg->genlMsg.cmd != OVS_CTRL_CMD_WIN_GET_PID) {
850                 if (ovsMsg->nlMsg.nlmsgPid != instance->pid) {
851                     status = STATUS_INVALID_PARAMETER;
852                     goto done;
853                 }
854             }
855
856             status = STATUS_SUCCESS;
857             break;
858         }
859     }
860
861 done:
862     return status;
863 }
864
865 /*
866  * --------------------------------------------------------------------------
867  * Function to invoke the netlink command handler.
868  * --------------------------------------------------------------------------
869  */
870 static NTSTATUS
871 InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
872                         NETLINK_FAMILY *nlFamilyOps,
873                         UINT32 *replyLen)
874 {
875     NTSTATUS status = STATUS_INVALID_PARAMETER;
876     UINT16 i;
877
878     for (i = 0; i < nlFamilyOps->opsCount; i++) {
879         if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) {
880             NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler;
881             ASSERT(handler);
882             if (handler) {
883                 status = handler(usrParamsCtx, replyLen);
884             }
885             break;
886         }
887     }
888
889     return status;
890 }
891
892 /*
893  * --------------------------------------------------------------------------
894  *  Command Handler for 'OVS_CTRL_CMD_WIN_GET_PID'.
895  *
896  *  Each handle on the device is assigned a unique PID when the handle is
897  *  created. On platforms that support netlink natively, the PID is available
898  *  to userspace when the netlink socket is created. However, without native
899  *  netlink support on Windows, OVS datapath generates the PID and lets the
900  *  userspace query it.
901  *
902  *  This function implements the query.
903  * --------------------------------------------------------------------------
904  */
905 static NTSTATUS
906 OvsGetPidCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
907                     UINT32 *replyLen)
908 {
909     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
910     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
911
912     if (usrParamsCtx->outputLength >= sizeof *msgOut) {
913         POVS_OPEN_INSTANCE instance =
914             (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
915
916         RtlZeroMemory(msgOut, sizeof *msgOut);
917         msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
918         msgOut->nlMsg.nlmsgPid = instance->pid;
919         *replyLen = sizeof *msgOut;
920         /* XXX: We might need to return the DP index as well. */
921     } else {
922         return STATUS_NDIS_INVALID_LENGTH;
923     }
924
925     return STATUS_SUCCESS;
926 }
927
928 /*
929  * --------------------------------------------------------------------------
930  * Utility function to fill up information about the datapath in a reply to
931  * userspace.
932  * Assumes that 'gOvsCtrlLock' lock is acquired.
933  * --------------------------------------------------------------------------
934  */
935 static NTSTATUS
936 OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext,
937               POVS_MESSAGE msgIn,
938               PNL_BUFFER nlBuf)
939 {
940     BOOLEAN writeOk;
941     OVS_MESSAGE msgOutTmp;
942     OVS_DATAPATH *datapath = &ovsSwitchContext->datapath;
943     PNL_MSG_HDR nlMsg;
944
945     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn);
946
947     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID;
948     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
949     msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
950     msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
951
952     msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET;
953     msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version;
954     msgOutTmp.genlMsg.reserved = 0;
955
956     msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo;
957
958     writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
959     if (writeOk) {
960         writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME,
961                                      OVS_SYSTEM_DP_NAME);
962     }
963     if (writeOk) {
964         OVS_DP_STATS dpStats;
965
966         dpStats.n_hit = datapath->hits;
967         dpStats.n_missed = datapath->misses;
968         dpStats.n_lost = datapath->lost;
969         dpStats.n_flows = datapath->nFlows;
970         writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS,
971                                      (PCHAR)&dpStats, sizeof dpStats);
972     }
973     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
974     nlMsg->nlmsgLen = NlBufSize(nlBuf);
975
976     return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
977 }
978
979 /*
980  * --------------------------------------------------------------------------
981  * Handler for queueing an IRP used for event notification. The IRP is
982  * completed when a port state changes. STATUS_PENDING is returned on
983  * success. User mode keep a pending IRP at all times.
984  * --------------------------------------------------------------------------
985  */
986 static NTSTATUS
987 OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
988                        UINT32 *replyLen)
989 {
990     NDIS_STATUS status;
991
992     UNREFERENCED_PARAMETER(replyLen);
993
994     POVS_OPEN_INSTANCE instance =
995         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
996     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
997     OVS_EVENT_POLL poll;
998
999     poll.dpNo = msgIn->ovsHdr.dp_ifindex;
1000     status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject,
1001                                &poll, sizeof poll);
1002     return status;
1003 }
1004
1005 /*
1006  * --------------------------------------------------------------------------
1007  *  Handler for the subscription for the event queue
1008  * --------------------------------------------------------------------------
1009  */
1010 static NTSTATUS
1011 OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1012                             UINT32 *replyLen)
1013 {
1014     NDIS_STATUS status;
1015     OVS_EVENT_SUBSCRIBE request;
1016     BOOLEAN rc;
1017     UINT8 join;
1018     PNL_ATTR attrs[2];
1019     const NL_POLICY policy[] =  {
1020         [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 },
1021         [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 },
1022         };
1023
1024     UNREFERENCED_PARAMETER(replyLen);
1025
1026     POVS_OPEN_INSTANCE instance =
1027         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1028     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1029
1030     rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn),
1031          NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, attrs, 2);
1032     if (!rc) {
1033         status = STATUS_INVALID_PARAMETER;
1034         goto done;
1035     }
1036
1037     /* XXX Ignore the MC group for now */
1038     join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]);
1039     request.dpNo = msgIn->ovsHdr.dp_ifindex;
1040     request.subscribe = join;
1041     request.mask = OVS_EVENT_MASK_ALL;
1042
1043     status = OvsSubscribeEventIoctl(instance->fileObject, &request,
1044                                     sizeof request);
1045 done:
1046     return status;
1047 }
1048
1049
1050 /*
1051  * --------------------------------------------------------------------------
1052  *  Command Handler for 'OVS_DP_CMD_GET'.
1053  *
1054  *  The function handles both the dump based as well as the transaction based
1055  *  'OVS_DP_CMD_GET' command. In the dump command, it handles the initial
1056  *  call to setup dump state, as well as subsequent calls to continue dumping
1057  *  data.
1058  * --------------------------------------------------------------------------
1059  */
1060 static NTSTATUS
1061 OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1062                    UINT32 *replyLen)
1063 {
1064     if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) {
1065         return HandleGetDpTransaction(usrParamsCtx, replyLen);
1066     } else {
1067         return HandleGetDpDump(usrParamsCtx, replyLen);
1068     }
1069 }
1070
1071 /*
1072  * --------------------------------------------------------------------------
1073  *  Function for handling the transaction based 'OVS_DP_CMD_GET' command.
1074  * --------------------------------------------------------------------------
1075  */
1076 static NTSTATUS
1077 HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1078                        UINT32 *replyLen)
1079 {
1080     return HandleDpTransaction(usrParamsCtx, replyLen);
1081 }
1082
1083
1084 /*
1085  * --------------------------------------------------------------------------
1086  *  Function for handling the dump-based 'OVS_DP_CMD_GET' command.
1087  * --------------------------------------------------------------------------
1088  */
1089 static NTSTATUS
1090 HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1091                 UINT32 *replyLen)
1092 {
1093     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1094     POVS_OPEN_INSTANCE instance =
1095         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1096
1097     if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) {
1098         *replyLen = 0;
1099         OvsSetupDumpStart(usrParamsCtx);
1100     } else {
1101         NL_BUFFER nlBuf;
1102         NTSTATUS status;
1103         POVS_MESSAGE msgIn = instance->dumpState.ovsMsg;
1104
1105         ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1106
1107         if (instance->dumpState.ovsMsg == NULL) {
1108             ASSERT(FALSE);
1109             return STATUS_INVALID_DEVICE_STATE;
1110         }
1111
1112         /* Dump state must have been deleted after previous dump operation. */
1113         ASSERT(instance->dumpState.index[0] == 0);
1114         /* Output buffer has been validated while validating read dev op. */
1115         ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1116
1117         NlBufInit(&nlBuf, usrParamsCtx->outputBuffer,
1118                   usrParamsCtx->outputLength);
1119
1120         OvsAcquireCtrlLock();
1121         if (!gOvsSwitchContext) {
1122             /* Treat this as a dump done. */
1123             OvsReleaseCtrlLock();
1124             *replyLen = 0;
1125             FreeUserDumpState(instance);
1126             return STATUS_SUCCESS;
1127         }
1128         status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1129         OvsReleaseCtrlLock();
1130
1131         if (status != STATUS_SUCCESS) {
1132             *replyLen = 0;
1133             FreeUserDumpState(instance);
1134             return status;
1135         }
1136
1137         /* Increment the dump index. */
1138         instance->dumpState.index[0] = 1;
1139         *replyLen = msgOut->nlMsg.nlmsgLen;
1140
1141         /* Free up the dump state, since there's no more data to continue. */
1142         FreeUserDumpState(instance);
1143     }
1144
1145     return STATUS_SUCCESS;
1146 }
1147
1148
1149 /*
1150  * --------------------------------------------------------------------------
1151  *  Command Handler for 'OVS_DP_CMD_SET'.
1152  * --------------------------------------------------------------------------
1153  */
1154 static NTSTATUS
1155 OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1156                    UINT32 *replyLen)
1157 {
1158     return HandleDpTransaction(usrParamsCtx, replyLen);
1159 }
1160
1161 /*
1162  * --------------------------------------------------------------------------
1163  *  Function for handling transaction based 'OVS_DP_CMD_GET' and
1164  *  'OVS_DP_CMD_SET' commands.
1165  * --------------------------------------------------------------------------
1166  */
1167 static NTSTATUS
1168 HandleDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1169                     UINT32 *replyLen)
1170 {
1171     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1172     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1173     NTSTATUS status = STATUS_SUCCESS;
1174     NL_BUFFER nlBuf;
1175     static const NL_POLICY ovsDatapathSetPolicy[] = {
1176         [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ },
1177         [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE },
1178         [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE },
1179     };
1180     PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)];
1181
1182     /* input buffer has been validated while validating write dev op. */
1183     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1184
1185     /* Parse any attributes in the request. */
1186     if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) {
1187         if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1188                         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1189                         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1190                         ovsDatapathSetPolicy, dpAttrs, ARRAY_SIZE(dpAttrs))) {
1191             return STATUS_INVALID_PARAMETER;
1192         }
1193
1194         /*
1195         * XXX: Not clear at this stage if there's any role for the
1196         * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed
1197         * from userspace.
1198         */
1199
1200     } else {
1201         RtlZeroMemory(dpAttrs, sizeof dpAttrs);
1202     }
1203
1204     /* Output buffer is optional for OVS_TRANSACTION_DEV_OP. */
1205     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1206         return STATUS_NDIS_INVALID_LENGTH;
1207     }
1208     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1209
1210     OvsAcquireCtrlLock();
1211     if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) {
1212         if (!gOvsSwitchContext &&
1213             !OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]),
1214                               OVS_SYSTEM_DP_NAME)) {
1215             OvsReleaseCtrlLock();
1216             status = STATUS_NOT_FOUND;
1217             goto cleanup;
1218         }
1219     } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) {
1220         OvsReleaseCtrlLock();
1221         status = STATUS_NOT_FOUND;
1222         goto cleanup;
1223     }
1224
1225     status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf);
1226     OvsReleaseCtrlLock();
1227
1228     *replyLen = NlBufSize(&nlBuf);
1229
1230 cleanup:
1231     return status;
1232 }
1233
1234
1235 NTSTATUS
1236 OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx)
1237 {
1238     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1239     POVS_OPEN_INSTANCE instance =
1240         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1241
1242     /* input buffer has been validated while validating write dev op. */
1243     ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn);
1244
1245     /* A write operation that does not indicate dump start is invalid. */
1246     if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) {
1247         return STATUS_INVALID_PARAMETER;
1248     }
1249     /* XXX: Handle other NLM_F_* flags in the future. */
1250
1251     /*
1252      * This operation should be setting up the dump state. If there's any
1253      * previous state, clear it up so as to set it up afresh.
1254      */
1255     if (instance->dumpState.ovsMsg != NULL) {
1256         FreeUserDumpState(instance);
1257     }
1258
1259     return InitUserDumpState(instance, msgIn);
1260 }
1261
1262 static VOID
1263 BuildMsgOut(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 type,
1264                      UINT32 length, UINT16 flags)
1265 {
1266     msgOut->nlMsg.nlmsgType = type;
1267     msgOut->nlMsg.nlmsgFlags = flags;
1268     msgOut->nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq;
1269     msgOut->nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid;
1270     msgOut->nlMsg.nlmsgLen = length;
1271
1272     msgOut->genlMsg.cmd = msgIn->genlMsg.cmd;
1273     msgOut->genlMsg.version = nlDatapathFamilyOps.version;
1274     msgOut->genlMsg.reserved = 0;
1275 }
1276
1277 static VOID
1278 BuildReplyMsgFromMsgIn(POVS_MESSAGE msgIn, POVS_MESSAGE msgOut, UINT16 flags)
1279 {
1280     BuildMsgOut(msgIn, msgOut, msgIn->nlMsg.nlmsgType, sizeof(OVS_MESSAGE),
1281                 flags);
1282 }
1283
1284 static VOID
1285 BuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode)
1286 {
1287     BuildMsgOut(msgIn, (POVS_MESSAGE)msgOut, NLMSG_ERROR,
1288                 sizeof(OVS_MESSAGE_ERROR), 0);
1289
1290     msgOut->errorMsg.error = errorCode;
1291     msgOut->errorMsg.nlMsg = msgIn->nlMsg;
1292 }
1293
1294 static NTSTATUS
1295 OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport,
1296                       POVS_MESSAGE msgIn,
1297                       PVOID outBuffer,
1298                       UINT32 outBufLen,
1299                       int dpIfIndex)
1300 {
1301     NL_BUFFER nlBuffer;
1302     OVS_VPORT_FULL_STATS vportStats;
1303     BOOLEAN ok;
1304     OVS_MESSAGE msgOut;
1305     PNL_MSG_HDR nlMsg;
1306
1307     NlBufInit(&nlBuffer, outBuffer, outBufLen);
1308
1309     BuildReplyMsgFromMsgIn(msgIn, &msgOut, NLM_F_MULTI);
1310     msgOut.ovsHdr.dp_ifindex = dpIfIndex;
1311
1312     ok = NlMsgPutHead(&nlBuffer, (PCHAR)&msgOut, sizeof msgOut);
1313     if (!ok) {
1314         return STATUS_INSUFFICIENT_RESOURCES;
1315     }
1316
1317     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo);
1318     if (!ok) {
1319         return STATUS_INSUFFICIENT_RESOURCES;
1320     }
1321
1322     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType);
1323     if (!ok) {
1324         return STATUS_INSUFFICIENT_RESOURCES;
1325     }
1326
1327     ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName);
1328     if (!ok) {
1329         return STATUS_INSUFFICIENT_RESOURCES;
1330     }
1331
1332     /*
1333      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1334      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1335      * it means we have an array of pids, instead of a single pid.
1336      * ATM we assume we have one pid only.
1337     */
1338
1339     ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID,
1340                          vport->upcallPid);
1341     if (!ok) {
1342         return STATUS_INSUFFICIENT_RESOURCES;
1343     }
1344
1345     /*stats*/
1346     vportStats.rxPackets = vport->stats.rxPackets;
1347     vportStats.rxBytes = vport->stats.rxBytes;
1348     vportStats.txPackets = vport->stats.txPackets;
1349     vportStats.txBytes = vport->stats.txBytes;
1350     vportStats.rxErrors = vport->errStats.rxErrors;
1351     vportStats.txErrors = vport->errStats.txErrors;
1352     vportStats.rxDropped = vport->errStats.rxDropped;
1353     vportStats.txDropped = vport->errStats.txDropped;
1354
1355     ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS,
1356                             (PCHAR)&vportStats,
1357                             sizeof(OVS_VPORT_FULL_STATS));
1358     if (!ok) {
1359         return STATUS_INSUFFICIENT_RESOURCES;
1360     }
1361
1362     /*
1363      * XXX: when vxlan udp dest port becomes configurable, we will also need
1364      * to add vport options
1365     */
1366
1367     nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0);
1368     nlMsg->nlmsgLen = NlBufSize(&nlBuffer);
1369
1370     return STATUS_SUCCESS;
1371 }
1372
1373 static NTSTATUS
1374 OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1375                     UINT32 *replyLen)
1376 {
1377     POVS_MESSAGE msgIn;
1378     POVS_OPEN_INSTANCE instance =
1379         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1380     LOCK_STATE_EX lockState;
1381     UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE;
1382
1383     /*
1384      * XXX: this function shares some code with other dump command(s).
1385      * In the future, we will need to refactor the dump functions
1386     */
1387
1388     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1389
1390     if (instance->dumpState.ovsMsg == NULL) {
1391         ASSERT(FALSE);
1392         return STATUS_INVALID_DEVICE_STATE;
1393     }
1394
1395     /* Output buffer has been validated while validating read dev op. */
1396     ASSERT(usrParamsCtx->outputBuffer != NULL);
1397
1398     msgIn = instance->dumpState.ovsMsg;
1399
1400     OvsAcquireCtrlLock();
1401     if (!gOvsSwitchContext) {
1402         /* Treat this as a dump done. */
1403         OvsReleaseCtrlLock();
1404         *replyLen = 0;
1405         FreeUserDumpState(instance);
1406         return STATUS_SUCCESS;
1407     }
1408
1409     /*
1410      * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath,
1411      * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set,
1412      * it means we have an array of pids, instead of a single pid.
1413      * ATM we assume we have one pid only.
1414     */
1415     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1416     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
1417         NDIS_RWL_AT_DISPATCH_LEVEL);
1418
1419     if (gOvsSwitchContext->numVports > 0) {
1420         /* inBucket: the bucket, used for lookup */
1421         UINT32 inBucket = instance->dumpState.index[0];
1422         /* inIndex: index within the given bucket, used for lookup */
1423         UINT32 inIndex = instance->dumpState.index[1];
1424         /* the bucket to be used for the next dump operation */
1425         UINT32 outBucket = 0;
1426         /* the index within the outBucket to be used for the next dump */
1427         UINT32 outIndex = 0;
1428
1429         for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
1430             PLIST_ENTRY head, link;
1431             head = &(gOvsSwitchContext->portHashArray[i]);
1432             POVS_VPORT_ENTRY vport = NULL;
1433
1434             outIndex = 0;
1435             LIST_FORALL(head, link) {
1436
1437                 /*
1438                  * if one or more dumps were previously done on this same bucket,
1439                  * inIndex will be > 0, so we'll need to reply with the
1440                  * inIndex + 1 vport from the bucket.
1441                 */
1442                 if (outIndex >= inIndex) {
1443                     vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portLink);
1444
1445                     if (vport->portNo != 0) {
1446                         OvsCreateMsgFromVport(vport, msgIn,
1447                                               usrParamsCtx->outputBuffer,
1448                                               usrParamsCtx->outputLength,
1449                                               gOvsSwitchContext->dpNo);
1450                         ++outIndex;
1451                         break;
1452                     } else {
1453                         vport = NULL;
1454                     }
1455                 }
1456
1457                 ++outIndex;
1458             }
1459
1460             if (vport) {
1461                 break;
1462             }
1463
1464             /*
1465              * if no vport was found above, check the next bucket, beginning
1466              * with the first (i.e. index 0) elem from within that bucket
1467             */
1468             inIndex = 0;
1469         }
1470
1471         outBucket = i;
1472
1473         /* XXX: what about NLMSG_DONE (as msg type)? */
1474         instance->dumpState.index[0] = outBucket;
1475         instance->dumpState.index[1] = outIndex;
1476     }
1477
1478     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1479
1480     OvsReleaseCtrlLock();
1481
1482     /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */
1483     if (i < OVS_MAX_VPORT_ARRAY_SIZE) {
1484         POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1485         *replyLen = msgOut->nlMsg.nlmsgLen;
1486     } else {
1487         /*
1488          * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found =>
1489          * it's dump done
1490          */
1491         *replyLen = 0;
1492         /* Free up the dump state, since there's no more data to continue. */
1493         FreeUserDumpState(instance);
1494     }
1495
1496     return STATUS_SUCCESS;
1497 }
1498
1499 static NTSTATUS
1500 OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1501             UINT32 *replyLen)
1502 {
1503     NTSTATUS status = STATUS_SUCCESS;
1504     LOCK_STATE_EX lockState;
1505
1506     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
1507     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1508     POVS_VPORT_ENTRY vport = NULL;
1509     NL_ERROR nlError = NL_ERROR_SUCCESS;
1510
1511     static const NL_POLICY ovsVportPolicy[] = {
1512         [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE },
1513         [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING,
1514                                   .minLen = 2,
1515                                   .maxLen = IFNAMSIZ,
1516                                   .optional = TRUE},
1517     };
1518     PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)];
1519
1520     /* input buffer has been validated while validating write dev op. */
1521     ASSERT(usrParamsCtx->inputBuffer != NULL);
1522
1523     if (!NlAttrParse((PNL_MSG_HDR)msgIn,
1524         NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN,
1525         NlMsgAttrsLen((PNL_MSG_HDR)msgIn),
1526         ovsVportPolicy, vportAttrs, ARRAY_SIZE(vportAttrs))) {
1527         return STATUS_INVALID_PARAMETER;
1528     }
1529
1530     if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) {
1531         return STATUS_INVALID_BUFFER_SIZE;
1532     }
1533
1534     OvsAcquireCtrlLock();
1535     if (!gOvsSwitchContext) {
1536         OvsReleaseCtrlLock();
1537         return STATUS_INVALID_PARAMETER;
1538     }
1539     OvsReleaseCtrlLock();
1540
1541     if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) {
1542         vport = OvsFindVportByOvsName(gOvsSwitchContext,
1543             NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]),
1544             NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]) - 1);
1545     } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) {
1546         vport = OvsFindVportByPortNo(gOvsSwitchContext,
1547             NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]));
1548     } else {
1549         nlError = NL_ERROR_INVAL;
1550         goto Cleanup;
1551     }
1552
1553     if (!vport) {
1554         nlError = NL_ERROR_NODEV;
1555         goto Cleanup;
1556     }
1557
1558     NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0);
1559     status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer,
1560                                    usrParamsCtx->outputLength,
1561                                    gOvsSwitchContext->dpNo);
1562     NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState);
1563
1564     *replyLen = msgOut->nlMsg.nlmsgLen;
1565
1566 Cleanup:
1567     if (nlError != NL_ERROR_SUCCESS) {
1568         POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)
1569             usrParamsCtx->outputBuffer;
1570
1571         BuildErrorMsg(msgIn, msgError, nlError);
1572         *replyLen = msgError->nlMsg.nlmsgLen;
1573     }
1574
1575     return STATUS_SUCCESS;
1576 }
1577
1578 /*
1579  * --------------------------------------------------------------------------
1580  *  Handler for the get vport command. The function handles the initial call to
1581  *  setup the dump state, as well as subsequent calls to continue dumping data.
1582  * --------------------------------------------------------------------------
1583 */
1584 static NTSTATUS
1585 OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1586                       UINT32 *replyLen)
1587 {
1588     *replyLen = 0;
1589
1590     switch (usrParamsCtx->devOp)
1591     {
1592     case OVS_WRITE_DEV_OP:
1593         return OvsSetupDumpStart(usrParamsCtx);
1594
1595     case OVS_READ_DEV_OP:
1596         return OvsGetVportDumpNext(usrParamsCtx, replyLen);
1597
1598     case OVS_TRANSACTION_DEV_OP:
1599         return OvsGetVport(usrParamsCtx, replyLen);
1600
1601     default:
1602         return STATUS_INVALID_DEVICE_REQUEST;
1603     }
1604
1605 }
1606
1607 /*
1608  * --------------------------------------------------------------------------
1609  *  Utility function to map the output buffer in an IRP. The buffer is assumed
1610  *  to have been passed down using METHOD_OUT_DIRECT (Direct I/O).
1611  * --------------------------------------------------------------------------
1612  */
1613 static NTSTATUS
1614 MapIrpOutputBuffer(PIRP irp,
1615                    UINT32 bufferLength,
1616                    UINT32 requiredLength,
1617                    PVOID *buffer)
1618 {
1619     ASSERT(irp);
1620     ASSERT(buffer);
1621     ASSERT(bufferLength);
1622     ASSERT(requiredLength);
1623     if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) {
1624         return STATUS_INVALID_PARAMETER;
1625     }
1626
1627     if (bufferLength < requiredLength) {
1628         return STATUS_NDIS_INVALID_LENGTH;
1629     }
1630     if (irp->MdlAddress == NULL) {
1631         return STATUS_INVALID_PARAMETER;
1632     }
1633     *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress,
1634                                            NormalPagePriority);
1635     if (*buffer == NULL) {
1636         return STATUS_INSUFFICIENT_RESOURCES;
1637     }
1638
1639     return STATUS_SUCCESS;
1640 }
1641
1642 /*
1643  * --------------------------------------------------------------------------
1644  * Utility function to fill up information about the state of a port in a reply
1645  * to* userspace.
1646  * Assumes that 'gOvsCtrlLock' lock is acquired.
1647  * --------------------------------------------------------------------------
1648  */
1649 static NTSTATUS
1650 OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1651                 POVS_EVENT_ENTRY eventEntry,
1652                 PNL_BUFFER nlBuf)
1653 {
1654     NTSTATUS status;
1655     BOOLEAN rc;
1656     OVS_MESSAGE msgOutTmp;
1657     PNL_MSG_HDR nlMsg;
1658     POVS_VPORT_ENTRY vport;
1659
1660     ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp);
1661
1662     msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID;
1663     msgOutTmp.nlMsg.nlmsgFlags = 0;  /* XXX: ? */
1664
1665     /* driver intiated messages should have zerp seq number*/
1666     msgOutTmp.nlMsg.nlmsgSeq = 0;
1667     msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid;
1668
1669     msgOutTmp.genlMsg.version = nlVportFamilyOps.version;
1670     msgOutTmp.genlMsg.reserved = 0;
1671
1672     /* we don't have netdev yet, treat link up/down a adding/removing a port*/
1673     if (eventEntry->status & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) {
1674         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW;
1675     } else if (eventEntry->status &
1676              (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) {
1677         msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL;
1678     } else {
1679         ASSERT(FALSE);
1680         return STATUS_UNSUCCESSFUL;
1681     }
1682     msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo;
1683
1684     rc = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp);
1685     if (!rc) {
1686         status = STATUS_INVALID_BUFFER_SIZE;
1687         goto cleanup;
1688     }
1689
1690     vport = OvsFindVportByPortNo(gOvsSwitchContext, eventEntry->portNo);
1691     if (!vport) {
1692         status = STATUS_DEVICE_DOES_NOT_EXIST;
1693         goto cleanup;
1694     }
1695
1696     rc = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) ||
1697          NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, vport->ovsType) ||
1698          NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, vport->ovsName);
1699     if (!rc) {
1700         status = STATUS_INVALID_BUFFER_SIZE;
1701         goto cleanup;
1702     }
1703
1704     /* XXXX Should we add the port stats attributes?*/
1705     nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0);
1706     nlMsg->nlmsgLen = NlBufSize(nlBuf);
1707     status = STATUS_SUCCESS;
1708
1709 cleanup:
1710     return status;
1711 }
1712
1713
1714 /*
1715  * --------------------------------------------------------------------------
1716  * Handler for reading events from the driver event queue. This handler is
1717  * executed when user modes issues a socket receive on a socket assocaited
1718  * with the MC group for events.
1719  * XXX user mode should read multiple events in one system call
1720  * --------------------------------------------------------------------------
1721  */
1722 static NTSTATUS
1723 OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
1724                        UINT32 *replyLen)
1725 {
1726 #ifdef DBG
1727     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
1728     POVS_OPEN_INSTANCE instance =
1729         (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance;
1730 #endif
1731     NL_BUFFER nlBuf;
1732     NTSTATUS status;
1733     OVS_EVENT_ENTRY eventEntry;
1734
1735     ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP);
1736
1737     /* Should never read events with a dump socket */
1738     ASSERT(instance->dumpState.ovsMsg == NULL);
1739
1740     /* Must have an event queue */
1741     ASSERT(instance->eventQueue != NULL);
1742
1743     /* Output buffer has been validated while validating read dev op. */
1744     ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut);
1745
1746     NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength);
1747
1748     OvsAcquireCtrlLock();
1749     if (!gOvsSwitchContext) {
1750         status = STATUS_SUCCESS;
1751         goto cleanup;
1752     }
1753
1754     /* remove an event entry from the event queue */
1755     status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry);
1756     if (status != STATUS_SUCCESS) {
1757         goto cleanup;
1758     }
1759
1760     status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf);
1761     if (status == NDIS_STATUS_SUCCESS) {
1762         *replyLen = NlBufSize(&nlBuf);
1763     }
1764
1765 cleanup:
1766     OvsReleaseCtrlLock();
1767     return status;
1768 }
1769 #endif /* OVS_USE_NL_INTERFACE */