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